From 808a358b12942af3ce12365665a7513d769af3b3 Mon Sep 17 00:00:00 2001 From: Jake Edwards Date: Tue, 9 Jun 2026 11:18:19 -0400 Subject: [PATCH 1/2] Extend release.yml: Android matrix entry + APK/AAB build + Play upload - Adds android matrix entry alongside mac/win/linux. - Gates the existing desktop build step with if: matrix.platform != 'android'. - Adds JDK 21 + Android SDK + Gradle cache setup (Capacitor 8 plugins like @capacitor/geolocation require JavaVersion.VERSION_21). - Decodes the keystore from ANDROID_KEYSTORE_BASE64 secret. - Runs set-android-version.js before mobile:sync so versionCode is correct. - Builds release APK + AAB with signing creds from secrets. - Renames APK to ClawControl-X.Y.Z.apk before the existing softprops/action-gh-release@v2 step picks it up. - Uploads AAB to Play Console production track as draft via r0adkll/upload-google-play@v1. continue-on-error so a Play hiccup doesn't strand the APK; explicit fail-job step keeps run status honest; AAB attached to workflow run as fallback artifact. --- .github/workflows/release.yml | 90 +++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 019c356..3364d7f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -37,6 +37,10 @@ jobs: artifacts: | release/*.AppImage release/*.deb + - os: ubuntu-latest + platform: android + artifacts: | + android/app/build/outputs/apk/release/ClawControl-*.apk steps: - name: Checkout @@ -53,14 +57,100 @@ jobs: - name: Install dependencies run: npm ci + # ---------- Desktop build (mac / win / linux only) ---------- - name: Build (${{ matrix.platform }}) + if: matrix.platform != 'android' run: npm run build:${{ matrix.platform }} env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # ---------- Android build (android only) ---------- + - name: Setup JDK 21 + if: matrix.platform == 'android' + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: '21' + + - name: Setup Android SDK + if: matrix.platform == 'android' + uses: android-actions/setup-android@v3 + + - name: Setup Gradle + if: matrix.platform == 'android' + uses: gradle/actions/setup-gradle@v3 + + - name: Decode keystore + if: matrix.platform == 'android' + env: + KEYSTORE_BASE64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }} + run: | + echo "$KEYSTORE_BASE64" | base64 -d > android/app/upload-keystore.jks + test -s android/app/upload-keystore.jks || { echo "Keystore decode produced empty file"; exit 1; } + + - name: Sync Android version from package.json + if: matrix.platform == 'android' + run: node scripts/set-android-version.js + + - name: Build web + cap sync + edge-to-edge patch + if: matrix.platform == 'android' + run: npm run mobile:sync + + - name: Build release APK + if: matrix.platform == 'android' + env: + ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }} + ANDROID_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }} + ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }} + run: cd android && ./gradlew assembleRelease + + - name: Build release AAB + if: matrix.platform == 'android' + env: + ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }} + ANDROID_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }} + ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }} + run: cd android && ./gradlew bundleRelease + + - name: Rename APK to include version + if: matrix.platform == 'android' + run: | + VERSION=$(node -p "require('./package.json').version") + mv android/app/build/outputs/apk/release/app-release.apk \ + android/app/build/outputs/apk/release/ClawControl-${VERSION}.apk + ls -la android/app/build/outputs/apk/release/ + + # ---------- Attach artifacts to GitHub Release (all platforms) ---------- - name: Upload to release uses: softprops/action-gh-release@v2 with: tag_name: ${{ inputs.tag || github.ref_name }} files: ${{ matrix.artifacts }} fail_on_unmatched_files: false + + # ---------- Play Console upload (android only, after GitHub Release attach) ---------- + - name: Upload AAB to Play Console + id: play_upload + if: matrix.platform == 'android' + continue-on-error: true + uses: r0adkll/upload-google-play@v1 + with: + serviceAccountJsonPlainText: ${{ secrets.PLAY_SERVICE_ACCOUNT_JSON }} + packageName: com.claw.control + releaseFiles: android/app/build/outputs/bundle/release/app-release.aab + track: production + status: draft + + - name: AAB fallback artifact (on Play upload failure) + if: matrix.platform == 'android' && steps.play_upload.outcome == 'failure' + uses: actions/upload-artifact@v4 + with: + name: aab-fallback-${{ github.ref_name }} + path: android/app/build/outputs/bundle/release/app-release.aab + retention-days: 14 + + - name: Fail job if Play upload failed + if: matrix.platform == 'android' && steps.play_upload.outcome == 'failure' + run: | + echo "::error::Play Console upload failed. AAB attached as workflow artifact 'aab-fallback-${{ github.ref_name }}' for manual upload." + exit 1 From 3383f908df9ab9eb7662e9df7bb1ce364be33dda Mon Sep 17 00:00:00 2001 From: Jake Edwards Date: Tue, 9 Jun 2026 11:19:34 -0400 Subject: [PATCH 2/2] Add android-ci.yml: PR check that compiles assembleDebug Triggers on PRs touching android-relevant paths (android/**, capacitor.config.ts, vite.config.mobile.ts, the two helper scripts, package.json/lock, src/**, plugins/capacitor-native-websocket/**, and this workflow file itself). No secrets exposed (debug build only, fork-safe). Uploads app-debug.apk as a 7-day workflow artifact for manual smoke-testing from the PR. Concurrency group cancels older runs on force-push. --- .github/workflows/android-ci.yml | 65 ++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 .github/workflows/android-ci.yml diff --git a/.github/workflows/android-ci.yml b/.github/workflows/android-ci.yml new file mode 100644 index 0000000..7af5417 --- /dev/null +++ b/.github/workflows/android-ci.yml @@ -0,0 +1,65 @@ +name: Android CI + +on: + pull_request: + paths: + - 'android/**' + - 'capacitor.config.ts' + - 'vite.config.mobile.ts' + - 'scripts/fix-android-edge-to-edge.js' + - 'scripts/set-android-version.js' + - 'package.json' + - 'package-lock.json' + - 'src/**' + - 'plugins/capacitor-native-websocket/**' + - '.github/workflows/android-ci.yml' + workflow_dispatch: + +concurrency: + group: android-ci-${{ github.ref }} + cancel-in-progress: true + +jobs: + debug-build: + name: Build debug APK + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: 22 + cache: npm + + - name: Setup JDK 21 + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: '21' + + - name: Setup Android SDK + uses: android-actions/setup-android@v3 + + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v3 + + - name: Install dependencies + run: npm ci + + - name: Sync Android version from package.json + run: node scripts/set-android-version.js + + - name: Build web + cap sync + edge-to-edge patch + run: npm run mobile:sync + + - name: Build debug APK + run: cd android && ./gradlew assembleDebug + + - name: Upload debug APK as artifact + uses: actions/upload-artifact@v4 + with: + name: app-debug-${{ github.event.pull_request.number || github.run_id }}.apk + path: android/app/build/outputs/apk/debug/app-debug.apk + retention-days: 7