import { existsSync } from "node:fs"; import { argv, env, exit } from "node:process"; const args = new Set(argv.slice(2)); const failures = []; const requireVariable = (name) => { if (!env[name]) { failures.push(`Missing ${name}`); } }; const requireOneVariable = (names, label) => { if (!names.some((name) => env[name])) { failures.push(`Missing ${label}: set one of ${names.join(", ")}`); } }; const decodeBase64 = (name) => { if (!env[name]) { return null; } try { const decoded = Buffer.from(env[name], "base64"); if (decoded.length === 0) { failures.push(`${name} is empty after base64 decoding`); return null; } return decoded; } catch { failures.push(`${name} is not valid base64`); return null; } }; const decodeBase64Json = (name) => { const decoded = decodeBase64(name); if (!decoded) { return null; } try { return JSON.parse(decoded.toString("utf8")); } catch { failures.push(`${name} is not base64-encoded JSON`); return null; } }; const isEnabled = (name) => !["false", "0", "no"].includes(String(env[name] ?? "true").toLowerCase()); const checkAndroid = () => { requireVariable("MOBILE_VERSION_NAME"); requireVariable("MOBILE_VERSION_CODE"); requireOneVariable(["ANDROID_KEYSTORE_BASE64", "ANDROID_KEYSTORE_FILE"], "Android release keystore"); requireVariable("ANDROID_KEYSTORE_PASSWORD"); requireVariable("ANDROID_KEY_ALIAS"); requireVariable("ANDROID_KEY_PASSWORD"); if (env.ANDROID_KEYSTORE_FILE && !existsSync(env.ANDROID_KEYSTORE_FILE)) { failures.push("ANDROID_KEYSTORE_FILE does not point to an existing file"); } decodeBase64("ANDROID_KEYSTORE_BASE64"); if (!isEnabled("UPLOAD_ANDROID_TO_PLAY")) { return; } requireVariable("ANDROID_PACKAGE_NAME"); requireVariable("ANDROID_AAB_PATH"); requireVariable("GOOGLE_PLAY_SERVICE_ACCOUNT_JSON_BASE64"); const validTracks = new Set(["production", "beta", "alpha", "internal"]); const validStatuses = new Set(["completed", "draft", "inProgress", "halted"]); const track = env.PLAY_STORE_TRACK || "production"; const status = env.PLAY_STORE_RELEASE_STATUS || "completed"; if (!validTracks.has(track)) { failures.push(`PLAY_STORE_TRACK must be one of ${Array.from(validTracks).join(", ")}`); } if (!validStatuses.has(status)) { failures.push(`PLAY_STORE_RELEASE_STATUS must be one of ${Array.from(validStatuses).join(", ")}`); } if (status === "inProgress") { const fraction = Number(env.PLAY_STORE_USER_FRACTION); if (!(fraction > 0 && fraction < 1)) { failures.push("PLAY_STORE_USER_FRACTION must be greater than 0 and less than 1 when status is inProgress"); } } const serviceAccount = decodeBase64Json("GOOGLE_PLAY_SERVICE_ACCOUNT_JSON_BASE64"); if (serviceAccount) { if (!serviceAccount.client_email) { failures.push("Google Play service account JSON is missing client_email"); } if (!serviceAccount.private_key) { failures.push("Google Play service account JSON is missing private_key"); } } }; const checkIos = () => { requireVariable("MOBILE_VERSION_NAME"); requireVariable("MOBILE_VERSION_CODE"); requireVariable("IOS_CERTIFICATE_BASE64"); requireVariable("IOS_CERTIFICATE_PASSWORD"); requireVariable("IOS_PROVISION_PROFILE_BASE64"); requireVariable("IOS_KEYCHAIN_PASSWORD"); requireVariable("APPLE_TEAM_ID"); decodeBase64("IOS_CERTIFICATE_BASE64"); decodeBase64("IOS_PROVISION_PROFILE_BASE64"); if (!isEnabled("UPLOAD_IOS_TO_APP_STORE")) { return; } requireVariable("APP_STORE_CONNECT_API_KEY_ID"); requireVariable("APP_STORE_CONNECT_ISSUER_ID"); requireVariable("APP_STORE_CONNECT_API_PRIVATE_KEY_BASE64"); const privateKey = decodeBase64("APP_STORE_CONNECT_API_PRIVATE_KEY_BASE64"); if (privateKey && !privateKey.toString("utf8").includes("PRIVATE KEY")) { failures.push("APP_STORE_CONNECT_API_PRIVATE_KEY_BASE64 does not look like a .p8 private key"); } }; if (args.has("--android")) { checkAndroid(); } if (args.has("--ios")) { checkIos(); } if (!args.has("--android") && !args.has("--ios")) { failures.push("Pass --android or --ios"); } if (failures.length > 0) { console.error("Mobile store upload environment is not configured:"); for (const failure of failures) { console.error(`- ${failure}`); } exit(1); } console.log("Mobile store upload environment is configured.");