From 8a1a6b2949b39129f1cc6f64f02c4d5a9fb425fe Mon Sep 17 00:00:00 2001 From: Fodesu Date: Wed, 27 May 2026 21:36:28 +0800 Subject: [PATCH 1/2] Fix signed app release artifacts --- .github/workflows/app-ci.yml | 14 +++++++++++ .github/workflows/app-release.yml | 42 +++++++++++++++++++++++++++++++ CHANGELOG.md | 4 +++ app/build.gradle.kts | 24 ++++++++++++++++++ 4 files changed, 84 insertions(+) diff --git a/.github/workflows/app-ci.yml b/.github/workflows/app-ci.yml index bbfcdbe..d84fa01 100644 --- a/.github/workflows/app-ci.yml +++ b/.github/workflows/app-ci.yml @@ -36,6 +36,20 @@ jobs: - name: Run Android quality checks run: ./gradlew ktlintCheck detekt testDebugUnitTest assembleDebug assembleRelease --stacktrace + - name: Verify debug APK signature + shell: bash + run: | + set -euo pipefail + shopt -s nullglob + apks=(app/build/outputs/apk/debug/*.apk) + if (( ${#apks[@]} == 0 )); then + echo "No debug APKs found" >&2 + exit 1 + fi + for apk in "${apks[@]}"; do + apksigner verify --verbose --print-certs "$apk" + done + - name: Upload debug APK uses: actions/upload-artifact@v6 with: diff --git a/.github/workflows/app-release.yml b/.github/workflows/app-release.yml index 1aefc2d..ac7b5b2 100644 --- a/.github/workflows/app-release.yml +++ b/.github/workflows/app-release.yml @@ -26,9 +26,51 @@ jobs: - name: Setup Android SDK uses: android-actions/setup-android@v3 + - name: Decode release keystore + shell: bash + env: + AUTOFISH_RELEASE_KEYSTORE_BASE64: ${{ secrets.AUTOFISH_RELEASE_KEYSTORE_BASE64 }} + AUTOFISH_RELEASE_STORE_PASSWORD: ${{ secrets.AUTOFISH_RELEASE_STORE_PASSWORD }} + AUTOFISH_RELEASE_KEY_ALIAS: ${{ secrets.AUTOFISH_RELEASE_KEY_ALIAS }} + AUTOFISH_RELEASE_KEY_PASSWORD: ${{ secrets.AUTOFISH_RELEASE_KEY_PASSWORD }} + run: | + set -euo pipefail + for secret_name in \ + AUTOFISH_RELEASE_KEYSTORE_BASE64 \ + AUTOFISH_RELEASE_STORE_PASSWORD \ + AUTOFISH_RELEASE_KEY_ALIAS \ + AUTOFISH_RELEASE_KEY_PASSWORD + do + if [[ -z "${!secret_name}" ]]; then + echo "$secret_name is required for app releases" >&2 + exit 1 + fi + done + mkdir -p "$RUNNER_TEMP/autofish-signing" + printf '%s' "${AUTOFISH_RELEASE_KEYSTORE_BASE64}" | base64 --decode > "$RUNNER_TEMP/autofish-signing/release.keystore" + - name: Build release APK + env: + AUTOFISH_RELEASE_STORE_FILE: ${{ runner.temp }}/autofish-signing/release.keystore + AUTOFISH_RELEASE_STORE_PASSWORD: ${{ secrets.AUTOFISH_RELEASE_STORE_PASSWORD }} + AUTOFISH_RELEASE_KEY_ALIAS: ${{ secrets.AUTOFISH_RELEASE_KEY_ALIAS }} + AUTOFISH_RELEASE_KEY_PASSWORD: ${{ secrets.AUTOFISH_RELEASE_KEY_PASSWORD }} run: ./gradlew assembleRelease + - name: Verify release APK signature + shell: bash + run: | + set -euo pipefail + shopt -s nullglob + apks=(app/build/outputs/apk/release/*.apk) + if (( ${#apks[@]} == 0 )); then + echo "No release APKs found" >&2 + exit 1 + fi + for apk in "${apks[@]}"; do + apksigner verify --verbose --print-certs "$apk" + done + - name: Collect APKs shell: bash run: | diff --git a/CHANGELOG.md b/CHANGELOG.md index 33e9d40..9369fb9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file. ## [Unreleased] +### Fixed +- Sign app release APKs in the GitHub release workflow and verify release artifacts with `apksigner` before publishing. +- Verify debug APK signatures in Android CI so installability regressions are caught earlier. + ## [0.5.0] - 2026-05-16 Range: `app-v0.4.0` / `cli-v0.4.0` .. `app-v0.5.0` / `cli-v0.5.0` diff --git a/app/build.gradle.kts b/app/build.gradle.kts index bb57c7f..5d61c11 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -48,6 +48,16 @@ val versionCodeProp = rawVersionCodeProp!! } +val releaseStoreFile = providers.environmentVariable("AUTOFISH_RELEASE_STORE_FILE") +val releaseStorePassword = providers.environmentVariable("AUTOFISH_RELEASE_STORE_PASSWORD") +val releaseKeyAlias = providers.environmentVariable("AUTOFISH_RELEASE_KEY_ALIAS") +val releaseKeyPassword = providers.environmentVariable("AUTOFISH_RELEASE_KEY_PASSWORD") +val hasReleaseSigningConfig = + releaseStoreFile.isPresent && + releaseStorePassword.isPresent && + releaseKeyAlias.isPresent && + releaseKeyPassword.isPresent + android { namespace = "com.memohai.autofish" compileSdk = 36 @@ -60,6 +70,17 @@ android { versionName = versionNameProp } + signingConfigs { + if (hasReleaseSigningConfig) { + create("release") { + storeFile = file(releaseStoreFile.get()) + storePassword = releaseStorePassword.get() + keyAlias = releaseKeyAlias.get() + keyPassword = releaseKeyPassword.get() + } + } + } + buildTypes { debug { applicationIdSuffix = ".debug" @@ -73,6 +94,9 @@ android { getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro", ) + if (hasReleaseSigningConfig) { + signingConfig = signingConfigs.getByName("release") + } } } From 54e284fe2f18ae445986b6817eb4e61ddba5d81e Mon Sep 17 00:00:00 2001 From: Fodesu Date: Wed, 27 May 2026 23:08:59 +0800 Subject: [PATCH 2/2] Resolve apksigner path in app workflows --- .github/workflows/app-ci.yml | 7 ++++++- .github/workflows/app-release.yml | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/.github/workflows/app-ci.yml b/.github/workflows/app-ci.yml index d84fa01..bc6edc5 100644 --- a/.github/workflows/app-ci.yml +++ b/.github/workflows/app-ci.yml @@ -40,6 +40,11 @@ jobs: shell: bash run: | set -euo pipefail + apksigner_bin="$(find "${ANDROID_HOME}/build-tools" -maxdepth 2 -type f -name apksigner | sort -V | tail -n1)" + if [[ -z "${apksigner_bin}" ]]; then + echo "apksigner not found under ${ANDROID_HOME}/build-tools" >&2 + exit 1 + fi shopt -s nullglob apks=(app/build/outputs/apk/debug/*.apk) if (( ${#apks[@]} == 0 )); then @@ -47,7 +52,7 @@ jobs: exit 1 fi for apk in "${apks[@]}"; do - apksigner verify --verbose --print-certs "$apk" + "${apksigner_bin}" verify --verbose --print-certs "$apk" done - name: Upload debug APK diff --git a/.github/workflows/app-release.yml b/.github/workflows/app-release.yml index ac7b5b2..6e44953 100644 --- a/.github/workflows/app-release.yml +++ b/.github/workflows/app-release.yml @@ -61,6 +61,11 @@ jobs: shell: bash run: | set -euo pipefail + apksigner_bin="$(find "${ANDROID_HOME}/build-tools" -maxdepth 2 -type f -name apksigner | sort -V | tail -n1)" + if [[ -z "${apksigner_bin}" ]]; then + echo "apksigner not found under ${ANDROID_HOME}/build-tools" >&2 + exit 1 + fi shopt -s nullglob apks=(app/build/outputs/apk/release/*.apk) if (( ${#apks[@]} == 0 )); then @@ -68,7 +73,7 @@ jobs: exit 1 fi for apk in "${apks[@]}"; do - apksigner verify --verbose --print-certs "$apk" + "${apksigner_bin}" verify --verbose --print-certs "$apk" done - name: Collect APKs