From 069e359a8d5b13846f2649370395d022d6ebfa64 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 2 Sep 2026 06:58:54 +0000 Subject: [PATCH] Fail release build when signing secrets are missing Confirmed via CI logs (release.yml run for v1.0.0.41) that KEYSTORE_FILE/KEYSTORE_PASSWORD/KEY_ALIAS/KEY_PASSWORD are all empty in this repo, so app/build.gradle's release build falls back to debug signing. Each GitHub Actions job runs on a fresh, ephemeral VM with no persisted ~/.android/debug.keystore, so that fallback generates a new, different debug keystore on every run - meaning every published GitHub Release APK is signed with a different certificate than the previous one. Android refuses to install an update whose signing certificate doesn't match the currently-installed app's, so the in-app "Update to latest" flow always downloads the new APK successfully and then always fails at install time, regardless of network conditions or device. Add a guard step that fails the workflow with a clear error instead of silently publishing another release nobody can update to, until the real signing secrets are configured in the repo. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01147MnP2Vf5sDY2BT7UhBbn --- .github/workflows/release.yml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ff5d70e..36f1b86 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -26,6 +26,22 @@ jobs: - name: Grant execute permission for gradlew run: chmod +x gradlew + - name: Verify release signing secrets are configured + run: | + # app/build.gradle silently falls back to debug signing when KEYSTORE_FILE isn't set, + # which lets `./gradlew assembleRelease` still work on a dev machine. But on this + # ephemeral GitHub Actions runner that fallback creates a brand-new, random debug + # keystore on every single run (there's no persisted ~/.android/debug.keystore to + # reuse) - so each GitHub Release APK ends up signed with a *different* certificate. + # Android refuses to install an update whose signing certificate doesn't match the + # currently-installed app's, so the in-app "Update to latest" flow + # (UpdateRepositoryImpl/AppUpdateInstaller) always downloads fine and then always fails + # to install. Fail loudly here instead of silently publishing another broken update. + if [ -z "${{ secrets.KEYSTORE_FILE }}" ]; then + echo "::error::KEYSTORE_FILE (and KEYSTORE_PASSWORD/KEY_ALIAS/KEY_PASSWORD) repository secrets are not set. Without them, this workflow falls back to a fresh debug keystore on every run, so each release gets a different signing certificate and in-app updates fail to install. Add the real signing secrets under Settings > Secrets and variables > Actions before releasing." + exit 1 + fi + - name: Build and Sign APK env: BUILD_NUMBER: ${{ github.run_number }}