c1fb2355c4
Adopts the FabledRulebook ci-runners.md policy: workflows select
toolchains via container.image, not via runs-on labels. `runs-on`
stays as the scheduling handle only.
Workflows updated:
- test-go.yml: ci-go:1.26 on both `test` and `integration` jobs.
- test-web.yml: ci-go:1.26 (bundles Node + npm); the
actions/setup-node@v4 step is removed.
- flutter.yml: ci-flutter:3.44 (Flutter 3.44 + Android + Java 25).
- release.yml: ci-go:1.26 (ships docker CLI + buildx).
Side effect: this unblocks the Go server deps bump (commit 6a62120)
which auto-bumped go.mod to `go 1.25.0` via x/crypto v0.51.0's
minimum — ci-go:1.26 satisfies it with headroom.
Adds ci-requirements.md at repo root per the ci-runners.md
"every project ships a requirements sheet" rule. Documents:
runtime images consumed, image deps used per workflow, per-job
installs (none), and a Notes section covering the integration
docker-socket dependency, the toolchain pin rationale, and the
in-app-update channel polling.
Tracked in local task #70.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
129 lines
5.0 KiB
YAML
129 lines
5.0 KiB
YAML
name: test-go
|
|
|
|
# Go server: vet + golangci-lint + short race tests. Runs on push to
|
|
# dev/main and PRs to main, scoped to Go-side files only — web-only or
|
|
# Flutter-only diffs don't trigger this workflow.
|
|
#
|
|
# Two jobs: `test` (fast — vet + lint + `go test -short -race`, no DB) and
|
|
# `integration` (full `go test -race` against an ephemeral Postgres).
|
|
#
|
|
# Integration-job DB wiring follows the act_runner shared-daemon pattern:
|
|
# the runner's Docker daemon also runs the operator's dev compose stack,
|
|
# so service containers get NO published ports (collision) and no
|
|
# service-name DNS. We discover the service container by the job-scoped
|
|
# name filter via the mounted docker socket and reach it by bridge IP.
|
|
# The exactly-one assertion is a hard guard — pointing tests at the dev
|
|
# Postgres would truncate it (the disaster Fable #339 exists to prevent).
|
|
#
|
|
# `web/build/` has a committed placeholder index.html so go:embed succeeds
|
|
# without needing the SPA to be freshly built. Real builds happen in
|
|
# release.yml (container) and locally during dev.
|
|
|
|
on:
|
|
push:
|
|
branches: [dev, main]
|
|
paths:
|
|
- '**/*.go'
|
|
- 'go.mod'
|
|
- 'go.sum'
|
|
- 'sqlc.yaml'
|
|
- 'internal/**'
|
|
- 'cmd/**'
|
|
- '.forgejo/workflows/test-go.yml'
|
|
pull_request:
|
|
branches: [main]
|
|
paths:
|
|
- '**/*.go'
|
|
- 'go.mod'
|
|
- 'go.sum'
|
|
- 'sqlc.yaml'
|
|
- 'internal/**'
|
|
- 'cmd/**'
|
|
- '.forgejo/workflows/test-go.yml'
|
|
|
|
jobs:
|
|
test:
|
|
runs-on: go-ci
|
|
container:
|
|
image: git.fabledsword.com/bvandeusen/ci-go:1.26
|
|
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Toolchain versions
|
|
run: |
|
|
go version
|
|
golangci-lint --version
|
|
|
|
- name: go vet
|
|
run: go vet ./...
|
|
|
|
- name: golangci-lint
|
|
run: golangci-lint run ./...
|
|
|
|
- name: go test (short, race)
|
|
run: go test -short -race ./...
|
|
|
|
integration:
|
|
runs-on: go-ci
|
|
container:
|
|
image: git.fabledsword.com/bvandeusen/ci-go:1.26
|
|
services:
|
|
postgres:
|
|
image: postgres:16-alpine
|
|
env:
|
|
POSTGRES_USER: minstrel
|
|
POSTGRES_PASSWORD: minstrel
|
|
POSTGRES_DB: minstrel_test
|
|
# No `ports:` — the runner shares the operator's dev compose
|
|
# Docker daemon; publishing a fixed host port collides.
|
|
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Integration suite (discover service by bridge IP, migrate, test)
|
|
run: |
|
|
set -eux
|
|
# Discover THIS job's Postgres service container via the
|
|
# mounted docker socket. act_runner attaches the job
|
|
# container and its service container(s) to a shared per-job
|
|
# network, so scope discovery to a postgres that sits on a
|
|
# network THIS job container is also on. The old
|
|
# `--filter name=integration` matched EVERY concurrent
|
|
# integration run's postgres (a dev push + the main-merge run
|
|
# overlap → 2 candidates → false "expected exactly 1" abort).
|
|
# The operator's dev compose `minstrel-postgres-*` is never on
|
|
# this job's network; skip it explicitly as belt-and-suspenders
|
|
# (a wrong target would truncate real data).
|
|
SELF=$(cat /etc/hostname)
|
|
SELF_NETS=$(docker inspect -f '{{range $k,$v := .NetworkSettings.Networks}}{{$k}} {{end}}' "$SELF")
|
|
test -n "$SELF_NETS"
|
|
echo "self ($SELF) networks: $SELF_NETS"
|
|
PG_ID=""
|
|
PG_NAME=""
|
|
for cid in $(docker ps --filter "ancestor=postgres:16-alpine" -q); do
|
|
nm=$(docker inspect -f '{{.Name}}' "$cid" | sed 's#^/##')
|
|
case "$nm" in *minstrel-postgres*|*_postgres_*) continue ;; esac
|
|
for net in $(docker inspect -f '{{range $k,$v := .NetworkSettings.Networks}}{{$k}} {{end}}' "$cid"); do
|
|
case " $SELF_NETS " in *" $net "*) PG_ID="$cid"; PG_NAME="$nm"; break 2 ;; esac
|
|
done
|
|
done
|
|
test -n "$PG_ID" || { echo "FATAL: no postgres service container on this job's network (self nets: $SELF_NETS)"; exit 1; }
|
|
echo "selected postgres: $PG_ID $PG_NAME"
|
|
PG_IP=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' "$PG_ID")
|
|
test -n "$PG_IP"
|
|
export MINSTREL_TEST_DATABASE_URL="postgres://minstrel:minstrel@${PG_IP}:5432/minstrel_test?sslmode=disable"
|
|
|
|
# Wait for Postgres to accept TCP (no health-check dependency).
|
|
for i in $(seq 1 60); do (echo > "/dev/tcp/${PG_IP}/5432") 2>/dev/null && break; sleep 2; done
|
|
|
|
# Apply embedded migrations to the fresh test DB, then run the
|
|
# full suite (no -short → integration tests execute). -p 1:
|
|
# every integration package TRUNCATEs the one shared test DB;
|
|
# concurrent package binaries → TRUNCATE deadlocks. Serialize
|
|
# package execution (the documented local invocation too).
|
|
MINSTREL_DATABASE_URL="$MINSTREL_TEST_DATABASE_URL" go run ./cmd/minstrel migrate
|
|
go test -p 1 -race ./...
|