Files
minstrel/.gitea/workflows/test-go.yml
T
bvandeusen 3cf829752b
flutter / analyze-test-build (push) Failing after 2s
test-go / test (push) Successful in 33s
android / Build + lint + test (push) Failing after 41s
test-web / test (push) Successful in 41s
android / Build signed release APK (push) Has been skipped
test-go / integration (push) Successful in 11m16s
chore(ci): migrate workflows to gitea + consolidate keystore secret
- rename .forgejo/workflows/ to .gitea/workflows/ (git mv preserves
  history); Gitea Actions reads either path, but the directory now
  matches the active platform
- collapse ANDROID_STORE_PASSWORD + ANDROID_KEY_PASSWORD into a
  single ANDROID_KEYSTORE_PASSWORD secret (PKCS12 keystores require
  the two passwords to be identical, so the duplication carried no
  information); build.gradle.kts still reads two env vars to stay
  format-agnostic, both now sourced from the same secret in CI
- ignore android/*.keystore, android/*.keystore.*, android/*.jks,
  android/keystore.properties so the regenerated signing material
  never reaches a remote on accident
- update prose references from Forgejo to Gitea in CLAUDE.md and
  the docs; the historical "migrated from Forgejo" note in CLAUDE.md
  is kept intentionally; forgejo/upload-artifact@v3 action refs are
  left untouched (canonical artifact action, resolves cleanly under
  Gitea Actions)

Operator side: ANDROID_KEY_ALIAS, ANDROID_KEYSTORE_PASSWORD, and
ANDROID_KEYSTORE_B64 secrets registered in Gitea before this lands.
2026-05-31 23:05:03 -04:00

131 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/**'
- '.golangci.yml'
- '.gitea/workflows/test-go.yml'
pull_request:
branches: [main]
paths:
- '**/*.go'
- 'go.mod'
- 'go.sum'
- 'sqlc.yaml'
- 'internal/**'
- 'cmd/**'
- '.golangci.yml'
- '.gitea/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 ./...