From 3cafdcbe78f3793535fecd285e9fbfc32fe8d154 Mon Sep 17 00:00:00 2001 From: Najuna Brian Date: Thu, 4 Jun 2026 01:56:31 +0300 Subject: [PATCH 1/5] ci(android): run FOSS patches and generate before release builds Align GitHub Actions with the F-Droid init recipe so Play and F-Droid ship the same dependency patches and generated assets. --- .github/workflows/formulus-android.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.github/workflows/formulus-android.yml b/.github/workflows/formulus-android.yml index 919a9e606..51a1ae036 100644 --- a/.github/workflows/formulus-android.yml +++ b/.github/workflows/formulus-android.yml @@ -121,6 +121,16 @@ jobs: working-directory: formulus run: pnpm run vendor:notifee + - name: Apply FOSS Android patches (match F-Droid init) + if: github.event_name != 'pull_request' + working-directory: formulus + run: pnpm run patch:android-foss + + - name: Generate injection script (match F-Droid init) + if: github.event_name != 'pull_request' + working-directory: formulus + run: pnpm run generate + - name: Set up Java uses: actions/setup-java@v4 with: From 7e97266dfe919a167a7e7156f40c5bc6015d7a9a Mon Sep 17 00:00:00 2001 From: Najuna Brian Date: Thu, 4 Jun 2026 01:57:50 +0300 Subject: [PATCH 2/5] fix(android): align split APK versionCodes with F-Droid --- formulus/android/app/build.gradle | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/formulus/android/app/build.gradle b/formulus/android/app/build.gradle index 7c869c946..f814ead13 100644 --- a/formulus/android/app/build.gradle +++ b/formulus/android/app/build.gradle @@ -89,6 +89,15 @@ def jscFlavor = 'io.github.react-native-community:jsc-android:2026004.+' def abiVersionCodes = ['armeabi-v7a': 1, 'arm64-v8a': 2, 'x86': 3, 'x86_64': 4] +/** + * Per-ABI versionCode for split APKs. Matches F-Droid metadata (base + ABI offset). + * F-Droid sets versionCode via prebuild; Play split APKs use the same formula locally. + * Play AAB (bundleRelease) uses defaultConfig.versionCode only. + */ +def perAbiVersionCode(int baseVersionCode, String abi) { + return baseVersionCode + abiVersionCodes.get(abi, 0) - 1 +} + android { ndkVersion = rootProject.ext.ndkVersion buildToolsVersion = rootProject.ext.buildToolsVersion @@ -158,8 +167,7 @@ android { variant.outputs.each { output -> def abi = output.getFilter(com.android.build.OutputFile.ABI) if (abi != null) { - output.versionCodeOverride = - (100 * defaultConfig.versionCode) + abiVersionCodes.get(abi, 0) + output.versionCodeOverride = perAbiVersionCode(defaultConfig.versionCode, abi) } } } From 646f24f4c1b06b1bb6c2f1b68b64ad9973ef34bd Mon Sep 17 00:00:00 2001 From: Najuna Brian Date: Thu, 4 Jun 2026 01:58:25 +0300 Subject: [PATCH 3/5] ci(android): build and upload Play Store AAB --- .github/workflows/formulus-android.yml | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/.github/workflows/formulus-android.yml b/.github/workflows/formulus-android.yml index 51a1ae036..5c020c844 100644 --- a/.github/workflows/formulus-android.yml +++ b/.github/workflows/formulus-android.yml @@ -75,7 +75,7 @@ jobs: retention-days: 7 build-android: - name: Build Formulus Android APK + name: Build Formulus Android runs-on: ubuntu-latest needs: build-formplayer-assets permissions: @@ -176,6 +176,11 @@ jobs: working-directory: formulus/android run: ./gradlew assembleRelease --no-daemon + - name: Build release AAB for Play Store (main/dev/release) + if: github.event_name != 'pull_request' + working-directory: formulus/android + run: ./gradlew bundleRelease --no-daemon + - name: Upload APK artifact uses: actions/upload-artifact@v6 with: @@ -183,11 +188,20 @@ jobs: path: | formulus/android/app/build/outputs/apk/**/**/*.apk + - name: Upload AAB artifact + if: github.event_name != 'pull_request' + uses: actions/upload-artifact@v6 + with: + name: formulus-android-aab-${{ github.event_name }}-${{ github.run_id }} + path: | + formulus/android/app/build/outputs/bundle/release/*.aab + - name: Upload APK to GitHub Release if: github.event_name == 'release' uses: softprops/action-gh-release@v2 with: files: | formulus/android/app/build/outputs/apk/**/**/*.apk + formulus/android/app/build/outputs/bundle/release/*.aab env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 493122fd9bda3d6f10c2dadb10d5ca630385d14a Mon Sep 17 00:00:00 2001 From: Najuna Brian Date: Thu, 4 Jun 2026 02:00:39 +0300 Subject: [PATCH 4/5] fix(android): fix perAbiVersionCode gradle scope --- formulus/android/app/build.gradle | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/formulus/android/app/build.gradle b/formulus/android/app/build.gradle index f814ead13..74cc1a770 100644 --- a/formulus/android/app/build.gradle +++ b/formulus/android/app/build.gradle @@ -87,15 +87,9 @@ def enableProguardInReleaseBuilds = false */ def jscFlavor = 'io.github.react-native-community:jsc-android:2026004.+' -def abiVersionCodes = ['armeabi-v7a': 1, 'arm64-v8a': 2, 'x86': 3, 'x86_64': 4] - -/** - * Per-ABI versionCode for split APKs. Matches F-Droid metadata (base + ABI offset). - * F-Droid sets versionCode via prebuild; Play split APKs use the same formula locally. - * Play AAB (bundleRelease) uses defaultConfig.versionCode only. - */ def perAbiVersionCode(int baseVersionCode, String abi) { - return baseVersionCode + abiVersionCodes.get(abi, 0) - 1 + def offsets = ['armeabi-v7a': 1, 'arm64-v8a': 2, 'x86': 3, 'x86_64': 4] + return baseVersionCode + offsets.get(abi, 0) - 1 } android { From 5d93f34bd5ac25012adf71ce1c7584848d15e009 Mon Sep 17 00:00:00 2001 From: Najuna Brian Date: Thu, 4 Jun 2026 02:00:39 +0300 Subject: [PATCH 5/5] docs(android): add Play and F-Droid release guide --- formulus/android/ANDROID_RELEASE.md | 49 +++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 formulus/android/ANDROID_RELEASE.md diff --git a/formulus/android/ANDROID_RELEASE.md b/formulus/android/ANDROID_RELEASE.md new file mode 100644 index 000000000..f01fe14ea --- /dev/null +++ b/formulus/android/ANDROID_RELEASE.md @@ -0,0 +1,49 @@ +# Android release: Google Play and F-Droid + +Formulus ships from one FOSS codebase. Play Store and F-Droid use the same dependency patches and build steps; only packaging and version-code handling differ. + +## Shared release prep + +1. Bump `versionName` / `versionCode` in `app/build.gradle` (or run `pnpm run sync:version` from `formulus/`). +2. From `formulus/`: + ```sh + pnpm install --frozen-lockfile + pnpm run vendor:notifee + pnpm run patch:android-foss + pnpm run generate + ``` +3. Tag the release commit on the upstream repo, e.g. `v1.0.2` (F-Droid update checks use stable tags matching `v[\d.]+$`). + +## Google Play + +- **Artifact:** Android App Bundle (AAB) — `./gradlew bundleRelease` in `formulus/android/`. +- **CI:** GitHub Actions builds `bundleRelease` on `main`, `dev`, and GitHub Releases; uploads the AAB artifact and attaches it to releases. +- **versionCode:** Uses `defaultConfig.versionCode` in the AAB (single code per release). +- **Signing:** See [SIGNING_CONFIG.md](./SIGNING_CONFIG.md). CI uses repository secrets; local builds use `android/local.properties`. + +Optional split APKs for sideloading or GitHub Release assets: + +```sh +./gradlew assembleRelease +``` + +Split APKs use per-ABI version codes: `baseVersionCode + abiOffset` (2–5 when base is 2). + +## F-Droid + +- **Artifact:** One APK per ABI, built from source by F-Droid. +- **Metadata:** `fdroiddata/metadata/org.opendataensemble.formulus.yml` — four builds with `abiFilters` and explicit `versionCode` per ABI. +- **Gradle:** Pass `-PabiFilters=`; F-Droid prebuild strips custom APK naming and sets `versionCode = $$VERCODE$$`. +- **Init steps:** Same as shared prep above (`vendor:notifee`, `patch:android-foss`, `generate`). + +After tagging, update the metadata commit SHA and version fields, then open/update the fdroiddata merge request. + +## Version codes + +| Channel | versionCode | +|---------|-------------| +| Play AAB | `defaultConfig.versionCode` | +| Play / local split APKs | base + ABI offset (armeabi-v7a +0, arm64-v8a +1, x86 +2, x86_64 +3) | +| F-Droid per-ABI build | Set in metadata prebuild (`$$VERCODE$$`) | + +When bumping a release, increase `defaultConfig.versionCode` and update F-Droid build entries (and `CurrentVersionCode`) to match the highest per-ABI code.