Closer/.github/workflows/android-ci.yml

162 lines
5.3 KiB
YAML

# Android CI — JVM unit tests + Android Lint + R8 release-build gate + static
# repo scans + secret scan.
#
# Real Firebase/RC secrets are intentionally ABSENT: the debug build compiles with
# an empty APP_CHECK_DEBUG_TOKEN and a placeholder RC_API_KEY (see
# app/build.gradle.kts `secret()` resolution). CI-built APKs therefore cannot pass
# App Check once enforcement is on — expected.
#
# The `release-build` job DOES exercise the RELEASE variant (minify+shrink+sign)
# using a THROWAWAY keystore + a dummy RC_API_KEY, purely to satisfy the release
# guards; that AAB is never distributed. A green release-build proves the R8
# toolchain, not that reflectively-loaded classes survive shrinking at runtime.
#
# app/google-services.json is gitignored (live Firebase config), but the
# google-services Gradle plugin hard-fails without one, so CI copies the dummy
# stub from .github/ci/google-services.json before any Gradle task.
name: Android CI
on:
push:
branches: [main, dev]
pull_request:
jobs:
unit-tests:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: "17"
- uses: gradle/actions/setup-gradle@v4
- name: Provide stub google-services.json (real one is gitignored)
run: |
if [ ! -f app/google-services.json ]; then
cp .github/ci/google-services.json app/google-services.json
fi
- name: Unit tests
run: ./gradlew testDebugUnitTest
- name: Upload test reports on failure
if: failure()
uses: actions/upload-artifact@v4
with:
name: unit-test-reports
path: app/build/reports/tests/
android-lint:
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: "17"
- uses: gradle/actions/setup-gradle@v4
- name: Provide stub google-services.json (real one is gitignored)
run: |
if [ ! -f app/google-services.json ]; then
cp .github/ci/google-services.json app/google-services.json
fi
# Fails on error-severity issues (NewApi, correctness). This gate caught two
# LocalDate.ofInstant NewApi crashes (API 34 call, minSdk 26) on 2026-07-11
# that emulator QA missed because the fixture emulators run API 34+.
- name: Android Lint (debug)
run: ./gradlew :app:lintDebug
- name: Upload lint report on failure
if: failure()
uses: actions/upload-artifact@v4
with:
name: lint-report
path: app/build/reports/lint-results-debug.html
release-build:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: "17"
- uses: gradle/actions/setup-gradle@v4
- name: Provide stub google-services.json (real one is gitignored)
run: |
if [ ! -f app/google-services.json ]; then
cp .github/ci/google-services.json app/google-services.json
fi
# Throwaway upload key — only exists to satisfy the release signing guard.
# This AAB is never distributed.
- name: Generate throwaway CI signing keystore
run: |
keytool -genkeypair -v \
-keystore "$RUNNER_TEMP/ci-release.jks" \
-alias ci -keyalg RSA -keysize 2048 -validity 30 \
-storepass ci_ci_ci -keypass ci_ci_ci \
-dname "CN=CI, O=CI, C=US"
- name: Build minified release bundle (R8 gate)
env:
RC_API_KEY: ci_dummy_rc_key_not_real
RELEASE_STORE_FILE: ${{ runner.temp }}/ci-release.jks
RELEASE_STORE_PASSWORD: ci_ci_ci
RELEASE_KEY_ALIAS: ci
RELEASE_KEY_PASSWORD: ci_ci_ci
# Skip the Crashlytics mapping upload — it needs real Firebase creds the
# stub google-services.json does not have.
run: ./gradlew :app:bundleRelease -x uploadCrashlyticsMappingFileRelease
- name: Upload R8 mapping on failure
if: failure()
uses: actions/upload-artifact@v4
with:
name: r8-mapping
path: app/build/outputs/mapping/release/
repo-scans:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
# Static scanners that gate known regression classes (theme tokens,
# navigation wiring, IME flags, painter/XML drawable misuse).
- name: Scan scripts
run: |
bash scripts/theme-scan.sh
bash scripts/wiring-scan.sh
bash scripts/ime-scan.sh
bash scripts/painter-xml-scan.sh
gitleaks:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # full history so the scan covers every commit
- name: Run gitleaks
env:
GITLEAKS_VERSION: "8.21.2"
run: |
curl -sSfL "https://github.com/gitleaks/gitleaks/releases/download/v${GITLEAKS_VERSION}/gitleaks_${GITLEAKS_VERSION}_linux_x64.tar.gz" | tar -xz gitleaks
./gitleaks detect --source . --no-banner --redact