android / Build + lint + test (push) Successful in 4m1s
Superseded by the M8 native Android rewrite. Last touched 2026-05-31, no workflow has built it since flutter.yml was removed, and rule #22 says a replaced path goes rather than lingering as something a reader has to work out the status of. 245 files, ~24.6k lines. Config references go with it: the .gitignore block (and its now-empty "# Flutter" header), the .dockerignore entry, and renovate's ignorePaths entry, which was suppressing dependency scanning for a directory that no longer exists. ci-requirements.md said ci-flutter "will retire once that directory goes". It has gone, so the doc now says so -- CI-Runner can drop the image, and nothing in this repo needs a Flutter toolchain. One thing is kept rather than deleted: shared/fabledsword.tokens.json. It lived under flutter_client/shared/ but was never Flutter's property -- it is the canonical statement of the palette, the only place the dark, light and flat cohorts are written down together, and FabledSwordTokens.kt names it as its source of truth. Losing it would have been collateral damage, so it moves to the repo root with a README saying what it is and that neither client generates from it. That comment in FabledSwordTokens.kt is repointed here. What deliberately does NOT change: `runs-on: flutter-ci` in android.yml and release.yml. That is a runner LABEL, not a path -- the Android jobs schedule on it while pulling ci-android:36, per the label/image split ci-requirements.md documents. Removing it would break scheduling for a cosmetic win, so the doc now spells that out beside the retirement note. Left for #2710: 64 files whose comments still name flutter_client/ paths. Sweeping them here would have buried the deletion, and each needs a judgement -- keep the substance and drop the dead path, delete pure "ported from" bookkeeping, or leave design rationale that happens to mention the Flutter build.
126 lines
8.6 KiB
Markdown
126 lines
8.6 KiB
Markdown
# CI Requirements — Minstrel
|
|
|
|
> Copy of [the template](https://git.fabledsword.com/bvandeusen/CI-runner/src/branch/main/docs/requirements-sheet.template.md).
|
|
> Family-wide policy: `ci-runners.md` in the FabledRulebook.
|
|
|
|
## Runtime images
|
|
|
|
Minstrel's four workflows consume two CI images:
|
|
|
|
```
|
|
git.fabledsword.com/bvandeusen/ci-go:1.26
|
|
git.fabledsword.com/bvandeusen/ci-android:36
|
|
```
|
|
|
|
- `ci-go:1.26` — Go server tests (`.gitea/workflows/test-go.yml`), web SPA tests (`.gitea/workflows/test-web.yml`), and the release container build (`release.yml`'s `image-release` job).
|
|
- `ci-android:36` — native Kotlin/Compose client: ktlint + detekt + unit tests + debug APK (`.gitea/workflows/android.yml`), and the signed release APK (`release.yml`'s `android-release` job).
|
|
|
|
**`ci-flutter` is no longer consumed, and `ci-flutter` can now be retired.**
|
|
The M8 rewrite replaced the Flutter client with the native Android app and
|
|
`flutter.yml` was removed; `ci-android` took its place. `flutter_client/`
|
|
itself was deleted on 2026-08-16, which was the condition CI-Runner was
|
|
waiting on before dropping the image — nothing in this repo needs a Flutter
|
|
toolchain any more.
|
|
|
|
Note this does **not** mean the `flutter-ci` runner *label* goes: the Android
|
|
jobs still schedule on it while pulling `ci-android:36`, per the label/image
|
|
split below. The label is a scheduling handle, not a toolchain assertion.
|
|
|
|
## Image deps used
|
|
|
|
### From `ci-go:1.26`
|
|
- **Go** (1.26 toolchain) — `go vet`, `go test -race`, `go build`, `go mod`.
|
|
- **Node + npm** — `npm ci` and `npm test` / `npm run check` in `test-web.yml`.
|
|
- **golangci-lint** — lint pass in `test-go.yml`.
|
|
- **docker CLI** — bridge-IP discovery of the per-job Postgres service container in `test-go.yml` integration job (via the runner's shared `/var/run/docker.sock`).
|
|
- **docker buildx** — release container build + push in `release.yml`.
|
|
- **curl** — release-asset polling / upload in `release.yml`.
|
|
|
|
### From `ci-android:36`
|
|
- **JDK 25** — Gradle launcher + Android build. Requires Gradle 9.1.0+ in
|
|
`android/gradle/wrapper`; older Gradle rejects JDK 25 with an opaque `"25.0.3"`
|
|
error. The workflows also set `JAVA_TOOL_OPTIONS=--enable-native-access=ALL-UNNAMED`
|
|
to silence Gradle's launcher-JVM restricted-method warning.
|
|
- **Android SDK + cmdline-tools + build-tools 36.0.0** — APK assembly + signing.
|
|
No NDK: the native client has no C/C++ sources (this is why it isn't on
|
|
`ci-flutter`).
|
|
- **ktlint + detekt** — `./gradlew ktlintCheck` and `./gradlew detekt` in
|
|
`android.yml`. Image pins track `android/gradle/libs.versions.toml` so local
|
|
and CI checks agree.
|
|
- **git** — `actions/checkout@v4` baseline (and any shell git operations).
|
|
- **base64 + curl** — keystore decode + release-asset upload in `release.yml`'s
|
|
`android-release` job.
|
|
|
|
## Per-job tool installs
|
|
|
|
None.
|
|
|
|
## Notes
|
|
|
|
- **Label/image split.** Workflows keep `runs-on: go-ci` / `runs-on: flutter-ci` as the scheduling label per the [`ci-runners.md`](https://…/FabledRulebook/ci-runners.md) "label = scheduling handle, image = `container.image`" pattern. The labels are intentional handles, not toolchain assertions — which is why the Android jobs still schedule on `flutter-ci` while pulling `ci-android:36`. Switch them to `android-ci` if that runner label is ever registered; nothing breaks either way.
|
|
- **Integration-job docker-socket dependency.** `test-go.yml`'s integration job uses the runner's shared docker socket (`/var/run/docker.sock`) to bridge-IP-discover the per-job Postgres service container by name + network intersection — the dev compose's `minstrel-postgres-*` containers are explicitly skipped as belt-and-suspenders. Depends on `act_runner.valid_volumes` whitelisting the socket; if that ever stops auto-mounting, integration tests fail at the `docker inspect` step.
|
|
- **Go toolchain pin.** `go.mod` is on `go 1.25.0` because `golang.org/x/crypto v0.51.0` declares 1.25 as its minimum. `ci-go:1.26` satisfies this with headroom. Future `x/crypto` bumps that move the Go floor should be paired with an image-tag bump in this file + the workflows.
|
|
- **In-app update channel — `needs:`, not polling.** `release.yml`'s `image-release` job declares `needs: [android-release]`, so on tag pushes the signed APK is guaranteed present before the image build starts — no polling window, no race. (The old cross-workflow polling against `flutter.yml` is gone with that workflow.) On non-tag `main` pushes `android-release` is skipped and `image-release` instead pulls the most recent release's APK and reconstructs its exact `versionName`, so `:latest` never ships without an update channel. It degrades to an empty `client/` — never a wrong version — if no release, asset, or tag commit-count can be resolved.
|
|
- **Cache server reachability.** `test-web.yml` does NOT use `cache: 'npm'` on `actions/setup-node` — the Gitea Actions cache server isn't reachable from this runner's container network and `setup-node` was burning ~4m41s on ETIMEDOUT before failing open. With the migration to `ci-go:1.26`, `setup-node` is removed entirely (Node is in the image). The cache concern reappears if a future change re-introduces a network-dependent action.
|
|
- **Artifacts — use the mirrored actions, never `actions/{upload,download}-artifact`.**
|
|
```yaml
|
|
uses: https://git.fabledsword.com/bvandeusen/upload-artifact@cb8afe72b42edc798abfb8fcb556cf660d894245
|
|
uses: https://git.fabledsword.com/bvandeusen/download-artifact@8d4e9521a5f7e5f8b6351f341f719f9f45a92a3a
|
|
```
|
|
Upstream's `@v4+` cannot work against this instance and no server-side change
|
|
will help: `isGhes()` rejects any hostname that isn't `github.com` /
|
|
`*.ghe.com` / `*.localhost` and throws before it opens a connection, so the
|
|
server is never asked what it supports. `@v3` is worse — it reports success,
|
|
and Gitea then serves artifacts back only through the v4 API
|
|
(`content_encoding = application/zip`), so a v3 upload is stored but invisible
|
|
to every retrieval path. A green job producing nothing retrievable; that is how
|
|
72 unreachable artifacts accumulated on this repo. Scribe issues 2255 / 2270.
|
|
|
|
Both are pull mirrors of the Forgejo project's forks
|
|
(`code.forgejo.org/forgejo/{upload,download}-artifact`, one commit on upstream
|
|
disabling that check), mirrored so CI depends on commits we hold and pinned by
|
|
SHA because the mirrors auto-sync every 8h — a moved upstream tag would
|
|
otherwise silently change what runs.
|
|
|
|
**Match the pins on `@actions/artifact`, not on the actions' own version
|
|
numbers.** The two actions release on unrelated cadences, so equal version
|
|
numbers do NOT mean a compatible pair — upload `v5` bundles `@actions/artifact`
|
|
^4.0.0 while download `v5` bundles ^2.3.2. The pins above are upload **v5** and
|
|
download **v6**, which is the pairing that puts ^4.0.0 on both sides. This
|
|
matters because `release.yml` is a producer/consumer pair — `android-release`
|
|
uploads `minstrel-apk`, `image-release` downloads it — and a protocol mismatch
|
|
across it yields an empty listing rather than an error, exactly the silent
|
|
failure this entry exists to prevent.
|
|
|
|
| tag | `@actions/artifact` | runtime |
|
|
|---|---|---|
|
|
| upload v4 | ^2.1.1 | node20 |
|
|
| **upload v5** ← pinned | **^4.0.0** | node20 |
|
|
| download v4 | ^2.1.1 | node20 |
|
|
| download v5 | ^2.3.2 | node20 |
|
|
| **download v6** ← pinned | **^4.0.0** | node20 |
|
|
| download v7 | ^5.0.0 | **node24** |
|
|
|
|
The only true protocol break in this history was **v3 → v4** (upstream:
|
|
"Downloading artifacts that were created from `actions/upload-artifact@v3` and
|
|
below are not supported"); v4-and-up are one family. Later majors are mostly
|
|
ergonomics and runtime — upload v4 forbids re-uploading a name and caps a job
|
|
at 500 artifacts; download v5 made by-ID extraction match by-name.
|
|
|
|
**Do not jump the download pin to v7.** That major is a runner requirement, not
|
|
a feature change: it moves to `runs.using: node24` and upstream states it
|
|
"requires a minimum Actions Runner version of 2.327.1 … if you are using
|
|
self-hosted runners, ensure they are updated before upgrading." act_runner is
|
|
not GitHub's runner and makes no such version claim, so node24 is unverified
|
|
here. Everything currently pinned is node20.
|
|
|
|
Upload steps set `if-no-files-found: error` rather than the default `warn`, so
|
|
an upload that matches nothing fails its own job instead of failing the
|
|
consumer later.
|
|
|
|
Retrieval: `GET /api/v1/repos/{owner}/{repo}/actions/runs/{run_id}/artifacts`
|
|
for the id (global run id, not the repo-scoped run number), then
|
|
`…/actions/artifacts/{id}/zip`. The workstation has no `unzip` — use
|
|
`python3 -m zipfile -e`.
|
|
- **Friction asks.** None pending. The two images cover everything Minstrel needs.
|