Files
minstrel/ci-requirements.md
T
bvandeusenandClaude Opus 5.5 c96231106f docs: setup files and the client, shared and CI docs describe what is (#6277)
.dockerignore, .gitignore, deploy/initdb, ci-requirements.md,
client/README.md, shared/README.md and two web/scripts test names: dates,
issue/rule/spike ids and the stories behind each exclusion removed.

Corrected:
- client/README.md: the dev lane bundles its own APK; :main no longer
  exists; main bundles the recorded sidecar instead of re-deriving the
  version name. Its smoke-test step wrote a bare "v0.2.0", which the
  server rejects as a sidecar that is not JSON; it now writes the JSON
  shape, which the README documents.
- ci-requirements.md: one workflow, not four; the android lane builds no
  debug APK; main reads the recorded sidecar.
- .gitignore: CI fills client/ on every image build, not tag releases only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 13:04:02 -04:00

85 lines
6.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 one workflow, `.gitea/workflows/release.yml`, consumes two CI images:
```
git.fabledsword.com/bvandeusen/ci-go:1.26
git.fabledsword.com/bvandeusen/ci-android:36
```
- `ci-go:1.26` — the `go`, `integration` and `web` lanes, `release-assets`, and the container build (`image-release`). All CI lives in that one workflow so every publishing job can depend on every lane.
- `golang:1.26-bookworm` (Docker Hub) — the `govulncheck` lane. Deliberately the same image as the Dockerfile's builder stage rather than `ci-go`, so the standard library it checks is the one in the shipped binary. Keep the two in step.
- `ci-android:36` — native Kotlin/Compose client: ktlint + detekt + unit tests (the `android` lane), and the signed release APK (`android-release`).
**No job uses `ci-flutter`**; nothing in this repo needs a Flutter toolchain.
The `flutter-ci` runner *label* is still used: the Android jobs schedule on it
while pulling `ci-android:36`, per the label/image split below.
## 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`, `npm audit`, `npm test` and `npm run check` in the `web` lane.
- **golangci-lint** — lint pass in the `go` lane.
- **docker CLI** — bridge-IP discovery of the per-job Postgres service container in the `integration` lane (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 app has no C/C++ sources.
- **ktlint + detekt** — `./gradlew ktlintCheck` and `./gradlew detekt` in
the `android` lane. 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.** The `integration` lane 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 dev and tag pushes the signed APK is guaranteed present before the image build starts — no polling window, no race. On `main` pushes `android-release` is skipped and `image-release` instead pulls the most recent release's APK and the version sidecar recorded beside it, so `:latest` never ships without an update channel. It degrades to an empty `client/` — never a wrong version — if no release or APK asset can be resolved.
- **Cache server reachability.** The `web` lane does NOT use `cache: 'npm'` on `actions/setup-node` — the Gitea Actions cache server isn't reachable from this runner's container network, so `setup-node` would spend minutes on ETIMEDOUT before failing open. `setup-node` is not used at all (Node is in the image). The cache concern reappears if a change introduces a network-dependent action.
- **Artifacts — stock `actions/upload-artifact@v7` and `actions/download-artifact@v8`; never `@v3`.**
```yaml
uses: actions/upload-artifact@v7
uses: actions/download-artifact@v8
```
Stock works on this forge because gitea/runner 3.x edits the actions'
client-side `isGhes()` refusal out of their bundles; upload v4–v7 and
download v4–v8 are proven.
`@v3` is still broken: it reports success, and Gitea serves artifacts back only
through the v4 API (`content_encoding = application/zip`), so a v3 upload is
stored but invisible to every retrieval path.
**The upload and download majors need not match.** Every download major
v4–v8 reads the artifacts of every upload major v4–v7, by name and by
pattern; the only protocol break is v3 → v4. Every CI-runner image carries
Node 24, and the runner runs actions with the image's `node`.
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.