4.7 KiB
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 installagainst live pacman/AUR mirrors. That requires a privileged container and mirror access; it lives in a future plan if ever needed. - No
golangci-lint. Thego vet+gofmtbaseline 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
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)
-
Install container prereqs. Before
actions/checkoutcan run, the Arch image needs git; the later steps also needbase-devel(for the C toolchain Go occasionally pulls in for cgo-adjacent deps) andshellcheck. Run once up front:pacman -Sy --needed --noconfirm base-devel git shellcheckNote:
gois intentionally NOT installed here —actions/setup-goinstalls the pinned version. -
Checkout code.
actions/checkout@v4withfetch-depth: 1(CI doesn't need history). -
Set up Go.
actions/setup-go@v5with:go-version-file: go.mod(pinned, deterministic)cache: true(built-in cache of$GOMODCACHE+$GOCACHE, keyed ongo.sum)
-
go vet ./...— catches obvious correctness issues the compiler misses. -
gofmtcheck:diff=$(gofmt -l .) if [ -n "$diff" ]; then echo "files need gofmt:"; echo "$diff"; exit 1 fiFails the build if any Go file isn't canonically formatted.
-
go build ./...— compiles every package. Catches deadcode paths and untested packages thatgo testwould otherwise skip. -
go test -race ./...— full unit test suite with the race detector. The engine (DAG scheduler) andprivilege.Acquire(sudo keepalive goroutine) are the concurrency hotspots this covers. -
shellcheck bootstrap.sh— lints the user-facing shell installer.install.sh.legacyis NOT linted; it's archived code. -
Install goreleaser +
goreleaser check:go install github.com/goreleaser/goreleaser/v2@latest "$(go env GOPATH)/bin/goreleaser" checkValidates
.goreleaser.yamlon 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-erroranywhere. gofmtcheck uses an explicit guard (not just-l) so a non-empty output is a clear failure.setup-gocache 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.