Skip to content

ci: make release preparation workflow robust (#76) #56

ci: make release preparation workflow robust (#76)

ci: make release preparation workflow robust (#76) #56

Workflow file for this run

name: Build NexaNote Android APK
# Releasing (the pubspec is the single source of truth for the version):
# 1. Bump `version:` in app/pubspec.yaml (e.g. 1.0.1+3 — versionName+versionCode).
# 2. Commit, then tag the matching versionName: `git tag v1.0.1 && git push --tags`.
# A `vX.Y.Z` tag builds a release-mode APK, attaches it to the GitHub Release as
# a stable-named asset (NexaNote-Android.apk) so Obtainium can track updates, and
# titles the release "NexaNote vX.Y.Z". The job fails if the tag does not match
# the pubspec versionName, or if the APK is missing.
on:
push:
branches: ["main"]
tags: ["v*"]
workflow_dispatch:
jobs:
build-android:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout
uses: actions/checkout@v4
# On a vX.Y.Z tag the release version must match the app's versionName
# (the part of pubspec `version:` before the `+build` suffix). Fail fast
# so we never publish a release whose tag and in-app version disagree.
- name: Verify tag matches pubspec version
if: github.ref_type == 'tag'
run: |
set -euo pipefail
tag_version="${GITHUB_REF_NAME#v}"
pubspec_version="$(sed -n 's/^version:[[:space:]]*\([0-9][0-9.]*\).*/\1/p' app/pubspec.yaml | head -n1)"
echo "Tag versionName: ${tag_version}"
echo "pubspec versionName: ${pubspec_version}"
if [ "${tag_version}" != "${pubspec_version}" ]; then
echo "::error::Release tag/pubspec version mismatch — refusing to publish."
echo " current tag version: v${tag_version} (the tag you pushed)"
echo " current pubspec version: ${pubspec_version} (app/pubspec.yaml)"
echo "Expected next action: tag the commit on main where app/pubspec.yaml already reads ${tag_version}, or run the Prepare release workflow to bump app/pubspec.yaml to ${tag_version} first, then re-create tag v${tag_version}."
exit 1
fi
- name: Setup Java
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: "21"
- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
channel: stable
cache: true
- name: Install dependencies
run: |
cd app
flutter pub get
# `flutter analyze` and `flutter test` are intentionally non-blocking
# for the APK build job: an analyze warning or a flaky test must not
# prevent shipping a tagged release. The Python test workflow is the
# gating signal for backend changes; static analysis still runs here
# so its output is visible in the job log, just without `set -e`
# promoting a warning to a build failure.
- name: Analyze (non-blocking — see comment above)
run: |
cd app
flutter analyze || true
- name: Run tests (non-blocking — see comment above)
run: |
cd app
flutter test || true
- name: Build NexaNote APK
run: |
cd app
flutter build apk --release
# Publish a stable asset name (NexaNote-Android.apk) so Obtainium can
# match the APK across releases. A versioned copy is kept alongside it
# for the workflow artifact / human download. Fail loudly if the build
# did not actually produce an APK.
- name: Prepare APK asset
run: |
set -euo pipefail
src="app/build/app/outputs/flutter-apk/app-release.apk"
if [ ! -f "${src}" ]; then
echo "::error::Expected APK not found at ${src} — the release build did not produce an APK."
exit 1
fi
mkdir -p release
cp "${src}" "release/NexaNote-Android.apk"
if [ "${GITHUB_REF_TYPE}" = "tag" ]; then
cp "${src}" "release/NexaNote-Android-${GITHUB_REF_NAME}.apk"
else
cp "${src}" "release/NexaNote-Android-dev-${GITHUB_SHA::7}.apk"
fi
ls -l release
- name: Upload APK artifact
uses: actions/upload-artifact@v4
with:
name: NexaNote-Android
path: release/*.apk
if-no-files-found: error
# Attach only the stable-named asset to the GitHub Release so Obtainium
# always finds NexaNote-Android.apk. `fail_on_unmatched_files` turns a
# missing APK into a failed release instead of an empty one.
- name: Create GitHub Release and attach APK
if: github.ref_type == 'tag'
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ github.ref_name }}
name: NexaNote ${{ github.ref_name }}
files: release/NexaNote-Android.apk
fail_on_unmatched_files: true