Files
thoughtsync/.forgejo/workflows
bvandeusen 0cf77336d4
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 the server image after the Android lane, not alongside it
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.
2026-08-20 21:46:05 -04:00
..