Files
Archduino/docs/superpowers/specs/2026-04-14-ci-workflow-design.md
2026-04-14 21:28:44 +02:00

100 lines
4.7 KiB
Markdown
Raw Permalink 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 Workflow Design (Plan 4d)
**Date:** 2026-04-14
**Scope:** Plan 4d of the Archduino Go rewrite — validate Go code quality, build health, and release config on every change.
## Goal
A single Gitea Actions workflow that gives fast, reliable feedback on every push and pull request. It runs unit tests with the race detector, static checks, a build sanity pass, shell-script linting, and validates the goreleaser config — all inside an Arch Linux container for environmental fidelity with the target platform.
## Non-goals
- No integration tests that invoke a real `archduino install` against live pacman/AUR mirrors. That requires a privileged container and mirror access; it lives in a future plan if ever needed.
- No `golangci-lint`. The `go vet` + `gofmt` baseline is enough for this codebase; revisit only if linting gaps surface.
- No coverage reporting or codecov upload.
- No scheduled/nightly runs. Trigger is push + PR only.
## Trigger
```yaml
on:
push:
pull_request:
```
Fires on every push to every branch and every pull request. Matches a single-developer workflow where noisy branches aren't a real concern; immediate feedback is worth the runner minutes.
## Runner
- `runs-on: ubuntu-latest` — same runner label used by the Plan 4c release workflow.
- `container: archlinux:latest` — tests execute inside a fresh Arch userspace each run, matching Archduino's sole supported OS.
**Runner infrastructure:** depends on the same Gitea Actions runner registered for Plan 4c. No new infra introduced by this plan. If the runner is offline, CI queues rather than failing; this is acceptable behavior.
## Job steps (single job, sequential)
1. **Install container prereqs.** Before `actions/checkout` can run, the Arch image needs git; the later steps also need `base-devel` (for the C toolchain Go occasionally pulls in for cgo-adjacent deps) and `shellcheck`. Run once up front:
```
pacman -Sy --needed --noconfirm base-devel git shellcheck
```
Note: `go` is intentionally NOT installed here — `actions/setup-go` installs the pinned version.
2. **Checkout code.** `actions/checkout@v4` with `fetch-depth: 1` (CI doesn't need history).
3. **Set up Go.** `actions/setup-go@v5` with:
- `go-version-file: go.mod` (pinned, deterministic)
- `cache: true` (built-in cache of `$GOMODCACHE` + `$GOCACHE`, keyed on `go.sum`)
4. **`go vet ./...`** — catches obvious correctness issues the compiler misses.
5. **`gofmt` check:**
```
diff=$(gofmt -l .)
if [ -n "$diff" ]; then
echo "files need gofmt:"; echo "$diff"; exit 1
fi
```
Fails the build if any Go file isn't canonically formatted.
6. **`go build ./...`** — compiles every package. Catches deadcode paths and untested packages that `go test` would otherwise skip.
7. **`go test -race ./...`** — full unit test suite with the race detector. The engine (DAG scheduler) and `privilege.Acquire` (sudo keepalive goroutine) are the concurrency hotspots this covers.
8. **`shellcheck bootstrap.sh`** — lints the user-facing shell installer. `install.sh.legacy` is NOT linted; it's archived code.
9. **Install goreleaser + `goreleaser check`:**
```
go install github.com/goreleaser/goreleaser/v2@latest
"$(go env GOPATH)/bin/goreleaser" check
```
Validates `.goreleaser.yaml` on every change. Cheap insurance against config drift.
## Runtime expectations
- Arch image pull: ~15–30s (cached on the runner after first run).
- `pacman -Sy … base-devel git shellcheck`: ~10–20s.
- `actions/setup-go` + cache restore: ~5–10s warm, ~30s cold.
- `go build ./...` + `go test -race ./...`: ~30–60s given 37 test files with mostly mocked I/O.
- `go vet`, `gofmt`, `shellcheck`, `goreleaser check`: <5s combined.
- **Total per run:** ~1–2 min warm cache, ~3 min cold.
## Error handling
- Sequential steps; the first failure fails the workflow. No `continue-on-error` anywhere.
- `gofmt` check uses an explicit guard (not just `-l`) so a non-empty output is a clear failure.
- `setup-go` cache misses silently fall back to a clean install — no workflow-breaking behavior.
## Caching
`actions/setup-go@v5` handles Go build/module caching natively via `cache: true`. No extra cache step. The `pacman` packages are re-installed every run; caching them is not worth the complexity for ~15s of savings on a 90s run.
## Files touched
- Create: `.gitea/workflows/ci.yml`
No other repo changes; the existing tests, `go.mod`, `.goreleaser.yaml`, and `bootstrap.sh` all stay as-is.
## Interaction with Plan 4c
The release workflow (`.gitea/workflows/release.yml`) is unchanged. Both workflows share the Gitea Actions runner but never run together: CI fires on push/PR, release fires on tag push. Independent files, independent triggers, zero coupling.