From 0471aee5a5134dab03f419acb937621ff1be2135 Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 14 Apr 2026 23:24:17 +0200 Subject: [PATCH] CI: GitHub Actions workflow to auto-build signed Android APK on tag push MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On every v*.*.* tag push the workflow: 1. Checks out the repo 2. Sets up JDK 17 + Node 20 + Android SDK (cached between runs) 3. Runs npm install + npx cap sync android in mobile/ 4. Restores the signing keystore from ANDROID_KEYSTORE_BASE64 secret 5. Builds a signed release APK via gradle 6. Renames to pedscribe-X.Y.Z.apk 7. Creates/updates the matching GitHub release with the APK attached and make_latest=true so the /releases/latest URL always points to the newest build (Obtanium and the login-page link pick it up automatically) Required repo secrets (set via gh secret set ... or the GitHub UI): ANDROID_KEYSTORE_BASE64 base64 -w0 of the .jks file ANDROID_KEYSTORE_PASSWORD keystore password ANDROID_KEY_ALIAS key alias (pedscribe) ANDROID_KEY_PASSWORD key password (same as keystore in our setup) Typical release flow after this lands: scripts/release.sh 6.1.1 --push (laptop, 5 sec) ── Actions builds APK in ~8-10 min ── ── Release updates automatically with signed APK ── ── Obtanium clients notice on next poll ── --- .github/workflows/android-release.yml | 116 ++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 .github/workflows/android-release.yml diff --git a/.github/workflows/android-release.yml b/.github/workflows/android-release.yml new file mode 100644 index 0000000..2e82eae --- /dev/null +++ b/.github/workflows/android-release.yml @@ -0,0 +1,116 @@ +name: Build & release Android APK + +# Fires whenever a semver tag is pushed (e.g. v6.1.1). Use +# scripts/release.sh --push from your laptop to mint the +# tag; this workflow does everything downstream. +on: + push: + tags: + - 'v[0-9]+.[0-9]+.[0-9]+' + workflow_dispatch: + inputs: + version: + description: 'Manual tag to build (e.g. v6.1.1)' + required: true + +permissions: + contents: write # needed to create GitHub releases from the runner + +jobs: + build: + name: Build signed APK + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Resolve tag + id: tag + run: | + TAG="${GITHUB_REF_NAME}" + if [[ -z "$TAG" || "$TAG" == "main" ]]; then + TAG="${{ github.event.inputs.version }}" + fi + echo "tag=$TAG" >> "$GITHUB_OUTPUT" + echo "version=${TAG#v}" >> "$GITHUB_OUTPUT" + + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: '17' + + - name: Set up Node 20 + uses: actions/setup-node@v4 + with: + node-version: '20' + cache: npm + cache-dependency-path: mobile/package-lock.json + + - name: Set up Android SDK + uses: android-actions/setup-android@v3 + + - name: Cache Gradle packages + uses: actions/cache@v4 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: gradle-${{ runner.os }}-${{ hashFiles('mobile/android/**/*.gradle*', 'mobile/android/gradle/wrapper/gradle-wrapper.properties') }} + restore-keys: gradle-${{ runner.os }}- + + - name: Install Capacitor + sync + working-directory: mobile + run: | + npm install --no-audit --no-fund + npx cap sync android + + - name: Restore keystore from secret + env: + KEYSTORE_B64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }} + run: | + echo "$KEYSTORE_B64" | base64 -d > $RUNNER_TEMP/pedscribe-release.jks + ls -la $RUNNER_TEMP/pedscribe-release.jks + + - name: Build signed release APK + working-directory: mobile/android + env: + KS_PASS: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }} + KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }} + KEY_PASS: ${{ secrets.ANDROID_KEY_PASSWORD }} + run: | + ./gradlew assembleRelease \ + -Pandroid.injected.signing.store.file=$RUNNER_TEMP/pedscribe-release.jks \ + -Pandroid.injected.signing.store.password="$KS_PASS" \ + -Pandroid.injected.signing.key.alias="$KEY_ALIAS" \ + -Pandroid.injected.signing.key.password="$KEY_PASS" \ + --no-daemon --stacktrace + + - name: Locate APK + id: apk + run: | + APK=$(find mobile/android/app/build/outputs/apk/release -name '*.apk' | head -1) + test -n "$APK" || { echo "no APK found"; exit 1; } + echo "path=$APK" >> "$GITHUB_OUTPUT" + echo "found: $APK ($(stat -c%s "$APK") bytes)" + + - name: Rename APK with version + id: rename + run: | + DST="pedscribe-${{ steps.tag.outputs.version }}.apk" + cp "${{ steps.apk.outputs.path }}" "$DST" + echo "path=$DST" >> "$GITHUB_OUTPUT" + + - name: Create or update GitHub release + uses: softprops/action-gh-release@v2 + with: + tag_name: ${{ steps.tag.outputs.tag }} + name: PedScribe ${{ steps.tag.outputs.version }} + make_latest: 'true' + generate_release_notes: true + files: | + ${{ steps.rename.outputs.path }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}