The sheet still described the pre-M8 world: "two CI images: ci-go + ci-flutter", a ci-flutter dep list, and cross-workflow release polling against flutter.yml. None of that is true now — flutter.yml is gone, android.yml and release.yml both pull ci-android:36, and image-release gates on `needs: [android-release]` instead of polling. Family rule 39 makes this sheet CI-Runner's decision input for "add a dep to an image vs. fork a variant", so a stale sheet quietly misinforms that call: CI-Runner was still carrying ci-flutter for a consumer that no longer exists, and had no record of ci-android's real consumer. - Runtime images: ci-flutter:3.44 -> ci-android:36, with a note on why ci-flutter is now unconsumed and what would have to change to revive it. - Image deps: replace the Flutter/Dart/NDK list with the actual ci-android surface (JDK 25 + Gradle 9.1 floor, SDK/build-tools 36, no NDK, ktlint + detekt). - Label/image split: record that Android jobs still schedule on the flutter-ci label on purpose — it's a scheduling handle, not a toolchain assertion. - Update channel: `needs:` gating, plus the non-tag rebundle path. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
8.4 KiB
CI Requirements — Minstrel
Copy of the template. Family-wide policy:
ci-runners.mdin 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'simage-releasejob).ci-android:36— native Kotlin/Compose client: ktlint + detekt + unit tests + debug APK (.gitea/workflows/android.yml), and the signed release APK (release.yml'sandroid-releasejob).
ci-flutter is no longer consumed. The M8 rewrite replaced the Flutter
client with the native Android app and flutter.yml was removed; ci-android
took its place. flutter_client/ is still in the tree but nothing builds it.
CI-Runner still publishes ci-flutter and will retire it once that directory
goes — so if the Flutter client is ever revived, say so there first.
Image deps used
From ci-go:1.26
- Go (1.26 toolchain) —
go vet,go test -race,go build,go mod. - Node + npm —
npm ciandnpm test/npm run checkintest-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.ymlintegration 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 setJAVA_TOOL_OPTIONS=--enable-native-access=ALL-UNNAMEDto 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 ktlintCheckand./gradlew detektinandroid.yml. Image pins trackandroid/gradle/libs.versions.tomlso local and CI checks agree. - git —
actions/checkout@v4baseline (and any shell git operations). - base64 + curl — keystore decode + release-asset upload in
release.yml'sandroid-releasejob.
Per-job tool installs
None.
Notes
-
Label/image split. Workflows keep
runs-on: go-ci/runs-on: flutter-cias the scheduling label per theci-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 onflutter-ciwhile pullingci-android:36. Switch them toandroid-ciif 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'sminstrel-postgres-*containers are explicitly skipped as belt-and-suspenders. Depends onact_runner.valid_volumeswhitelisting the socket; if that ever stops auto-mounting, integration tests fail at thedocker inspectstep. -
Go toolchain pin.
go.modis ongo 1.25.0becausegolang.org/x/crypto v0.51.0declares 1.25 as its minimum.ci-go:1.26satisfies this with headroom. Futurex/cryptobumps 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'simage-releasejob declaresneeds: [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 againstflutter.ymlis gone with that workflow.) On non-tagmainpushesandroid-releaseis skipped andimage-releaseinstead pulls the most recent release's APK and reconstructs its exactversionName, so:latestnever ships without an update channel. It degrades to an emptyclient/— never a wrong version — if no release, asset, or tag commit-count can be resolved. -
Cache server reachability.
test-web.ymldoes NOT usecache: 'npm'onactions/setup-node— the Gitea Actions cache server isn't reachable from this runner's container network andsetup-nodewas burning ~4m41s on ETIMEDOUT before failing open. With the migration toci-go:1.26,setup-nodeis 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.uses: https://git.fabledsword.com/bvandeusen/upload-artifact@cb8afe72b42edc798abfb8fcb556cf660d894245 uses: https://git.fabledsword.com/bvandeusen/download-artifact@8d4e9521a5f7e5f8b6351f341f719f9f45a92a3aUpstream's
@v4+cannot work against this instance and no server-side change will help:isGhes()rejects any hostname that isn'tgithub.com/*.ghe.com/*.localhostand throws before it opens a connection, so the server is never asked what it supports.@v3is 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 — uploadv5bundles@actions/artifact^4.0.0 while downloadv5bundles ^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 becauserelease.ymlis a producer/consumer pair —android-releaseuploadsminstrel-apk,image-releasedownloads 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/artifactruntime 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@v3and 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: node24and 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: errorrather than the defaultwarn, 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}/artifactsfor the id (global run id, not the repo-scoped run number), then…/actions/artifacts/{id}/zip. The workstation has nounzip— usepython3 -m zipfile -e. -
Friction asks. None pending. The two images cover everything Minstrel needs.