1- name : Build and Release APK
1+ name : Build & Release Android (Signed AAB + APK)
22
33on :
44 push :
55 tags :
66 - ' v*'
77 workflow_dispatch :
8+ inputs :
9+ version_name :
10+ description : ' versionName (e.g. 1.0.0). Defaults to tag name without leading v.'
11+ required : false
12+ default : ' '
13+ publish_to_play :
14+ description : ' Upload AAB to Play Console internal track?'
15+ required : false
16+ default : ' false'
817
918jobs :
1019 build :
1120 runs-on : ubuntu-latest
1221 permissions :
1322 contents : write
14-
23+
1524 defaults :
1625 run :
1726 working-directory : web
18-
27+
1928 steps :
2029 - name : Checkout code
2130 uses : actions/checkout@v4
2736 cache : ' npm'
2837 cache-dependency-path : web/package-lock.json
2938
30- - name : Setup Java
39+ - name : Setup Java 21
3140 uses : actions/setup-java@v4
3241 with :
3342 distribution : ' temurin'
@@ -36,55 +45,107 @@ jobs:
3645 - name : Setup Android SDK
3746 uses : android-actions/setup-android@v3
3847
48+ - name : Compute version
49+ id : version
50+ run : |
51+ if [[ "${GITHUB_REF}" == refs/tags/* ]]; then
52+ RAW="${GITHUB_REF#refs/tags/}"
53+ else
54+ RAW="${{ github.event.inputs.version_name }}"
55+ fi
56+ # Strip a leading 'v' if present.
57+ VERSION_NAME="${RAW#v}"
58+ if [[ -z "$VERSION_NAME" ]]; then
59+ VERSION_NAME="0.0.0-${GITHUB_RUN_NUMBER}"
60+ fi
61+ # versionCode must be a monotonically increasing integer for Play.
62+ # Use the workflow run number as a safe baseline.
63+ VERSION_CODE="${GITHUB_RUN_NUMBER}"
64+ echo "version_name=${VERSION_NAME}" >> "$GITHUB_OUTPUT"
65+ echo "version_code=${VERSION_CODE}" >> "$GITHUB_OUTPUT"
66+ echo "Building versionName=${VERSION_NAME} versionCode=${VERSION_CODE}"
67+
3968 - name : Install dependencies
4069 run : npm ci
4170
4271 - name : Build web app
4372 run : npm run build
4473
45- - name : Remove docs from dist (if any)
74+ - name : Strip docs from dist (if any)
4675 run : rm -rf dist/docs 2>/dev/null || true
4776
4877 - name : Sync Capacitor
4978 run : npx cap sync android
5079
51- - name : Build Debug APK
80+ - name : Decode upload keystore
81+ env :
82+ KEYSTORE_BASE64 : ${{ secrets.ANDROID_KEYSTORE_BASE64 }}
83+ run : |
84+ if [[ -z "${KEYSTORE_BASE64}" ]]; then
85+ echo "::error::ANDROID_KEYSTORE_BASE64 secret is not set. See web/android/PLAY_STORE_RELEASE.md"
86+ exit 1
87+ fi
88+ echo "${KEYSTORE_BASE64}" | base64 -d > "${RUNNER_TEMP}/spendwise-upload.jks"
89+ echo "KEYSTORE_PATH=${RUNNER_TEMP}/spendwise-upload.jks" >> "$GITHUB_ENV"
90+
91+ - name : Build signed AAB and APK
92+ working-directory : web/android
93+ env :
94+ SPENDWISE_KEYSTORE_PATH : ${{ env.KEYSTORE_PATH }}
95+ SPENDWISE_KEYSTORE_PASSWORD : ${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
96+ SPENDWISE_KEY_ALIAS : ${{ secrets.ANDROID_KEY_ALIAS }}
97+ SPENDWISE_KEY_PASSWORD : ${{ secrets.ANDROID_KEY_PASSWORD }}
98+ SPENDWISE_VERSION_NAME : ${{ steps.version.outputs.version_name }}
99+ SPENDWISE_VERSION_CODE : ${{ steps.version.outputs.version_code }}
52100 run : |
53- cd android
54101 chmod +x gradlew
55- ./gradlew assembleDebug
102+ ./gradlew bundleRelease assembleRelease
56103
57- - name : Copy APK
58- run : cp android/app/build/outputs/apk/debug/app-debug.apk SpendWise-${{ github.ref_name }}.apk
104+ - name : Stage release artifacts
105+ run : |
106+ mkdir -p release-artifacts
107+ cp android/app/build/outputs/bundle/release/app-release.aab \
108+ release-artifacts/SpendWise-${{ steps.version.outputs.version_name }}.aab
109+ cp android/app/build/outputs/apk/release/app-release.apk \
110+ release-artifacts/SpendWise-${{ steps.version.outputs.version_name }}.apk
59111
60- - name : Upload APK artifact
112+ - name : Upload artifacts
61113 uses : actions/upload-artifact@v4
62114 with :
63- name : SpendWise-APK
64- path : SpendWise-*.apk
115+ name : SpendWise-Android-${{ steps.version.outputs.version_name }}
116+ path : web/release-artifacts/*
65117
66- - name : Create Release
118+ - name : Create GitHub Release
67119 if : startsWith(github.ref, 'refs/tags/')
68120 uses : softprops/action-gh-release@v1
69121 with :
70- files : SpendWise-*.apk
122+ files : |
123+ web/release-artifacts/SpendWise-${{ steps.version.outputs.version_name }}.aab
124+ web/release-artifacts/SpendWise-${{ steps.version.outputs.version_name }}.apk
71125 generate_release_notes : true
72126 name : SpendWise ${{ github.ref_name }}
73127 body : |
74- ## 📱 SpendWise Android App
75-
76- **Download and install the APK on your Android device.**
77-
78- ### Features:
79- - 💰 Track income & expenses
80- - 📊 Budget management
81- - 🎯 Savings goals
82- - 📈 Financial reports
83- - 🔔 Notifications & reminders
84-
85- ### Installation:
86- 1. Download the APK file
87- 2. Enable "Install from unknown sources" in Settings
88- 3. Open the APK file to install
128+ ## SpendWise Android (signed release)
129+
130+ Wraps the live web app at https://spendwisev2.vercel.app for Android.
131+
132+ - `*.aab` — upload to Google Play Console.
133+ - `*.apk` — sideload / direct install.
134+
135+ ### Install (APK):
136+ 1. Download the APK.
137+ 2. Enable "Install from unknown sources" for your browser.
138+ 3. Open the APK to install.
89139 env :
90140 GITHUB_TOKEN : ${{ secrets.GITHUB_TOKEN }}
141+
142+ - name : Upload to Play Console (internal track)
143+ if : ${{ startsWith(github.ref, 'refs/tags/') || github.event.inputs.publish_to_play == 'true' }}
144+ uses : r0adkll/upload-google-play@v1
145+ with :
146+ serviceAccountJsonPlainText : ${{ secrets.PLAY_SERVICE_ACCOUNT_JSON }}
147+ packageName : com.spendwise.app
148+ releaseFiles : web/release-artifacts/SpendWise-${{ steps.version.outputs.version_name }}.aab
149+ track : internal
150+ status : completed
151+ mappingFile : web/android/app/build/outputs/mapping/release/mapping.txt
0 commit comments