name: validate on: push: branches: [main] pull_request: branches: [main] # The repository uses the sha256 git object format. Git < 2.42 cannot speak # the sha256 wire protocol, and actions/checkout@v4 does `git init` without # --object-format=sha256, which produces `fatal: mismatched algorithms: # client sha1; server sha256`. We therefore skip actions/checkout entirely # and clone manually from a trixie-based container (git 2.47+). jobs: json-schema: name: JSON Schema (ajv) runs-on: ubuntu-latest container: image: node:24-trixie-slim steps: - name: Install git + curl in container run: | set -euo pipefail apt-get update apt-get install -y --no-install-recommends git curl ca-certificates git --version node --version - name: Clone repository (sha256-aware) run: | set -euo pipefail git clone --depth=1 --branch="${GITHUB_REF_NAME}" \ "https://x-access-token:${GITHUB_TOKEN}@forgejo.basicstack.de/${GITHUB_REPOSITORY}.git" "${GITHUB_WORKSPACE}" cd "${GITHUB_WORKSPACE}" git log -1 --oneline - name: Install ajv-cli + ajv-formats working-directory: ${{ github.workspace }} run: | set -euo pipefail npm install --prefix /tmp/ajv ajv-cli@5 ajv-formats@3 - name: Validate every JSON Schema parses as JSON working-directory: ${{ github.workspace }} run: | set -eu find schemas -type f -name 'v*.json' -print | while IFS= read -r f; do echo "--- parse: $f" node -e "require('./$f')" done - name: Meta-validate each schema against JSON Schema 2020-12 working-directory: ${{ github.workspace }} run: | set -eu export NODE_PATH=/tmp/ajv/node_modules find schemas -type f -name 'v*.json' -print | while IFS= read -r f; do echo "--- meta-validate: $f" node /tmp/ajv/node_modules/.bin/ajv compile --spec=draft2020 -c=ajv-formats -s "$f" done - name: Validate per-version example payloads against their schemas working-directory: ${{ github.workspace }} run: | set -eu export NODE_PATH=/tmp/ajv/node_modules # Each schema file is /vX.Y.Z.json; example payloads for that version # live under /examples/vX.Y.Z/*.json. This keeps v1.0.0 examples from # being validated against v1.1.0 and vice-versa. find schemas -type f -name 'v*.json' -print | while IFS= read -r schema; do version="$(basename "$schema" .json)" dir="$(dirname "$schema")/examples/$version" [ -d "$dir" ] || continue find "$dir" -type f -name '*.json' -print | while IFS= read -r example; do echo "--- validate example $example against $schema" node /tmp/ajv/node_modules/.bin/ajv validate --spec=draft2020 -c=ajv-formats -s "$schema" -d "$example" done done rego: name: Rego (opa fmt + opa test) runs-on: ubuntu-latest container: image: debian:trixie-slim steps: - name: Install git + curl in container run: | set -euo pipefail apt-get update apt-get install -y --no-install-recommends git curl ca-certificates git --version - name: Clone repository (sha256-aware) run: | set -euo pipefail git clone --depth=1 --branch="${GITHUB_REF_NAME}" \ "https://x-access-token:${GITHUB_TOKEN}@forgejo.basicstack.de/${GITHUB_REPOSITORY}.git" "${GITHUB_WORKSPACE}" cd "${GITHUB_WORKSPACE}" git log -1 --oneline - name: Install OPA run: | set -euo pipefail mkdir -p "$HOME/.local/bin" curl -sSL -o "$HOME/.local/bin/opa" https://openpolicyagent.org/downloads/v1.9.0/opa_linux_amd64_static chmod +x "$HOME/.local/bin/opa" echo "$HOME/.local/bin" >> "$GITHUB_PATH" "$HOME/.local/bin/opa" version - name: opa fmt (fail on any diff) working-directory: ${{ github.workspace }} run: opa fmt --list --diff policies/ - name: opa test working-directory: ${{ github.workspace }} run: opa test policies/ -v go: name: Go (vet + test) runs-on: ubuntu-latest container: # Alpine image: git 2.45+, speaks sha256 wire protocol; small footprint. # Pinned to 1.26 because signing-service depends on # github.com/open-policy-agent/opa v1.21.4 which requires Go >= 1.26.0. image: golang:1.26-alpine3.23 steps: - name: Install git + bash in container run: | set -eu apk add --no-cache git bash ca-certificates git --version go version - name: Clone repository (sha256-aware) run: | set -euo pipefail git clone --depth=1 --branch="${GITHUB_REF_NAME}" \ "https://x-access-token:${GITHUB_TOKEN}@forgejo.basicstack.de/${GITHUB_REPOSITORY}.git" "${GITHUB_WORKSPACE}" cd "${GITHUB_WORKSPACE}" git log -1 --oneline - name: Enumerate Go modules id: mods working-directory: ${{ github.workspace }} run: | set -eu # List every directory that contains a go.mod and run vet+test there. # Shell-quoted paths handle spaces. find services -type f -name 'go.mod' -print | sed 's|/go.mod$||' | sort -u | tee /tmp/go_modules.txt - name: go vet + test for every module working-directory: ${{ github.workspace }} run: | set -eu while IFS= read -r m; do [ -n "$m" ] || continue echo "=== $m ===" (cd "$m" && go vet ./... && go test ./...) done < /tmp/go_modules.txt db: name: DB (migrate up/down dry-run) runs-on: ubuntu-latest container: image: debian:trixie-slim options: --user 0:0 env: # Host-networked job container → localhost = host; use a non-default # port so parallel jobs do not collide on 5432 if any happen to share # a runner host. PGHOST: 127.0.0.1 PGPORT: "55432" PGUSER: postgres PGPASSWORD: postgres PGDATABASE: trust DB_URL: postgres://postgres:postgres@127.0.0.1:55432/trust?sslmode=disable PG_IMAGE: postgres:16-alpine PG_CONTAINER: pm-ci-pg-${{ github.run_id }}-${{ github.run_attempt }} steps: - name: Install git + curl + docker CLI + psql run: | set -euo pipefail apt-get update # Debian trixie: `docker.io` only brings the daemon; the CLI # (`/usr/bin/docker`) ships in `docker-cli` as a Recommends that # `--no-install-recommends` skips. Pull it in explicitly. apt-get install -y --no-install-recommends \ git curl ca-certificates docker.io docker-cli postgresql-client git --version docker --version psql --version - name: Clone repository (sha256-aware) run: | set -euo pipefail git clone --depth=1 --branch="${GITHUB_REF_NAME}" \ "https://x-access-token:${GITHUB_TOKEN}@forgejo.basicstack.de/${GITHUB_REPOSITORY}.git" \ "${GITHUB_WORKSPACE}" cd "${GITHUB_WORKSPACE}" git log -1 --oneline - name: Install golang-migrate CLI run: | set -euo pipefail curl -sSL -o /tmp/migrate.tgz \ https://github.com/golang-migrate/migrate/releases/download/v4.17.1/migrate.linux-amd64.tar.gz tar -xzf /tmp/migrate.tgz -C /usr/local/bin migrate migrate -version - name: Start Postgres (via docker) run: | set -euo pipefail docker run -d --name "${PG_CONTAINER}" \ -e POSTGRES_PASSWORD="${PGPASSWORD}" \ -e POSTGRES_USER="${PGUSER}" \ -e POSTGRES_DB="${PGDATABASE}" \ -p "${PGPORT}:5432" \ "${PG_IMAGE}" # Wait up to 60s for pg to be ready. for i in $(seq 1 60); do if pg_isready -h "${PGHOST}" -p "${PGPORT}" -U "${PGUSER}" -d "${PGDATABASE}" >/dev/null 2>&1; then echo "postgres ready after ${i}s" exit 0 fi sleep 1 done echo "postgres never became ready" >&2 docker logs "${PG_CONTAINER}" || true exit 1 - name: migrate up (full) + verify tables working-directory: ${{ github.workspace }} run: | set -euo pipefail migrate -path db/trust/migrations -database "${DB_URL}" up psql "${DB_URL}" -c '\dt' | tee /tmp/dt-after-up.txt grep -q ' per_diem_rates ' /tmp/dt-after-up.txt grep -q ' agents ' /tmp/dt-after-up.txt grep -q ' agent_keys ' /tmp/dt-after-up.txt grep -q ' approval_sessions ' /tmp/dt-after-up.txt # Roll back everything on top of per_diem_rates (0001) — currently # 0004_approval_sessions, 0003_agent_keys, 0002_agents — and verify # per_diem_rates remains while the agent-side tables are gone. Keep # this count equal to the number of migrations above 0001; bump when # a new migration is added. - name: migrate down (all above per_diem_rates) + verify working-directory: ${{ github.workspace }} run: | set -euo pipefail migrate -path db/trust/migrations -database "${DB_URL}" down 3 psql "${DB_URL}" -c '\dt' | tee /tmp/dt-after-down.txt grep -q ' per_diem_rates ' /tmp/dt-after-down.txt for table in agents agent_keys approval_sessions; do if grep -q " ${table} " /tmp/dt-after-down.txt; then echo "FAIL: ${table} still present after migrate down" >&2 exit 1 fi done - name: migrate up (re-apply) + apply seed + verify three agents working-directory: ${{ github.workspace }} run: | set -euo pipefail migrate -path db/trust/migrations -database "${DB_URL}" up psql "${DB_URL}" -v ON_ERROR_STOP=1 -f db/trust/seeds/0001_phase1_agents.sql # Idempotency: re-run must not error and not duplicate rows. psql "${DB_URL}" -v ON_ERROR_STOP=1 -f db/trust/seeds/0001_phase1_agents.sql count=$(psql "${DB_URL}" -At -c \ "SELECT count(*) FROM agents WHERE agent_id IN ('agent-approval-service','agent-testagent-transform','agent-dummy-sink')") echo "seeded agents = ${count}" [ "${count}" = "3" ] - name: Postgres logs (always) if: always() run: docker logs "${PG_CONTAINER}" || true - name: Stop Postgres (always) if: always() run: docker rm -f "${PG_CONTAINER}" || true