diff --git a/.github/workflows/fork-release.yml b/.github/workflows/fork-release.yml index d2f752ba22050..2a346ef8c75cc 100644 --- a/.github/workflows/fork-release.yml +++ b/.github/workflows/fork-release.yml @@ -348,23 +348,53 @@ jobs: # upstream's preview bundles. The release version replaces upstream's # store version so Obtainium can match the APK to its release tag. It # goes in .env because the Gradle bundling step reads app.config.ts again. + # The fork ships its own app: the ember icon always, and the package name, + # app name and Firebase config from repository settings when they are set. + # Without Firebase config the app builds without push notifications. - name: Configure the build env: VERSION: ${{ needs.prepare.outputs.version }} + ANDROID_PACKAGE: ${{ vars.T3CODE_ANDROID_PACKAGE }} + APP_NAME: ${{ vars.T3CODE_APP_NAME }} + GOOGLE_SERVICES_JSON: ${{ secrets.ANDROID_GOOGLE_SERVICES_JSON }} run: | cp .env.example .env - echo "T3CODE_MOBILE_UPDATES_ENABLED=0" >> .env - echo "T3CODE_MOBILE_VERSION=$VERSION" >> .env + { + echo "T3CODE_MOBILE_UPDATES_ENABLED=0" + echo "T3CODE_MOBILE_VERSION=$VERSION" + echo "T3CODE_ANDROID_ICON_BACKGROUND=./assets/android-icon-background-ember.png" + echo "T3CODE_ANDROID_PACKAGE=$ANDROID_PACKAGE" + echo "T3CODE_APP_NAME=$APP_NAME" + } >> .env + if [ -n "$GOOGLE_SERVICES_JSON" ]; then + printf '%s' "$GOOGLE_SERVICES_JSON" > "$RUNNER_TEMP/google-services.json" + echo "T3CODE_ANDROID_GOOGLE_SERVICES_FILE=$RUNNER_TEMP/google-services.json" >> .env + fi - name: Generate the native project working-directory: apps/mobile run: ./node_modules/.bin/expo prebuild --platform android --clean --no-install - # Release builds sign with the React Native template's debug keystore, - # the same key as the APK already installed from earlier fork builds. + # Signs with the repository's release keystore when one is set, and + # otherwise with the React Native template's public debug keystore. - name: Build release APK working-directory: apps/mobile/android - run: ./gradlew :app:assembleRelease -PreactNativeArchitectures=arm64-v8a -x lintVitalAnalyzeRelease --no-daemon + env: + KEYSTORE_BASE64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }} + KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }} + KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }} + run: | + signing=() + if [ -n "$KEYSTORE_BASE64" ]; then + printf '%s' "$KEYSTORE_BASE64" | base64 -d > "$RUNNER_TEMP/release.keystore" + signing=( + "-Pandroid.injected.signing.store.file=$RUNNER_TEMP/release.keystore" + "-Pandroid.injected.signing.store.password=$KEYSTORE_PASSWORD" + "-Pandroid.injected.signing.key.alias=$KEY_ALIAS" + "-Pandroid.injected.signing.key.password=$KEYSTORE_PASSWORD" + ) + fi + ./gradlew :app:assembleRelease -PreactNativeArchitectures=arm64-v8a -x lintVitalAnalyzeRelease --no-daemon "${signing[@]}" - name: Collect APK env: diff --git a/FORK.md b/FORK.md index 901559213762f..3c7036d2e508c 100644 --- a/FORK.md +++ b/FORK.md @@ -71,3 +71,6 @@ adds the feature, and remove it in the PR that drops the feature. - #30 A checkout's pull requests and repository come from the remote `gh repo set-default` chose, ahead of `upstream`, so a fork lists its own PRs: `apps/server/src/project/RepositoryIdentityResolver.ts` (`parseGhDefaultRemote`). +- #32 The Android APK is the fork's own app: an ember icon, plus the package name, app name, release + keystore and Firebase config from repository variables and secrets when they are set: + `apps/mobile/app.config.ts` (`T3CODE_ANDROID_PACKAGE`), `.github/workflows/fork-release.yml`. diff --git a/apps/mobile/app.config.ts b/apps/mobile/app.config.ts index 1caf24896e6e9..63972f999967b 100644 --- a/apps/mobile/app.config.ts +++ b/apps/mobile/app.config.ts @@ -124,6 +124,17 @@ const iosBundleIdentifier = isIosPersonalTeamBuild ? personalTeamBundleIdentifier! : variant.iosBundleIdentifier; +// Fork builds rebrand the Android app here; unset values keep the variant's. +const appName = repoEnv.T3CODE_APP_NAME?.trim() || variant.appName; +const androidPackage = repoEnv.T3CODE_ANDROID_PACKAGE?.trim() || variant.androidPackage; +const androidAdaptiveBackgroundImage = + repoEnv.T3CODE_ANDROID_ICON_BACKGROUND?.trim() || variant.assets.androidAdaptiveBackgroundImage; +if (!/^[A-Za-z][A-Za-z0-9_]*(?:\.[A-Za-z][A-Za-z0-9_]*)+$/.test(androidPackage)) { + throw new Error( + "T3CODE_ANDROID_PACKAGE must be an Android package name such as com.example.t3code.", + ); +} + const dmSansFonts = { regular: "@expo-google-fonts/dm-sans/400Regular/DMSans_400Regular.ttf", medium: "@expo-google-fonts/dm-sans/500Medium/DMSans_500Medium.ttf", @@ -228,7 +239,7 @@ const sharingPlugin: NonNullable[number] = [ // Match iOS PostScript names on Android so RN and the native composer share fonts before runtime loading. const config: ExpoConfig = { - name: variant.appName, + name: appName, slug: "t3-code", platforms: ["ios", "android"], scheme: variant.scheme, @@ -291,14 +302,14 @@ const config: ExpoConfig = { }, android: { icon: variant.assets.appIcon, - package: variant.androidPackage, + package: androidPackage, ...(repoEnv.T3CODE_ANDROID_GOOGLE_SERVICES_FILE ? { googleServicesFile: repoEnv.T3CODE_ANDROID_GOOGLE_SERVICES_FILE } : {}), adaptiveIcon: { backgroundColor: variant.assets.androidAdaptiveBackgroundColor, - ...(variant.assets.androidAdaptiveBackgroundImage - ? { backgroundImage: variant.assets.androidAdaptiveBackgroundImage } + ...(androidAdaptiveBackgroundImage + ? { backgroundImage: androidAdaptiveBackgroundImage } : {}), foregroundImage: variant.assets.androidAdaptiveForeground, monochromeImage: variant.assets.androidMonochromeIcon, diff --git a/apps/mobile/assets/android-icon-background-ember.png b/apps/mobile/assets/android-icon-background-ember.png new file mode 100644 index 0000000000000..7cd310448697a Binary files /dev/null and b/apps/mobile/assets/android-icon-background-ember.png differ