ci: build the server image after the Android lane, not alongside it
CI & Build / Build now, or wait for Android? (push) Successful in 3s
CI & Build / Python lint (push) Successful in 2s
CI & Build / TypeScript typecheck (push) Successful in 8s
CI & Build / Build & push image (push) Skipped
CI & Build / Python tests (push) Successful in 10s
Android / Kotlin + Rust (APK) (push) Successful in 7m19s
CI & Build / Build now, or wait for Android? (push) Successful in 3s
CI & Build / Python lint (push) Successful in 2s
CI & Build / TypeScript typecheck (push) Successful in 8s
CI & Build / Build & push image (push) Skipped
CI & Build / Python tests (push) Successful in 10s
Android / Kotlin + Rust (APK) (push) Successful in 7m19s
Baking the newest client into every image left two holes, both raised by the operator. **An Android-only push never rebuilt the image.** `ci.yml` does not trigger on `android/**`, so a new APK could be published and no image would ever pick it up until some unrelated server change came along. **A push touching both raced.** Both workflows start at once; the image build would fetch the PREVIOUS client and there would be no second build to correct it — `:<sha>` is the immutable rollback unit (rule 46), so rebuilding it with different content would make it neither immutable nor a rollback unit. Ordering now runs the other way: the Android lane finishes, then calls the image build. `ci.yml` gains a `gate` job that stands down on any push touching the Android app, and `android.yml` dispatches `ci.yml` when it is done. One image per commit, containing the client from that commit. Cases: - **server only** — ci builds immediately; the newest published client is already the right one. - **Android only** — ci does not trigger at all; the Android lane dispatches it afterwards. - **both** — ci's push run stands down, the Android lane dispatches it. Exactly one image. - **tag** — always builds. The Android lane does not run on tags, so waiting for a call that never comes would mean a release tag with no image. The dispatch is `always()`, so a FAILED Android build still lets the server image through with the previous client. The alternative is a broken Android lane silently blocking server delivery, which is a worse failure than a slightly old APK. Two details that would each have made this quietly wrong: The gate diffs the whole PUSHED RANGE (`event.before..HEAD`, full fetch), not `HEAD^..HEAD`. A three-commit push whose Android change sat in the first would otherwise have looked Android-free and raced anyway — silently, which is the worst version of this bug. The dispatch is `curl -fsS`, not `|| true`. If that call ever stops working the symptom is server images silently never being built for Android pushes, which nobody would notice until wondering why the app stopped updating. The gate's path list has to match android.yml's trigger, and two places holding one decision is the recurring failure in this repo (issues 2181-2183). It is a `git diff` rather than a config precisely so the decision is visible in the log, and both sides carry a comment pointing at the other.
This commit is contained in:
@@ -52,6 +52,11 @@ jobs:
|
||||
# Android ABIs + cargo-ndk + SDK/NDK + JDK 25 + ktlint + detekt.
|
||||
image: git.fabledsword.com/bvandeusen/ci-rust-android:1.97
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
# For the dispatch at the end: this lane starts the server image build.
|
||||
actions: write
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: android
|
||||
@@ -222,3 +227,31 @@ jobs:
|
||||
name: thoughtsync-android-${{ steps.build.outputs.label }}-${{ github.sha }}
|
||||
path: ${{ steps.build.outputs.apk }}
|
||||
if-no-files-found: error
|
||||
|
||||
# The server image bakes in whatever client the dev release holds, so it has
|
||||
# to be built AFTER this lane, not alongside it. `ci.yml` stands down on any
|
||||
# push that touches the Android app (its `gate` job) and waits to be called
|
||||
# from here — that is the other half of this.
|
||||
#
|
||||
# `always()`: a FAILED Android build must still let the server image through.
|
||||
# There is no new client in that case, so it bakes in the previous one, which
|
||||
# is exactly right — the alternative is a broken Android lane silently
|
||||
# blocking server delivery.
|
||||
#
|
||||
# Not `if: success()` and not skipped on tags either: every ref that builds an
|
||||
# image needs the call, or nothing builds one at all.
|
||||
- name: Build the server image now the client is published
|
||||
if: always() && (github.ref == 'refs/heads/dev' || github.ref == 'refs/heads/main')
|
||||
working-directory: .
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
# Loud on failure rather than `|| true`: if this call stops working, the
|
||||
# symptom is server images silently never being built for Android pushes,
|
||||
# which is invisible until someone wonders why the app never updates.
|
||||
curl -fsS -X POST \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"ref":"${{ github.ref_name }}"}' \
|
||||
"${{ github.server_url }}/api/v1/repos/${{ github.repository }}/actions/workflows/ci.yml/dispatches"
|
||||
echo "Dispatched ci.yml on ${{ github.ref_name }}."
|
||||
|
||||
Reference in New Issue
Block a user