Skip to content

Commit 09da118

Browse files
committed
Add Android APK build workflows + Play Store release pipeline
- New 'Build Debug APK' workflow: no signing, runnable from Actions tab, downloads as artifact. APK wraps live https://spendwisev2.vercel.app via Capacitor. - Upgraded existing tag-triggered workflow to produce signed AAB+APK for Play Store. - Release signing config in app/build.gradle (env vars or keystore.properties). - Added usesCleartextTraffic to manifest to match Capacitor cleartext setting. - Docs: BUILD_APK.md (debug path) and PLAY_STORE_RELEASE.md (production path).
1 parent 472a5ad commit 09da118

7 files changed

Lines changed: 479 additions & 32 deletions

File tree

.github/workflows/build-apk.yml

Lines changed: 91 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,30 @@
1-
name: Build and Release APK
1+
name: Build & Release Android (Signed AAB + APK)
22

33
on:
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

918
jobs:
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
@@ -27,7 +36,7 @@ jobs:
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
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
name: Build Debug APK (no signing required)
2+
3+
# Produces an installable, debug-signed APK that wraps the live web app at
4+
# https://spendwisev2.vercel.app. No Play Console account, no keystore, no
5+
# secrets needed. Trigger from the Actions tab and download from "Artifacts".
6+
7+
on:
8+
workflow_dispatch:
9+
push:
10+
branches:
11+
- main
12+
paths:
13+
- 'web/**'
14+
- '.github/workflows/build-debug-apk.yml'
15+
16+
jobs:
17+
build:
18+
runs-on: ubuntu-latest
19+
permissions:
20+
contents: read
21+
22+
defaults:
23+
run:
24+
working-directory: web
25+
26+
steps:
27+
- name: Checkout code
28+
uses: actions/checkout@v4
29+
30+
- name: Setup Node.js
31+
uses: actions/setup-node@v4
32+
with:
33+
node-version: '22'
34+
cache: 'npm'
35+
cache-dependency-path: web/package-lock.json
36+
37+
- name: Setup Java 21
38+
uses: actions/setup-java@v4
39+
with:
40+
distribution: 'temurin'
41+
java-version: '21'
42+
43+
- name: Setup Android SDK
44+
uses: android-actions/setup-android@v3
45+
46+
- name: Install dependencies
47+
run: npm ci
48+
49+
- name: Build web app
50+
run: npm run build
51+
52+
- name: Strip docs from dist (if any)
53+
run: rm -rf dist/docs 2>/dev/null || true
54+
55+
- name: Sync Capacitor
56+
run: npx cap sync android
57+
58+
- name: Build debug APK
59+
working-directory: web/android
60+
run: |
61+
chmod +x gradlew
62+
./gradlew assembleDebug
63+
64+
- name: Stage APK
65+
run: |
66+
mkdir -p out
67+
cp android/app/build/outputs/apk/debug/app-debug.apk \
68+
out/SpendWise-debug-${GITHUB_SHA::7}.apk
69+
70+
- name: Upload APK artifact
71+
uses: actions/upload-artifact@v4
72+
with:
73+
name: SpendWise-debug-apk
74+
path: web/out/*.apk
75+
if-no-files-found: error

web/android/.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,3 +99,8 @@ app/src/main/assets/public
9999
app/src/main/assets/capacitor.config.json
100100
app/src/main/assets/capacitor.plugins.json
101101
app/src/main/res/xml/config.xml
102+
103+
# Release signing — never commit
104+
app/keystore.properties
105+
app/*.jks
106+
app/*.keystore

web/android/BUILD_APK.md

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# Build a SpendWise APK (no Play Store needed)
2+
3+
The APK is a thin Capacitor wrapper around the live web app at
4+
`https://spendwisev2.vercel.app`. The web code is **not** copied into the APK.
5+
Whatever is live on Vercel is what the app shows.
6+
7+
## Easiest path: GitHub Actions (recommended)
8+
9+
You don't need Java, Android SDK, or anything else installed locally.
10+
11+
1. Push these changes to GitHub (any branch).
12+
2. Go to the repo on github.com → **Actions** tab.
13+
3. Pick **Build Debug APK (no signing required)** in the left sidebar.
14+
4. Click **Run workflow****Run workflow**.
15+
5. Wait ~5 minutes for the run to finish (green checkmark).
16+
6. Click into the run → scroll to **Artifacts** at the bottom →
17+
download `SpendWise-debug-apk`.
18+
7. Unzip → you get `SpendWise-debug-<sha>.apk`.
19+
20+
### Install on your phone
21+
22+
1. Transfer the APK to the phone (USB, Drive, email, whatever).
23+
2. Open it on the phone. Android will prompt to enable
24+
"Install from unknown sources" for the app you opened it from — accept.
25+
3. Tap install. Done.
26+
27+
The APK is **debug-signed**, which means:
28+
29+
- ✅ Installs and runs perfectly on any Android device.
30+
- ✅ Pulls live updates from Vercel automatically — no rebuild needed when you
31+
update the web app.
32+
- ❌ Cannot be uploaded to the Play Store. (When you're ready for Play Store,
33+
see `PLAY_STORE_RELEASE.md` in this folder.)
34+
35+
## Local build (only if you want to)
36+
37+
Requires JDK 17+ and Android SDK installed.
38+
39+
```cmd
40+
cd web
41+
npm ci
42+
npm run build
43+
npx cap sync android
44+
cd android
45+
gradlew.bat assembleDebug
46+
```
47+
48+
Output: `web\android\app\build\outputs\apk\debug\app-debug.apk`

0 commit comments

Comments
 (0)