serene-pub/.github/workflows/build-android.yml

140 lines
5.7 KiB
YAML

name: Build Android APK
on:
push:
tags:
- "v*.*.*"
- "v*.*.*-dev"
- "v*.*.*-alpha"
- "v*.*.*-beta"
- "v*.*.*-pr-*"
- "v*.*.*-rc-*"
jobs:
build-android:
name: Build Android APK
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
# Mirrors release.yml's own prerelease detection exactly, so this
# workflow's APK lands as an asset on the same tagged release with
# a prerelease flag that never disagrees with the main build.
- name: Determine prerelease status
id: release_info
shell: bash
run: |
TAG="${{ github.ref_name }}"
VERSION="${TAG#v}"
if [[ "$VERSION" =~ ^([0-9]+\.[0-9]+\.[0-9]+)(-(.+))?$ ]]; then
SUFFIX="${BASH_REMATCH[3]}"
if [ -z "$SUFFIX" ] || [ "$SUFFIX" = "beta" ]; then
IS_PRERELEASE="false"
else
IS_PRERELEASE="true"
fi
else
IS_PRERELEASE="true"
fi
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "is_prerelease=$IS_PRERELEASE" >> $GITHUB_OUTPUT
echo "Version: $VERSION"
echo "Is Pre-release: $IS_PRERELEASE"
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: "24"
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
distribution: "temurin"
java-version: "17"
- name: Install dependencies
run: |
node -e "const fs = require('fs'); try { fs.unlinkSync('package-lock.json'); } catch (e) { /* ignore */ }"
for i in 1 2 3; do npm install --fetch-retries=5 --fetch-retry-mintimeout=20000 --fetch-retry-maxtimeout=120000 && break || sleep 15; done
- name: Prepare SvelteKit
run: npx @sveltejs/kit sync
- name: Build SvelteKit app
run: npm run build
- name: Prepare production dependencies
run: |
npm install --omit=dev
npm rebuild
- name: Prepare Android assets
run: node scripts/build-android.js
- name: Decode keystore
env:
KEYSTORE_BASE64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }}
run: |
if [ -n "$KEYSTORE_BASE64" ]; then
echo "$KEYSTORE_BASE64" | base64 -d > android/app/release.keystore
echo "Keystore decoded successfully"
else
echo "No keystore configured, building an unsigned APK"
fi
- name: Make gradlew executable
run: chmod +x android/gradlew
- name: Build Release APK
env:
KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }}
KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }}
working-directory: android
run: |
# Only set KEYSTORE_FILE if the keystore was actually decoded —
# build.gradle treats its mere presence as "sign this build", so
# an unconditionally-set env var would make it try to sign with a
# keystore that doesn't exist whenever the secrets aren't configured.
# Gradle's file() in app/build.gradle resolves relative to the
# app/ project directory, so this must be a bare filename, not a
# path relative to this step's android/ working directory.
if [ -f "app/release.keystore" ]; then
export KEYSTORE_FILE=release.keystore
fi
./gradlew assembleRelease
# Signed and unsigned release builds produce differently-named APKs
# (app-release.apk vs. app-release-unsigned.apk) — this always
# results in exactly one renamed file in dist/, regardless of which
# case occurred, so exactly one APK ever reaches the release.
- name: Rename APK
id: rename_apk
run: |
VERSION="${{ steps.release_info.outputs.version }}"
mkdir -p dist
SRC=""
if [ -f "android/app/build/outputs/apk/release/app-release.apk" ]; then
SRC="android/app/build/outputs/apk/release/app-release.apk"
elif [ -f "android/app/build/outputs/apk/release/app-release-unsigned.apk" ]; then
SRC="android/app/build/outputs/apk/release/app-release-unsigned.apk"
else
echo "Error: no release APK found in android/app/build/outputs/apk/release/"
ls -la android/app/build/outputs/apk/release/ || true
exit 1
fi
DEST="dist/serene-pub-${VERSION}-android.apk"
cp "$SRC" "$DEST"
echo "apk_path=$DEST" >> $GITHUB_OUTPUT
echo "Renamed $SRC -> $DEST"
- name: Upload to Release
uses: softprops/action-gh-release@v2
with:
files: ${{ steps.rename_apk.outputs.apk_path }}
prerelease: ${{ steps.release_info.outputs.is_prerelease }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}