1- # Build, sign and upload APK as a GitHub Release asset
21on :
32 push :
4- branches :
5- - ' ** '
3+ tags :
4+ - " v* "
65 workflow_dispatch :
76
87permissions :
98 contents : write
109
11- name : Build, Sign and Release APK
10+ name : Android Release
1211
1312jobs :
1413 build-and-release :
1514 runs-on : ubuntu-latest
16- env :
17- SIGNED_APK_DIR : signed-apks
18- RELEASE_TAG : apk-${{ github.sha }}
1915 steps :
20- - name : Checkout code
16+ - name : Checkout
2117 uses : actions/checkout@v4
2218 with :
2319 fetch-depth : 0
@@ -28,61 +24,60 @@ jobs:
2824 distribution : temurin
2925 java-version : ' 21'
3026
31- - name : Stop any existing Gradle daemons
32- run : ./gradlew --stop || true
33- - name : Cache Gradle
34- uses : actions/cache@v4
35- with :
36- path : |
37- ~/.gradle/caches
38- ~/.gradle/wrapper
39- key : gradle-${{ runner.os }}-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
40- restore-keys : |
41- gradle-${{ runner.os }}-
27+ - name : Set up Gradle
28+ uses : gradle/actions/setup-gradle@v4
4229
4330 - name : Decode keystore (if provided)
4431 run : |
4532 if [ -z "${{ secrets.KEYSTORE_BASE64 }}" ]; then
46- echo "KEYSTORE_BASE64 not set — skipping keystore decode (build will be unsigned)"
33+ echo "KEYSTORE_BASE64 not set — continuing without keystore (build will be unsigned)"
4734 exit 0
4835 fi
49- # create runner temp directory path and decode keystore there
36+ mkdir -p "$RUNNER_TEMP"
5037 echo "${{ secrets.KEYSTORE_BASE64 }}" | base64 --decode > "$RUNNER_TEMP/keystore.jks"
38+ chmod 600 "$RUNNER_TEMP/keystore.jks"
5139 echo "Keystore decoded to $RUNNER_TEMP/keystore.jks"
52- - name : Build release APK and AAB
40+
41+ - name : Debug: verify files before build
42+ run : |
43+ echo "JAVA_HOME=$JAVA_HOME"
44+ java -version
45+ ./gradlew -version
46+
47+ - name : Build release APK and AAB (signed if keystore present)
5348 env :
49+ ANDROID_KEYSTORE_PATH : $RUNNER_TEMP/keystore.jks
5450 ANDROID_KEYSTORE_PASSWORD : ${{ secrets.KEYSTORE_PASSWORD }}
5551 ANDROID_KEY_ALIAS : ${{ secrets.KEY_ALIAS }}
5652 ANDROID_KEY_PASSWORD : ${{ secrets.KEY_PASSWORD }}
5753 run : |
58- # If keystore was decoded, RUNNER_TEMP/keystore.jks exists. Use injected signing props so no keystore info is committed.
59- STORE_FILE="$RUNNER_TEMP/keystore.jks"
54+ STORE="$ANDROID_KEYSTORE_PATH"
6055 STORE_ARG=""
61- if [ -f "$STORE_FILE" ]; then
62- STORE_ARG="-Pandroid.injected.signing.store.file=$STORE_FILE -Pandroid.injected.signing.store.password=$ANDROID_KEYSTORE_PASSWORD -Pandroid.injected.signing.key.alias=$ANDROID_KEY_ALIAS -Pandroid.injected.signing.key.password=$ANDROID_KEY_PASSWORD"
63- echo "Signing with keystore at $STORE_FILE"
56+ if [ -f "$STORE" ]; then
57+ # Use Android's injected signing properties so you don't have to commit the keystore
58+ STORE_ARG="-Pandroid.injected.signing.store.file=$STORE -Pandroid.injected.signing.store.password=$ANDROID_KEYSTORE_PASSWORD -Pandroid.injected.signing.key.alias=$ANDROID_KEY_ALIAS -Pandroid.injected.signing.key.password=$ANDROID_KEY_PASSWORD"
59+ echo "Will sign using keystore at $STORE"
6460 else
65- echo "No keystore found at $STORE_FILE ; building unsigned artifacts"
61+ echo "No keystore found at $STORE ; building unsigned artifacts"
6662 fi
67- # Force Gradle to use the setup-java JDK on the runner in case project has local overrides
68- ./gradlew assembleRelease bundleRelease -Dorg.gradle.java.home="$JAVA_HOME" $STORE_ARG
69- - name : Remove keystore file if present
70- if : always()
63+
64+ # Force Gradle to use the runner JDK and do a clean build
65+ ./gradlew --no-daemon clean assembleRelease bundleRelease -Dorg.gradle.java.home="$JAVA_HOME" $STORE_ARG
66+
67+ - name : Show built artifacts (debug)
7168 run : |
72- if [ -f "$RUNNER_TEMP/keystore.jks" ]; then
73- shred -u "$RUNNER_TEMP/keystore.jks" || rm -f "$RUNNER_TEMP/keystore.jks"
74- echo "Keystore removed"
75- else
76- echo "No keystore to remove"
77- fi
69+ echo "APK files:"
70+ ls -al app/build/outputs/apk/release || true
71+ echo "AAB files:"
72+ ls -al app/build/outputs/bundle/release || true
7873
79- - name : Upload APK artifact
74+ - name : Upload APK artifact (Actions artifact - optional)
8075 uses : actions/upload-artifact@v4
8176 with :
8277 name : simple-data-entry-apk
8378 path : app/build/outputs/apk/release/*.apk
8479
85- - name : Upload AAB artifact
80+ - name : Upload AAB artifact (Actions artifact - optional)
8681 uses : actions/upload-artifact@v4
8782 with :
8883 name : simple-data-entry-aab
@@ -92,11 +87,23 @@ jobs:
9287 if : ${{ startsWith(github.ref, 'refs/tags/') }}
9388 uses : ncipollo/release-action@v1
9489 with :
95-
9690 tag : ${{ github.ref_name }}
9791 name : " Release ${{ github.ref_name }}"
9892 body : |
9993 Release created by workflow run ${{ github.run_id }} for commit ${{ github.sha }}.
10094 files : |
10195 app/build/outputs/apk/release/*.apk
10296 app/build/outputs/bundle/release/*.aab
97+
98+ env :
99+ GITHUB_TOKEN : ${{ github.token }}
100+
101+ - name : Remove decoded keystore (always)
102+ if : always()
103+ run : |
104+ if [ -f "$RUNNER_TEMP/keystore.jks" ]; then
105+ shred -u "$RUNNER_TEMP/keystore.jks" || rm -f "$RUNNER_TEMP/keystore.jks"
106+ echo "Keystore removed"
107+ else
108+ echo "No keystore to remove"
109+ fi
0 commit comments