diff --git a/apps/app/app.config.js b/apps/app/app.config.js index 7ea75798..e449299e 100644 --- a/apps/app/app.config.js +++ b/apps/app/app.config.js @@ -1,10 +1,12 @@ +const isTestBuild = process.env.ROCKSKY_VARIANT === "test"; + module.exports = { - name: "Rocksky", + name: isTestBuild ? "Rocksky Test" : "Rocksky", slug: "rocksky", version: "2.0.0", orientation: "portrait", icon: "./assets/images/icon.png", - scheme: "rocksky", + scheme: isTestBuild ? "rocksky-test" : "rocksky", userInterfaceStyle: "dark", backgroundColor: "#130825", newArchEnabled: true, @@ -22,7 +24,7 @@ module.exports = { backgroundImage: "./assets/images/icon-background.png", backgroundColor: "#130825", }, - package: "app.rocksky", + package: isTestBuild ? "app.rocksky.test" : "app.rocksky", versionCode: 3, }, web: { @@ -31,6 +33,7 @@ module.exports = { favicon: "./assets/images/favicon.png", }, plugins: [ + "./plugins/withAndroidIdentity", "./plugins/withRustEngine", "./plugins/withReleaseOptimization", [ diff --git a/apps/app/docs/android-test-release.md b/apps/app/docs/android-test-release.md new file mode 100644 index 00000000..d11cb7c9 --- /dev/null +++ b/apps/app/docs/android-test-release.md @@ -0,0 +1,25 @@ +# Rocksky Test release + +Use `ROCKSKY_VARIANT=test` to generate the separate application ID +`app.rocksky.test`, display name **Rocksky Test**, and `rocksky-test` URL scheme. +Omitting the variable retains the production identity `app.rocksky`. + +```sh +bash scripts/build-android-test-release.sh +cd android +adb install -r app/build/outputs/apk/release/app-release.apk +adb shell am start -n app.rocksky.test/.MainActivity +``` + +The existing Rust Gradle task always invokes `cargo ndk ... build --release +--locked` for both supported engine ABIs. Do not replace it with a debug Cargo +build. The APK includes its JavaScript bundle and works without Metro. +The identity plugin invalidates stale React Native autolinking metadata when +switching package names, so the generated entry point uses the right BuildConfig. + +This local release uses the project's existing local signing configuration, +not the Play Store signing key. The separate package can coexist with production; +it has separate login, notification access, and application data. + +To return generated Android files to the production identity, rerun prebuild +without `ROCKSKY_VARIANT`. Never install the test APK over the production package. diff --git a/apps/app/package.json b/apps/app/package.json index 2f8a5a20..ffd2f8ab 100644 --- a/apps/app/package.json +++ b/apps/app/package.json @@ -12,6 +12,7 @@ "format": "biome format src", "typecheck": "tsc --noEmit", "test:analytics": "node --experimental-strip-types --test scripts/analytics.test.mjs", + "build:android:test:release": "bash scripts/build-android-test-release.sh", "adb:reverse": "adb reverse tcp:8081 tcp:8081", "eas-build-pre-install": "bash scripts/eas-pre-install.sh", "eas-build-post-install": "node scripts/eas-post-install.cjs", diff --git a/apps/app/plugins/withAndroidIdentity.js b/apps/app/plugins/withAndroidIdentity.js new file mode 100644 index 00000000..84cdd1be --- /dev/null +++ b/apps/app/plugins/withAndroidIdentity.js @@ -0,0 +1,25 @@ +const { withDangerousMod } = require("@expo/config-plugins"); +const fs = require("node:fs/promises"); +const path = require("node:path"); + +// React Native's autolinking cache watches lockfiles, not Expo's package name. +// Invalidate only that generated file when changing production/test identity. +module.exports = (config) => + withDangerousMod(config, [ + "android", + async (config) => { + const cache = path.join( + config.modRequest.platformProjectRoot, + "build/generated/autolinking/autolinking.json", + ); + try { + const json = JSON.parse(await fs.readFile(cache, "utf8")); + if (json.project?.android?.packageName !== config.android.package) { + await fs.unlink(cache); + } + } catch (error) { + if (error.code !== "ENOENT") throw error; + } + return config; + }, + ]); diff --git a/apps/app/scripts/build-android-test-release.sh b/apps/app/scripts/build-android-test-release.sh new file mode 100644 index 00000000..627c0f2e --- /dev/null +++ b/apps/app/scripts/build-android-test-release.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash +set -euo pipefail +app_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +cd "$app_dir" +export ROCKSKY_VARIANT=test +node node_modules/expo/bin/cli prebuild --platform android --no-install +cd android +# buildRustEngine remains a preBuild dependency and uses --release --locked. +./gradlew :app:assembleRelease -PreactNativeArchitectures="${ROCKSKY_TEST_ABIS:-arm64-v8a}" "$@" +echo "Rocksky Test APK: $app_dir/android/app/build/outputs/apk/release/app-release.apk"