149 lines
4.3 KiB
JavaScript
149 lines
4.3 KiB
JavaScript
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.");
|