From e480b528dfcca72f9ea70cddf4787520b69b1ca4 Mon Sep 17 00:00:00 2001 From: morepriyam Date: Fri, 21 Aug 2026 20:30:53 +0530 Subject: [PATCH 1/3] ci: add Android build + Play Store upload workflow Android twin of build-ios-from-expo.yml. Composes mieweb/actions building blocks (pinned to v2.3.0): stamp versionCode, expo prebuild, build-sign-android (direct-keystore), publish-android-to-play. Closes #52 --- .github/workflows/build-android-from-expo.yml | 108 ++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 .github/workflows/build-android-from-expo.yml diff --git a/.github/workflows/build-android-from-expo.yml b/.github/workflows/build-android-from-expo.yml new file mode 100644 index 0000000..16b310d --- /dev/null +++ b/.github/workflows/build-android-from-expo.yml @@ -0,0 +1,108 @@ +# ───────────────────────────────────────────────────────────────────────────── +# Android Expo Build & Upload +# +# Android twin of build-ios-from-expo.yml. Composes mieweb/actions building +# blocks (pinned to v2.3.0): +# checkout + Node + JS deps → stamp versionCode → expo prebuild (android) +# → build-sign-android (direct-keystore) → publish-android-to-play +# +# Signing mode: direct-keystore +# You supply the upload keystore (.jks) and its credentials as secrets. +# Gradle signs the release bundle with injected signing properties — no +# fastlane match, no keystore committed to the repo. +# +# Required org/repo secrets: +# ANDROID_KEYSTORE_BASE64 base64 of the upload keystore (.jks) +# ANDROID_KEYSTORE_PASSWORD keystore password +# ANDROID_KEY_ALIAS signing key alias inside the keystore +# ANDROID_KEY_PASSWORD password of that key +# PLAY_JSON_KEY_BASE64 base64 of the Google Play service-account JSON +# (only needed when uploading to Google Play) +# +# NOTE: the very FIRST bundle for a new Play Console app must be uploaded +# manually through the Play Console UI; the API (and therefore this workflow) +# can only publish once the app record exists with an initial upload. +# ───────────────────────────────────────────────────────────────────────────── +name: Android Expo Build & Upload + +on: + workflow_dispatch: + inputs: + upload_to_play: + description: "Upload the build to Google Play" + type: boolean + default: true + track: + description: "Google Play track" + type: choice + options: [internal, alpha, beta, production] + default: internal + +permissions: + contents: read + +jobs: + android: + runs-on: ubuntu-latest + steps: + # ── Checkout, JDK, Node, JS deps ────────────────────────────────────── + # prepare-expo-env is macOS-only (it selects Xcode), so set up directly. + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup JDK + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: "17" + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: "20" + + - name: Install JS dependencies + run: npm ci + + # ── Give every CI build a unique, increasing versionCode ────────────── + # Base versionCode from app.json + per-workflow run number keeps the + # value monotonically increasing and above any previous manual upload. + - name: Stamp versionCode + run: | + node -e "const fs=require('fs');const j=JSON.parse(fs.readFileSync('app.json','utf8'));j.expo.android=j.expo.android||{};j.expo.android.versionCode=(j.expo.android.versionCode||0)+Number(process.env.GITHUB_RUN_NUMBER);fs.writeFileSync('app.json',JSON.stringify(j,null,2));console.log('Android versionCode =',j.expo.android.versionCode);" + + # ── Regenerate native android/ from app.json ────────────────────────── + - name: Expo prebuild (android) + run: npx expo prebuild --clean --platform android --non-interactive + + # ── Build + sign the release bundle ─────────────────────────────────── + - name: Build & sign Android bundle + id: build + uses: mieweb/actions/build-sign-android@360fec8818c88cc06f1e3a997b227daba34aa1a3 # v2.3.0 + with: + signing_method: direct-keystore + app_identifier: com.mieweb.pulse + android_build_dir: android + build_type: bundle + keystore_base64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }} + keystore_password: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }} + key_alias: ${{ secrets.ANDROID_KEY_ALIAS }} + key_password: ${{ secrets.ANDROID_KEY_PASSWORD }} + + # ── Keep the signed bundle as a run artifact ────────────────────────── + - name: Upload signed bundle artifact + uses: actions/upload-artifact@v4 + with: + name: pulse-android-release + path: ${{ steps.build.outputs.signed_artifact_path }} + if-no-files-found: error + + # ── Publish to Google Play ──────────────────────────────────────────── + - name: Publish to Google Play + if: inputs.upload_to_play + uses: mieweb/actions/publish-android-to-play@360fec8818c88cc06f1e3a997b227daba34aa1a3 # v2.3.0 + with: + signed_artifact_path: ${{ steps.build.outputs.signed_artifact_path }} + play_json_key_base64: ${{ secrets.PLAY_JSON_KEY_BASE64 }} + package_name: com.mieweb.pulse + track: ${{ inputs.track }} From 2ff1da806ab1855a2ebab12191f636cdf349c33a Mon Sep 17 00:00:00 2001 From: morepriyam Date: Fri, 21 Aug 2026 20:40:02 +0530 Subject: [PATCH 2/3] ci: serialize android workflow runs with a concurrency group Overlapping workflow_dispatch runs could publish out of order, carrying a lower versionCode than an already-published later run. --- .github/workflows/build-android-from-expo.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/build-android-from-expo.yml b/.github/workflows/build-android-from-expo.yml index 16b310d..45dac62 100644 --- a/.github/workflows/build-android-from-expo.yml +++ b/.github/workflows/build-android-from-expo.yml @@ -43,6 +43,11 @@ permissions: jobs: android: + # Serialize runs: versionCode ordering relies on run numbers, so a later + # run must never publish before an earlier one still in flight. + concurrency: + group: ${{ github.workflow }}-android + cancel-in-progress: false runs-on: ubuntu-latest steps: # ── Checkout, JDK, Node, JS deps ────────────────────────────────────── From be96db3c56d08a298f544bf959cfebdc4794de7c Mon Sep 17 00:00:00 2001 From: morepriyam Date: Fri, 21 Aug 2026 21:02:48 +0530 Subject: [PATCH 3/3] ci: fix prebuild flag and raise Gradle heap for release builds Verified the pipeline end-to-end locally with a throwaway keystore: versionCode stamp, expo prebuild, gradle bundleRelease with injected signing, and jarsigner verification all pass. - expo prebuild does not support --non-interactive (CI=1 covers it) - the Expo template's -Xmx2048m can OOM the Gradle daemon during the R8 release build; bump to 4g after prebuild regenerates the file --- .github/workflows/build-android-from-expo.yml | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-android-from-expo.yml b/.github/workflows/build-android-from-expo.yml index 45dac62..f2aefcd 100644 --- a/.github/workflows/build-android-from-expo.yml +++ b/.github/workflows/build-android-from-expo.yml @@ -77,8 +77,19 @@ jobs: node -e "const fs=require('fs');const j=JSON.parse(fs.readFileSync('app.json','utf8'));j.expo.android=j.expo.android||{};j.expo.android.versionCode=(j.expo.android.versionCode||0)+Number(process.env.GITHUB_RUN_NUMBER);fs.writeFileSync('app.json',JSON.stringify(j,null,2));console.log('Android versionCode =',j.expo.android.versionCode);" # ── Regenerate native android/ from app.json ────────────────────────── + # CI=1 (set by GitHub Actions) makes the CLI non-interactive. - name: Expo prebuild (android) - run: npx expo prebuild --clean --platform android --non-interactive + run: npx expo prebuild --clean --platform android + + # ── Raise the Gradle JVM heap ────────────────────────────────────────── + # The Expo template caps Gradle at -Xmx2048m, which can OOM the daemon + # during the R8-minified release build. Prebuild regenerates + # gradle.properties every run, so patch it here. + - name: Raise Gradle JVM heap + run: | + sed -i.bak 's/^org.gradle.jvmargs=.*/org.gradle.jvmargs=-Xmx4g -XX:MaxMetaspaceSize=1g/' android/gradle.properties + rm -f android/gradle.properties.bak + grep "^org.gradle.jvmargs" android/gradle.properties # ── Build + sign the release bundle ─────────────────────────────────── - name: Build & sign Android bundle