git.iko.soy/parseable-android
on master help sign in
Configure release signing for Android app builds (#22)

* Use stable release keystore to fix Obtainium update conflicts Release builds were signed with the auto-generated debug keystore, which is different on every CI runner. This caused APK signature mismatches when Obtainium users tried to update, since each release was signed with a different key. Add a committed release keystore so all builds use a consistent signing key, allowing seamless in-place updates. https://claude.ai/code/session_01X8SWFDBwvzJ8oJQSgbjFk8 * Move release keystore to GitHub secrets instead of committing it A committed keystore lets anyone build APKs that Android would accept as valid updates, which is a security risk. Instead, read the signing config from environment variables populated by GitHub secrets: - RELEASE_KEYSTORE_BASE64 (base64-encoded .keystore file) - RELEASE_KEYSTORE_PASSWORD - RELEASE_KEY_ALIAS - RELEASE_KEY_PASSWORD When the secrets aren't set (local dev, forks), the build falls back to the debug signing config so it still compiles. https://claude.ai/code/session_01X8SWFDBwvzJ8oJQSgbjFk8 * Add keystore files to .gitignore Prevent accidentally committing signing keystores to the repo. https://claude.ai/code/session_01X8SWFDBwvzJ8oJQSgbjFk8 --------- Co-authored-by: Claude <noreply@anthropic.com>

commit2f6fb81c1c3ffe366695b33885a0ad7656b813d5 authoriko <mail@iko.soy> dateMon 16 Feb 2026, 03:34 (8 months ago) parent382a6d7
4 files changed+47−1
.github/workflows/build-release.yml+11 .github/workflows/ci.yml+22 .gitignore+2 app/build.gradle.kts+12−1
.github/workflows/build-release.yml+11
@@ -22,6 +22,12 @@ jobs:
2222 - name: Checkout code
2323 uses: actions/checkout@v4
2424
25+ - name: Decode release keystore
26+ if: env.RELEASE_KEYSTORE_BASE64 != ''
27+ env:
28+ RELEASE_KEYSTORE_BASE64: ${{ secrets.RELEASE_KEYSTORE_BASE64 }}
29+ run: echo "$RELEASE_KEYSTORE_BASE64" | base64 -d > app/release.keystore
30+
2531 - name: Set up JDK 17
2632 uses: actions/setup-java@v4
2733 with:
@@ -38,6 +44,11 @@ jobs:
3844 run: gradle wrapper --gradle-version 8.9
3945
4046 - name: Build release APK
47+ env:
48+ RELEASE_KEYSTORE_FILE: ${{ secrets.RELEASE_KEYSTORE_BASE64 != '' && 'release.keystore' || '' }}
49+ RELEASE_KEYSTORE_PASSWORD: ${{ secrets.RELEASE_KEYSTORE_PASSWORD }}
50+ RELEASE_KEY_ALIAS: ${{ secrets.RELEASE_KEY_ALIAS }}
51+ RELEASE_KEY_PASSWORD: ${{ secrets.RELEASE_KEY_PASSWORD }}
4152 run: ./gradlew assembleRelease
4253
4354 - name: Run unit tests
.github/workflows/ci.yml+22
@@ -18,6 +18,12 @@ jobs:
1818 - name: Checkout code
1919 uses: actions/checkout@v4
2020
21+ - name: Decode release keystore
22+ if: env.RELEASE_KEYSTORE_BASE64 != ''
23+ env:
24+ RELEASE_KEYSTORE_BASE64: ${{ secrets.RELEASE_KEYSTORE_BASE64 }}
25+ run: echo "$RELEASE_KEYSTORE_BASE64" | base64 -d > app/release.keystore
26+
2127 - name: Set up JDK 17
2228 uses: actions/setup-java@v4
2329 with:
@@ -34,6 +40,11 @@ jobs:
3440 run: gradle wrapper --gradle-version 8.9
3541
3642 - name: Build release APK
43+ env:
44+ RELEASE_KEYSTORE_FILE: ${{ secrets.RELEASE_KEYSTORE_BASE64 != '' && 'release.keystore' || '' }}
45+ RELEASE_KEYSTORE_PASSWORD: ${{ secrets.RELEASE_KEYSTORE_PASSWORD }}
46+ RELEASE_KEY_ALIAS: ${{ secrets.RELEASE_KEY_ALIAS }}
47+ RELEASE_KEY_PASSWORD: ${{ secrets.RELEASE_KEY_PASSWORD }}
3748 run: ./gradlew assembleRelease
3849
3950 - name: Run unit tests
@@ -73,6 +84,12 @@ jobs:
7384 - name: Checkout code
7485 uses: actions/checkout@v4
7586
87+ - name: Decode release keystore
88+ if: env.RELEASE_KEYSTORE_BASE64 != ''
89+ env:
90+ RELEASE_KEYSTORE_BASE64: ${{ secrets.RELEASE_KEYSTORE_BASE64 }}
91+ run: echo "$RELEASE_KEYSTORE_BASE64" | base64 -d > app/release.keystore
92+
7693 - name: Set version from commit timestamp
7794 run: |
7895 VERSION_NAME=$(TZ=UTC git log -1 --format='%cd' --date=format-local:'%Y.%m.%d.%H%M')
@@ -94,6 +111,11 @@ jobs:
94111 run: gradle wrapper --gradle-version 8.9
95112
96113 - name: Build release APK
114+ env:
115+ RELEASE_KEYSTORE_FILE: ${{ secrets.RELEASE_KEYSTORE_BASE64 != '' && 'release.keystore' || '' }}
116+ RELEASE_KEYSTORE_PASSWORD: ${{ secrets.RELEASE_KEYSTORE_PASSWORD }}
117+ RELEASE_KEY_ALIAS: ${{ secrets.RELEASE_KEY_ALIAS }}
118+ RELEASE_KEY_PASSWORD: ${{ secrets.RELEASE_KEY_PASSWORD }}
97119 run: ./gradlew assembleRelease
98120
99121 - name: Rename APK with version
.gitignore+2
@@ -10,3 +10,5 @@
1010local.properties
1111app/build/
1212gradle/wrapper/gradle-wrapper.jar
13+*.keystore
14+*.jks
app/build.gradle.kts+12−1
@@ -32,11 +32,22 @@ android {
3232 testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
3333 }
3434
35+ signingConfigs {
36+ if (System.getenv("RELEASE_KEYSTORE_FILE") != null) {
37+ create("release") {
38+ storeFile = file(System.getenv("RELEASE_KEYSTORE_FILE"))
39+ storePassword = System.getenv("RELEASE_KEYSTORE_PASSWORD")
40+ keyAlias = System.getenv("RELEASE_KEY_ALIAS")
41+ keyPassword = System.getenv("RELEASE_KEY_PASSWORD")
42+ }
43+ }
44+ }
45+
3546 buildTypes {
3647 release {
3748 isMinifyEnabled = true
3849 isShrinkResources = true
39− signingConfig = signingConfigs.getByName("debug")
50+ signingConfig = signingConfigs.findByName("release") ?: signingConfigs.getByName("debug")
4051 proguardFiles(
4152 getDefaultProguardFile("proguard-android-optimize.txt"),
4253 "proguard-rules.pro"