Files

125 lines
5.2 KiB
YAML

# Tag-driven release. Builds standalone rather than consuming ci.yml
# artifacts: a tag run has no upstream run to pull from, and a release
# should be reproducible from the tagged tree alone.
name: android / release
on:
push:
tags: ['v*.*.*']
permissions:
contents: write
concurrency:
group: android-release-${{ github.ref }}
cancel-in-progress: false
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
with:
persist-credentials: false
# Full history + tags for the versionCode guard below.
fetch-depth: 0
# Play rejects an upload whose versionCode is not higher than the last
# one, but only after the whole build has run — fail fast instead.
# Compares this tag's versionCode against the previous v*.*.* tag,
# reading the wrapper-era path for tags that predate the native app.
- name: Guard against an unbumped versionCode
run: |
set -eu
current=$(grep -oP 'versionCode = \K\d+' android/app/build.gradle.kts)
echo "versionCode at ${GITHUB_REF_NAME}: $current"
prev_tag=$(git tag --list 'v*.*.*' --sort=-v:refname | grep -Fvx "${GITHUB_REF_NAME}" | head -n1 || true)
if [ -z "$prev_tag" ]; then
echo "No previous release tag; skipping comparison."
exit 0
fi
prev=$(git show "$prev_tag:android/app/build.gradle.kts" 2>/dev/null | grep -oP 'versionCode = \K\d+' || true)
if [ -z "$prev" ]; then
# Wrapper-era layout (Capacitor project under android/android/).
prev=$(git show "$prev_tag:android/android/app/build.gradle" 2>/dev/null | grep -oP 'versionCode \K\d+' || true)
fi
if [ -z "$prev" ]; then
echo "::warning::Could not read versionCode at $prev_tag; skipping comparison."
exit 0
fi
echo "versionCode at $prev_tag: $prev"
if [ "$current" -le "$prev" ]; then
echo "::error::versionCode $current at ${GITHUB_REF_NAME} must be greater than $prev at $prev_tag. Bump it in android/app/build.gradle.kts before tagging."
exit 1
fi
- uses: ./.github/actions/setup-android
# ci.yml does not run on tags, so this is the only gate before signing.
- name: Lint and test
run: ./gradlew :app:lint :app:test
working-directory: android
- name: Decode keystore
env:
KEYSTORE_BASE64: ${{ secrets.KEYSTORE_BASE64 }}
run: printf '%s' "$KEYSTORE_BASE64" | base64 --decode > "$GITHUB_WORKSPACE/keystore.p12"
- name: Build signed AAB + APK
working-directory: android
env:
LOTO_KEYSTORE_PATH: ${{ github.workspace }}/keystore.p12
LOTO_KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
LOTO_KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
LOTO_KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
run: ./gradlew :app:bundleRelease :app:assembleRelease
# L10: the decoded keystore has no real exposure on this ephemeral
# runner (nothing archives the workspace), but removing it explicitly
# closes the class of mistake where a future step does. if: always()
# so it runs even if the build step above fails.
- name: Remove decoded keystore
if: always()
run: rm -f "$GITHUB_WORKSPACE/keystore.p12"
- name: Upload to GitHub Release
uses: softprops/action-gh-release@3d0d9888cb7fd7b750713d6e236d1fcb99157228 # v3.0.2
with:
files: |
android/app/build/outputs/apk/release/*.apk
android/app/build/outputs/bundle/release/*.aab
# Auto-publish to Google Play (gated on the service-account secret).
# Skips silently if PLAY_SERVICE_ACCOUNT_JSON is not configured, so
# tagging a release before Play setup still produces a GH Release.
# Uploads to "alpha", the closed-testing track. Internal testing is a
# separate track and does not count toward the 12-tester requirement
# for production access, so releases have to land here to reach the
# testers. An unknown track name fails the step with the list of
# valid tracks rather than publishing somewhere unintended.
- name: Check Play Store config
id: play
env:
PLAY_SERVICE_ACCOUNT_JSON: ${{ secrets.PLAY_SERVICE_ACCOUNT_JSON }}
run: |
if [ -n "$PLAY_SERVICE_ACCOUNT_JSON" ]; then
echo "configured=true" >> "$GITHUB_OUTPUT"
else
echo "configured=false" >> "$GITHUB_OUTPUT"
echo "::notice::PLAY_SERVICE_ACCOUNT_JSON not set; skipping Play Store upload."
fi
- name: Upload to Google Play (closed testing track)
if: steps.play.outputs.configured == 'true'
uses: r0adkll/upload-google-play@e738b9dd8f2476ea806d921b64aacd24f34515a5 # v1.1.5
with:
serviceAccountJsonPlainText: ${{ secrets.PLAY_SERVICE_ACCOUNT_JSON }}
packageName: com.miti99.loto
releaseFiles: android/app/build/outputs/bundle/release/*.aab
tracks: alpha
status: completed
# versionCode is bumped manually in android/app/build.gradle.kts
# before tagging; the guard step above catches a forgotten bump.