feat(app): automate iOS beta distribution

Beta tags now reach external testers without per-release dashboard work. Reserve distinct native build slots so successive betas and the promoted stable build can coexist under one app version.
This commit is contained in:
Mohamed Boudra
2026-08-04 19:21:22 +02:00
parent adf508a1c8
commit d191fa6685
6 changed files with 105 additions and 33 deletions
+5 -28
View File
@@ -3,6 +3,7 @@ const path = require("node:path");
const pkg = require("./package.json");
const withAndroidProfileable = require("./plugins/with-android-profileable");
const withFdroidAutolinking = require("./plugins/with-fdroid-autolinking");
const { getNativeReleaseVersion } = require("./native-release-version");
const appVariant = process.env.APP_VARIANT ?? "production";
const isFdroidBuild = process.env.PASEO_FDROID_BUILD === "1";
const isProfileBuild = process.env.PASEO_PROFILE_BUILD === "1";
@@ -47,30 +48,6 @@ const buildProfile = isFdroidBuild
],
};
function getNativeBuildVersionCode(version) {
const match = /^(\d+)\.(\d+)\.(\d+)(?:[-+].*)?$/.exec(version);
if (!match) {
throw new Error(`Cannot derive Android versionCode from non-semver version: ${version}`);
}
const [, majorText, minorText, patchText] = match;
const major = Number(majorText);
const minor = Number(minorText);
const patch = Number(patchText);
if (minor > 999 || patch > 999) {
throw new Error(`Cannot derive collision-free Android versionCode from version: ${version}`);
}
const versionCode = major * 1_000_000 + minor * 1_000 + patch;
if (!Number.isSafeInteger(versionCode) || versionCode <= 0 || versionCode > 2_100_000_000) {
throw new Error(`Derived Android versionCode is out of range: ${versionCode}`);
}
return versionCode;
}
function resolveSecretFile(params) {
const fromEnv = process.env[params.envKey];
if (typeof fromEnv === "string" && fromEnv.trim().length > 0) {
@@ -113,13 +90,13 @@ const variants = {
};
const variant = variants[appVariant] ?? variants.production;
const nativeBuildVersionCode = getNativeBuildVersionCode(pkg.version);
const nativeReleaseVersion = getNativeReleaseVersion(pkg.version);
export default {
expo: {
name: variant.name,
slug: "voice-mobile",
version: pkg.version,
version: nativeReleaseVersion.appVersion,
orientation: "portrait",
icon: "./assets/images/icon.png",
scheme: "paseo",
@@ -135,7 +112,7 @@ export default {
...(variant.googleServiceInfoPlist
? { googleServicesFile: variant.googleServiceInfoPlist }
: {}),
buildNumber: String(nativeBuildVersionCode),
buildNumber: nativeReleaseVersion.iosBuildNumber,
},
android: {
adaptiveIcon: {
@@ -149,7 +126,7 @@ export default {
usesCleartextTraffic: true,
permissions: buildProfile.androidPermissions,
package: variant.packageId,
versionCode: nativeBuildVersionCode,
versionCode: nativeReleaseVersion.androidVersionCode,
...(variant.googleServicesFile ? { googleServicesFile: variant.googleServicesFile } : {}),
},
web: {