66
77jobs :
88 build-1 :
9- name : Build 1 (signed)
9+ name : Build 1 (signed splits )
1010 runs-on : ubuntu-latest
1111 environment : production
1212 steps :
@@ -20,14 +20,12 @@ jobs:
2020 echo "Keystore file is empty — check the KEYSTORE_BASE64 secret."
2121 exit 1
2222 fi
23- ls -lh ${{ github.workspace }}/release.keystore
24- echo "Keystore decoded successfully."
2523
2624 - name : Build Docker image
2725 working-directory : ci
2826 run : docker build -t deku_rep_build_release .
2927
30- - name : Build unsigned APK
28+ - name : Build unsigned split APKs
3129 run : |
3230 docker run --rm \
3331 -v "$(pwd)":/project \
@@ -44,34 +42,43 @@ jobs:
4442 -Dkotlin.daemon.jvm.options="-Xmx512m,-Xss1m" \
4543 -Dkotlin.compiler.execution.strategy=in-process
4644
47- - name : Sign APK with apksigner
45+ - name : Sign split APKs with apksigner
4846 run : |
4947 BUILD_TOOLS=$(ls $ANDROID_HOME/build-tools | sort -V | tail -1)
50- $ANDROID_HOME/build-tools/$BUILD_TOOLS/apksigner sign \
51- --ks ${{ github.workspace }}/release.keystore \
52- --ks-type JKS \
53- --ks-key-alias "${{ secrets.KEY_ALIAS }}" \
54- --ks-pass pass:"${{ secrets.STORE_PASSWORD }}" \
55- --key-pass pass:"${{ secrets.KEY_PASSWORD }}" \
56- --out app/build/outputs/apk/release/app-release-signed.apk \
57- app/build/outputs/apk/release/app-release-unsigned.apk
48+ cd app/build/outputs/apk/release/
49+
50+ # Loop through each unsigned split and sign it uniquely
51+ for unsigned_apk in app-*-release-unsigned.apk; do
52+ abi=$(echo "$unsigned_apk" | sed 's/app-//;s/-release-unsigned.apk//')
53+ echo "Signing split for ABI: $abi"
54+
55+ $ANDROID_HOME/build-tools/$BUILD_TOOLS/apksigner sign \
56+ --ks ${{ github.workspace }}/release.keystore \
57+ --ks-type JKS \
58+ --ks-key-alias "${{ secrets.KEY_ALIAS }}" \
59+ --ks-pass pass:"${{ secrets.STORE_PASSWORD }}" \
60+ --key-pass pass:"${{ secrets.KEY_PASSWORD }}" \
61+ --out "app-${abi}-release-signed.apk" \
62+ "$unsigned_apk"
63+ done
5864
59- - name : Verify signature
65+ - name : Verify signatures
6066 run : |
6167 BUILD_TOOLS=$(ls $ANDROID_HOME/build-tools | sort -V | tail -1)
62- $ANDROID_HOME/build-tools/$BUILD_TOOLS/apksigner verify \
63- --verbose \
64- app/build/outputs/apk/release/app-release-signed.apk
68+ for signed_apk in app/build/outputs/apk/release/app-*-release-signed.apk; do
69+ echo "Verifying $signed_apk"
70+ $ANDROID_HOME/build-tools/$BUILD_TOOLS/apksigner verify --verbose "$signed_apk"
71+ done
6572
66- - name : Upload signed APK
73+ - name : Upload signed APKs
6774 uses : actions/upload-artifact@v4
6875 with :
6976 name : apk-build-1
70- path : app/build/outputs/apk/release/app-release-signed.apk
77+ path : app/build/outputs/apk/release/app-*- release-signed.apk
7178 retention-days : 1
7279
7380 build-2 :
74- name : Build 2 (signed)
81+ name : Build 2 (signed splits )
7582 runs-on : ubuntu-latest
7683 environment : production
7784 steps :
@@ -82,17 +89,15 @@ jobs:
8289 run : |
8390 echo "${{ secrets.KEYSTORE_BASE64 }}" | base64 --decode > ${{ github.workspace }}/release.keystore
8491 if [ ! -s ${{ github.workspace }}/release.keystore ]; then
85- echo "Keystore file is empty — check the KEYSTORE_BASE64 secret ."
92+ echo "Keystore file is empty."
8693 exit 1
8794 fi
88- ls -lh ${{ github.workspace }}/release.keystore
89- echo "Keystore decoded successfully."
9095
9196 - name : Build Docker image
9297 working-directory : ci
9398 run : docker build -t deku_rep_build_release .
9499
95- - name : Build unsigned APK
100+ - name : Build unsigned split APKs
96101 run : |
97102 docker run --rm \
98103 -v "$(pwd)":/project \
@@ -109,44 +114,48 @@ jobs:
109114 -Dkotlin.daemon.jvm.options="-Xmx512m,-Xss1m" \
110115 -Dkotlin.compiler.execution.strategy=in-process
111116
112- - name : Sign APK with apksigner
117+ - name : Sign split APKs with apksigner
113118 run : |
114119 BUILD_TOOLS=$(ls $ANDROID_HOME/build-tools | sort -V | tail -1)
115- $ANDROID_HOME/build-tools/$BUILD_TOOLS/apksigner sign \
116- --ks ${{ github.workspace }}/release.keystore \
117- --ks-type JKS \
118- --ks-key-alias "${{ secrets.KEY_ALIAS }}" \
119- --ks-pass pass:"${{ secrets.STORE_PASSWORD }}" \
120- --key-pass pass:"${{ secrets.KEY_PASSWORD }}" \
121- --out app/build/outputs/apk/release/app-release-signed.apk \
122- app/build/outputs/apk/release/app-release-unsigned.apk
120+ cd app/build/outputs/apk/release/
121+ for unsigned_apk in app-*-release-unsigned.apk; do
122+ abi=$(echo "$unsigned_apk" | sed 's/app-//;s/-release-unsigned.apk//')
123+ $ANDROID_HOME/build-tools/$BUILD_TOOLS/apksigner sign \
124+ --ks ${{ github.workspace }}/release.keystore \
125+ --ks-type JKS \
126+ --ks-key-alias "${{ secrets.KEY_ALIAS }}" \
127+ --ks-pass pass:"${{ secrets.STORE_PASSWORD }}" \
128+ --key-pass pass:"${{ secrets.KEY_PASSWORD }}" \
129+ --out "app-${abi}-release-signed.apk" \
130+ "$unsigned_apk"
131+ done
123132
124- - name : Verify signature
133+ - name : Verify signatures
125134 run : |
126135 BUILD_TOOLS=$(ls $ANDROID_HOME/build-tools | sort -V | tail -1)
127- $ANDROID_HOME /build-tools/$BUILD_TOOLS/apksigner verify \
128- -- verbose \
129- app/build/outputs/apk/release/app-release-signed.apk
136+ for signed_apk in app /build/outputs/apk/release/app-*-release-signed.apk; do
137+ $ANDROID_HOME/build-tools/$BUILD_TOOLS/apksigner verify -- verbose "$signed_apk"
138+ done
130139
131- - name : Upload signed APK
140+ - name : Upload signed APKs
132141 uses : actions/upload-artifact@v4
133142 with :
134143 name : apk-build-2
135- path : app/build/outputs/apk/release/app-release-signed.apk
144+ path : app/build/outputs/apk/release/app-*- release-signed.apk
136145 retention-days : 1
137146
138147 compare :
139148 name : Compare signed APKs
140149 runs-on : ubuntu-latest
141150 needs : [ build-1, build-2 ]
142151 steps :
143- - name : Download APK from build 1
152+ - name : Download APKs from build 1
144153 uses : actions/download-artifact@v4
145154 with :
146155 name : apk-build-1
147156 path : apk-build-1
148157
149- - name : Download APK from build 2
158+ - name : Download APKs from build 2
150159 uses : actions/download-artifact@v4
151160 with :
152161 name : apk-build-2
@@ -155,11 +164,13 @@ jobs:
155164 - name : Compare hashes
156165 id : compare
157166 run : |
158- SHA1=$(sha256sum apk-build-1/app-release-signed.apk | awk '{ print $1 }')
159- SHA2=$(sha256sum apk-build-2/app-release-signed.apk | awk '{ print $1 }')
160- echo "Build 1: $SHA1"
161- echo "Build 2: $SHA2"
162- if [ "$SHA1" = "$SHA2" ]; then
167+ cd apk-build-1 && sha256sum app-*-release-signed.apk > ../hashes1.txt && cd ..
168+ cd apk-build-2 && sha256sum app-*-release-signed.apk > ../hashes2.txt && cd ..
169+
170+ awk '{print $1}' hashes1.txt > clean_hashes1.txt
171+ awk '{print $1}' hashes2.txt > clean_hashes2.txt
172+
173+ if cmp -s clean_hashes1.txt clean_hashes2.txt; then
163174 echo "Reproducible build verified — hashes match."
164175 echo "reproducible=true" >> "$GITHUB_OUTPUT"
165176 else
@@ -176,11 +187,7 @@ jobs:
176187 - name : Run diffoscope
177188 if : steps.compare.outputs.reproducible == 'false'
178189 run : |
179- diffoscope \
180- --text diffoscope-report.txt \
181- --html diffoscope-report.html \
182- apk-build-1/app-release-signed.apk \
183- apk-build-2/app-release-signed.apk || true
190+ diffoscope --text diffoscope-report.txt --html diffoscope-report.html apk-build-1/ apk-build-2/ || true
184191
185192 - name : Upload diffoscope report
186193 if : steps.compare.outputs.reproducible == 'false'
@@ -195,7 +202,6 @@ jobs:
195202 - name : Fail if not reproducible
196203 if : steps.compare.outputs.reproducible == 'false'
197204 run : |
198- echo "See the diffoscope-report artifact for a full breakdown of differences."
199205 exit 1
200206
201207 tag-and-release :
@@ -220,57 +226,50 @@ jobs:
220226 exit 1
221227 fi
222228 VERSION_NAME=$(grep "^versionName=" version.properties | cut -d'=' -f2)
223- if [ -z "$VERSION_NAME" ]; then
224- echo "ERROR: versionName missing from version.properties"
225- exit 1
226- fi
229+ TAG_VERSION=$(grep "^tagVersion=" version.properties | cut -d'=' -f2)
227230 echo "version_name=$VERSION_NAME" >> "$GITHUB_OUTPUT"
228- echo "versionName=$VERSION_NAME"
229-
230- - name : Compute next tag
231- id : next_tag
232- run : |
233- LATEST=$(git tag --sort=-v:refname | head -1)
234- if [ -z "$LATEST" ]; then
235- LATEST="0"
236- fi
237- echo "Latest tag: $LATEST"
238- NEW_TAG=$((LATEST + 1))
239- echo "New tag: $NEW_TAG"
240- echo "new_tag=$NEW_TAG" >> "$GITHUB_OUTPUT"
231+ echo "tag_version=$TAG_VERSION" >> "$GITHUB_OUTPUT"
241232
242233 - name : Create and push tag
243234 run : |
244235 git config user.name "github-actions[bot]"
245236 git config user.email "github-actions[bot]@users.noreply.github.com"
246- git tag "${{ steps.next_tag.outputs.new_tag }}" -m "Release ${{ steps.version.outputs.version_name }} — reproducible build verified"
247- git push origin " ${{ steps.next_tag .outputs.new_tag }}"
248- echo "Tagged ${{ steps.next_tag .outputs.new_tag }} and pushed. "
237+ # Tags using the clean tagVersion tracking integer
238+ git tag "${{ steps.version.outputs.tag_version }}" -m "Release ${{ steps.version .outputs.version_name }} — reproducible splits verified "
239+ git push origin " ${{ steps.version .outputs.tag_version }}"
249240
250- - name : Download signed APK from build-1
241+ - name : Download signed APKs from build-1
251242 uses : actions/download-artifact@v4
252243 with :
253244 name : apk-build-1
254245 path : apk-release
255246
256- - name : Rename APK with version name
247+ - name : Rename split APKs with version and ABI info
257248 run : |
258- mv apk-release/app-release-signed.apk \
259- "apk-release/${{ steps.version.outputs.version_name }}.apk"
249+ cd apk-release
250+ # Example: transforms app-arm64-v8a-release-signed.apk -> [AppName]-0.72.0-arm64-v8a.apk
251+ for signed_apk in app-*-release-signed.apk; do
252+ abi=$(echo "$signed_apk" | sed 's/app-//;s/-release-signed.apk//')
253+ mv "$signed_apk" "${{ steps.version.outputs.version_name }}-${abi}.apk"
254+ done
260255
261256 - name : Create GitHub release
262257 env :
263258 GH_TOKEN : ${{ secrets.GH_LIB_SYNC_TOKEN }}
264259 run : |
265- gh release create "${{ steps.next_tag.outputs.new_tag }}" \
266- "apk-release/${{ steps.version.outputs.version_name }}.apk" \
260+ cd apk-release
261+
262+ # Build a release notes string displaying hashes for all 3 versions
263+ NOTE_HASHES=$(sha256sum *.apk | awk '{print "**" $2 ":** " $1 "\n"}')
264+
265+ gh release create "${{ steps.version.outputs.tag_version }}" \
266+ *.apk \
267267 --title "${{ steps.version.outputs.version_name }}" \
268- --notes "Reproducible build verified
268+ --notes "Reproducible split builds verified
269269
270270 **Release:** ${{ steps.version.outputs.version_name }}
271- **Build No:** ${{ steps.next_tag.outputs.new_tag }}
272- **sha256sum:** $(sha256sum "apk-release/${{ steps.version.outputs.version_name }}.apk" | awk '{print $1}')
271+ **Build No:** ${{ steps.version.outputs.tag_version }}
273272
274- **changelogs :** " \
275- --fail-on-no-commits \
276- --verify-tag
273+ **SHA-256 Checksums :**
274+ $NOTE_HASHES" \
275+ --fail-on-no-commits
0 commit comments