Skip to content

chore: release 1.3.0 #723

chore: release 1.3.0

chore: release 1.3.0 #723

Workflow file for this run

name: CI
on:
pull_request:
push:
branches: [main]
concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
jobs:
# Detect the scope of the diff so the heavy jobs can short-circuit on
# docs/site-only changes. Gating is by *changed paths*: any .go / go.mod /
# go.sum / Makefile / .github / scripts touch flips `code` true and the full
# pipeline runs. See docs/CI_CD.md § "Path-based job gating".
changes:
name: Detect change scope
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: read
outputs:
code: ${{ steps.filter.outputs.code }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: dorny/paths-filter@ceb8a2b8f2d89434be7ff52d3de7ec3738c5cc9d # v4.0.3
id: filter
with:
# `code` = anything that can change the built binary or the CI/build
# machinery itself. Mirrors the docs-only definition in CLAUDE.md: a
# PR touching none of these is docs/site-only and skips build+fuzz.
#
# The Go source directories are matched wholesale rather than by
# `*.go` extension, because the binary embeds non-Go files too
# (`internal/schemaindex/schema_index.json`,
# `internal/compliance/datasafety/reference.csv`). Gating on the
# extension let those through as "docs-only", skipping the very tests
# that guard them. Directory-level globs keep any future `go:embed`
# covered by default; there are no docs under these paths, so nothing
# is over-triggered.
filters: |
code:
- 'cmd/**'
- 'commands/**'
- 'internal/**'
- '**/*.go'
- 'go.mod'
- 'go.sum'
- 'Makefile'
- '.github/**'
- 'scripts/**'
- 'install.sh'
# Inputs to `make schema-index-update`: they live under docs/ but
# a change here can desynchronise the embedded Schema index.
- 'docs/discovery/**'
build:
name: Build, lint, test
runs-on: ubuntu-latest
needs: changes
# NOTE: no job-level `if`. "Build, lint, test" is a required check on main,
# and a *skipped* required job blocks merge (GitHub treats skipped as
# unsatisfied). So the job always runs and reports success; the expensive
# steps self-select on `changes.outputs.code`. On a docs-only PR every step
# below is skipped and the job goes green in seconds — merge stays unblocked.
steps:
- if: needs.changes.outputs.code == 'true'
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- if: needs.changes.outputs.code == 'true'
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version: '1.25'
cache: true
- name: gofmt
if: needs.changes.outputs.code == 'true'
run: |
diff <(gofmt -d .) <(echo -n) || (echo '::error::run "make format" to fix formatting'; exit 1)
- name: go vet
if: needs.changes.outputs.code == 'true'
run: go vet ./...
- name: golangci-lint
if: needs.changes.outputs.code == 'true'
uses: golangci/golangci-lint-action@ba0d7d2ec06a0ea1cb5fa41b2e4a3ab91d21278a # v9.3.0
with:
version: v2.12.2
- name: test
if: needs.changes.outputs.code == 'true'
run: go test -race ./...
- name: build
if: needs.changes.outputs.code == 'true'
run: go build ./cmd/gplay
fuzz:
name: Fuzz smoke (untrusted-input parsers)
runs-on: ubuntu-latest
needs: changes
# Fuzz smoke is NOT a required check, so it can be skipped outright at the
# job level on docs-only PRs — no green-stub gymnastics needed.
if: needs.changes.outputs.code == 'true'
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version: '1.25'
cache: true
# Short bounded fuzzing of the parsers that consume untrusted bytes (API
# error envelope, JWT scope claim, reply-batch TSV). The seed corpora run
# as ordinary cases in the `test` job; this surfaces crashers from
# generated input without materially slowing it. -fuzztime is small on
# purpose — this is a smoke, not an exhaustive campaign.
- name: Fuzz parsers
run: |
set -euo pipefail
# Go persists generated fuzz inputs in $GOCACHE/fuzz, which setup-go's
# build cache restores across runs. That corpus grows unboundedly and
# eventually pushes the time-boxed run past the coordinator's shutdown
# deadline ("context deadline exceeded") on slow runners — seen on the
# v0.17.0 release PR (#383). Reset it so each smoke starts from the
# in-code f.Add seeds only; regression cases still run in `test`.
rm -rf "$(go env GOCACHE)/fuzz"
go test -run='^$' -fuzz='^FuzzParseErrorEnvelope$' -fuzztime=30s ./internal/play/api/
go test -run='^$' -fuzz='^FuzzScopesFromAssertion$' -fuzztime=30s ./internal/transport/
go test -run='^$' -fuzz='^FuzzParse$' -fuzztime=30s ./internal/reviews/batch/
docs:
name: Docs sanity
runs-on: ubuntu-latest
# Always runs: it's a required check, and its steps (verb gate, dash gate,
# shellcheck, required-files) are cheap and relevant to every PR regardless
# of scope.
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Verb vocabulary gate (ADR-0019)
run: bash scripts/verb-gate.sh
- name: Em dash gate (help text and error messages reach users)
run: bash scripts/dash-gate.sh
- name: shellcheck install.sh
run: shellcheck install.sh scripts/install-test.sh
# Offline: a stub curl serves a fixture release, so this asserts the
# fail-closed sha256 gate without touching the network.
- name: Install script fail-closed checksum gate
run: bash scripts/install-test.sh
- name: Verify required files exist
run: |
set -euo pipefail
missing=0
for f in \
CLAUDE.md \
CONTEXT.md \
CONTRIBUTING.md \
CODE_OF_CONDUCT.md \
LICENSE \
README.md \
SUPPORT.md \
docs/DESIGN.md \
docs/BACKLOG.md \
docs/CI_CD.md \
docs/adr/0001-credential-storage.md \
docs/adr/0002-safe-production-defaults.md \
docs/adr/0003-json-passthrough.md
do
if [ ! -f "$f" ]; then
echo "::error file=$f::missing required file"
missing=$((missing+1))
fi
done
exit $missing