mirror of
https://github.com/kuhyx/screen-locker.git
synced 2026-11-10 20:27:13 +01:00
Some checks are pending
dependency freshness / dependencies on newest stable (push) Waiting to run
md-naming / markdown naming convention (push) Waiting to run
pre-commit / pre-commit (${{ matrix.group }}) (flutter, flutter-analyze, ) (push) Waiting to run
pre-commit / pre-commit (${{ matrix.group }}) (mypy, mypy, ) (push) Waiting to run
pre-commit / pre-commit (${{ matrix.group }}) (pylint, pylint, ) (push) Waiting to run
pre-commit / pre-commit (${{ matrix.group }}) (rest, , mypy,pylint,flutter-analyze) (push) Waiting to run
Tests / test (push) Waiting to run
Tests / lock-popup-safety (push) Waiting to run
Tests / flutter test (shard ${{ matrix.shard }}) (0) (push) Waiting to run
Tests / flutter test (shard ${{ matrix.shard }}) (1) (push) Waiting to run
Tests / flutter test (shard ${{ matrix.shard }}) (2) (push) Waiting to run
Tests / flutter test (shard ${{ matrix.shard }}) (3) (push) Waiting to run
Tests / flutter test (shard ${{ matrix.shard }}) (4) (push) Waiting to run
Tests / flutter test (shard ${{ matrix.shard }}) (5) (push) Waiting to run
Tests / flutter test (shard ${{ matrix.shard }}) (6) (push) Waiting to run
Tests / flutter test (shard ${{ matrix.shard }}) (7) (push) Waiting to run
Tests / flutter test (shard ${{ matrix.shard }}) (8) (push) Waiting to run
Tests / flutter test (shard ${{ matrix.shard }}) (9) (push) Waiting to run
Tests / flutter-coverage (push) Blocked by required conditions
Tests / web-coverage (push) Waiting to run
release-apk / release (APK) (push) Waiting to run
repo-contract / repo contract (push) Waiting to run
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
174 lines
7.0 KiB
YAML
174 lines
7.0 KiB
YAML
name: release-apk
|
|
|
|
on:
|
|
push:
|
|
branches: [main]
|
|
# A publisher, not a gate: build only when something that ends up in
|
|
# the APK changed. versionCode is the commit count, so skipped pushes
|
|
# still leave it monotonic.
|
|
paths:
|
|
- 'stronglift_replacement/workout_app/**'
|
|
- '.fvmrc'
|
|
- '.versioncode-offset'
|
|
- '.github/workflows/release-apk.yml'
|
|
# Lets a release be rebuilt from the current main without a dummy commit.
|
|
workflow_dispatch:
|
|
|
|
# gh release create needs write access to the repo's contents.
|
|
permissions:
|
|
contents: write
|
|
|
|
# Releases are tag-anchored; two concurrent runs would race on the same tag.
|
|
concurrency:
|
|
group: release-apk-${{ github.ref }}
|
|
cancel-in-progress: false
|
|
|
|
# NOTE: versionCode is the commit count (`git rev-list --count HEAD`), which
|
|
# only ever increases and tracks the code rather than the CI file. It replaced
|
|
# github.run_number on 2026-08-09: run_number counts workflow RUNS, so it
|
|
# resets to 1 if this file is renamed AND it can fall behind a build made
|
|
# outside this workflow — which happened, leaving CI unable to install over a
|
|
# manually sideloaded APK (INSTALL_FAILED_VERSION_DOWNGRADE). The checkout
|
|
# below therefore needs fetch-depth: 0; a shallow clone counts 1 commit.
|
|
#
|
|
# This job is NOT gated on a test suite: this repo has no Flutter analyze/test
|
|
# job to depend on, so a broken commit still publishes. The build failing is
|
|
# the only backstop. Add `needs:` here if a Flutter CI job is ever added.
|
|
jobs:
|
|
release:
|
|
name: release (APK)
|
|
runs-on: ubuntu-latest
|
|
defaults:
|
|
run:
|
|
working-directory: stronglift_replacement/workout_app
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
with:
|
|
# versionCode is the commit count; a shallow clone would report 1.
|
|
fetch-depth: 0
|
|
|
|
- name: Derive versionCode from the commit count
|
|
id: vc
|
|
run: echo "code=$(git rev-list --count HEAD)" >> "$GITHUB_OUTPUT"
|
|
|
|
- uses: actions/setup-java@v4
|
|
with:
|
|
distribution: temurin
|
|
java-version: "17"
|
|
# ~/.gradle caches + wrapper, keyed on the Gradle files.
|
|
cache: gradle
|
|
|
|
- uses: subosito/flutter-action@v2
|
|
with:
|
|
flutter-version: 3.47.7
|
|
channel: stable
|
|
cache: true
|
|
|
|
- name: Read version from pubspec
|
|
id: ver
|
|
run: |
|
|
set -euo pipefail
|
|
# 1.0.0+1 -> 1.0.0; the +build part is replaced by the run number.
|
|
raw=$(grep -m1 -E '^version:' pubspec.yaml | sed -E 's/^version:[[:space:]]*//')
|
|
semver=${raw%%+*}
|
|
if ! echo "$semver" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+$'; then
|
|
echo "::error::pubspec version '$raw' is not X.Y.Z"; exit 1
|
|
fi
|
|
echo "semver=$semver" >> "$GITHUB_OUTPUT"
|
|
|
|
# One step, no echoes, printf-into-file rather than echo: the key must
|
|
# never reach the log. storeFile is ABSOLUTE because key.properties is
|
|
# read from android/ while file() resolves against android/app/.
|
|
- name: Materialise the release keystore
|
|
env:
|
|
KEYSTORE_B64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }}
|
|
STORE_PASS: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
|
|
KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }}
|
|
KEY_PASS: ${{ secrets.ANDROID_KEY_PASSWORD }}
|
|
run: |
|
|
set -euo pipefail
|
|
if [ -z "$KEYSTORE_B64" ]; then
|
|
echo "::error::ANDROID_KEYSTORE_BASE64 is not set; refusing to publish."
|
|
exit 1
|
|
fi
|
|
umask 077
|
|
ks="$GITHUB_WORKSPACE/stronglift_replacement/workout_app/android/app/kuhy-release.jks"
|
|
printf '%s' "$KEYSTORE_B64" | base64 -d > "$ks"
|
|
{
|
|
printf 'storeFile=%s\n' "$ks"
|
|
printf 'storePassword=%s\n' "$STORE_PASS"
|
|
printf 'keyAlias=%s\n' "$KEY_ALIAS"
|
|
printf 'keyPassword=%s\n' "$KEY_PASS"
|
|
} > "$GITHUB_WORKSPACE/stronglift_replacement/workout_app/android/key.properties"
|
|
ls -l "$ks"
|
|
|
|
- run: flutter pub get
|
|
|
|
- name: Build signed APK
|
|
run: |
|
|
flutter build apk --release --flavor daily \
|
|
--build-name="${{ steps.ver.outputs.semver }}" \
|
|
--build-number="${{ steps.vc.outputs.code }}"
|
|
|
|
# Fail closed. `findByName("release") ?: debug` silently falls back to
|
|
# the debug key if key.properties is misplaced, producing a green build
|
|
# that can never be installed over. This is the check that catches it.
|
|
- name: Verify the APK is release-signed
|
|
run: |
|
|
set -euo pipefail
|
|
apk=build/app/outputs/flutter-apk/app-daily-release.apk
|
|
signer=$(ls -d "$ANDROID_SDK_ROOT"/build-tools/* | sort -V | tail -1)/apksigner
|
|
"$signer" verify --print-certs "$apk" | tee /tmp/certs.txt
|
|
if grep -qi 'CN=Android Debug' /tmp/certs.txt; then
|
|
echo "::error::APK is debug-signed; not publishing."; exit 1
|
|
fi
|
|
if ! grep -q 'CN=kuhy' /tmp/certs.txt; then
|
|
echo "::error::APK is not signed with the kuhy release key."; exit 1
|
|
fi
|
|
|
|
- name: Shred the signing material
|
|
if: always()
|
|
run: |
|
|
rm -f "$GITHUB_WORKSPACE/stronglift_replacement/workout_app/android/key.properties" \
|
|
"$GITHUB_WORKSPACE/stronglift_replacement/workout_app/android/app/kuhy-release.jks"
|
|
|
|
- name: Name the asset
|
|
id: art
|
|
run: |
|
|
set -euo pipefail
|
|
out="workout-app-${{ steps.ver.outputs.semver }}-build.${{ steps.vc.outputs.code }}.apk"
|
|
cp build/app/outputs/flutter-apk/app-daily-release.apk "$out"
|
|
echo "path=$out" >> "$GITHUB_OUTPUT"
|
|
|
|
- name: Publish the per-commit prerelease
|
|
env:
|
|
GH_TOKEN: ${{ github.token }}
|
|
run: |
|
|
set -euo pipefail
|
|
tag="v${{ steps.ver.outputs.semver }}-build.${{ steps.vc.outputs.code }}"
|
|
gh release create "$tag" "${{ steps.art.outputs.path }}" \
|
|
--prerelease \
|
|
--target "${{ github.sha }}" \
|
|
--title "$tag" \
|
|
--notes "Automated build of ${{ github.sha }} (versionCode ${{ steps.vc.outputs.code }})."
|
|
|
|
# Cut a clean vX.Y.Z the first time a pubspec version is seen. A failing
|
|
# `gh release view` is the "not published yet" signal, which also covers
|
|
# the first-ever run on a repo with no releases.
|
|
- name: Publish the clean version release
|
|
env:
|
|
GH_TOKEN: ${{ github.token }}
|
|
run: |
|
|
set -euo pipefail
|
|
tag="v${{ steps.ver.outputs.semver }}"
|
|
if gh release view "$tag" >/dev/null 2>&1; then
|
|
echo "$tag already exists; leaving it as-is."; exit 0
|
|
fi
|
|
if git rev-parse -q --verify "refs/tags/$tag" >/dev/null; then
|
|
echo "Tag $tag exists without a release; refusing to clobber."; exit 0
|
|
fi
|
|
gh release create "$tag" "${{ steps.art.outputs.path }}" \
|
|
--target "${{ github.sha }}" \
|
|
--title "$tag" \
|
|
--notes "Release $tag (versionCode ${{ steps.vc.outputs.code }})."
|