Skip to content

CI

CI #102

Workflow file for this run

name: CI
on:
pull_request:
branches:
- main
merge_group:
permissions:
contents: read
# pull-requests: read is needed so dorny/paths-filter can diff the PR.
pull-requests: read
jobs:
# changes detects whether this PR touches code or build config. Docs-only
# PRs skip the heavy jobs entirely but still produce a green CI Complete
# so the branch ruleset lets them merge.
#
# merge_group doesn't pass a useful base ref to dorny; the action falls
# back to treating every file as changed, which is exactly what we want —
# the merge queue should always run the full suite.
changes:
runs-on: ubuntu-latest
outputs:
code: ${{ steps.filter.outputs.code }}
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1
id: filter
with:
filters: |
code:
- '**/*.go'
- 'go.mod'
- 'go.sum'
- 'Makefile'
- '.goreleaser.yml'
- '.github/workflows/**'
- 'install.sh'
lint:
needs: changes
if: needs.changes.outputs.code == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Set up Go
uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0
with:
go-version: "1.25"
- name: Check formatting
run: |
if [ -n "$(gofmt -l .)" ]; then
echo "Code is not formatted. Run 'go fmt ./...'"
gofmt -d .
exit 1
fi
- name: Install staticcheck
run: go install honnef.co/go/tools/cmd/staticcheck@2025.1.1
- name: Run go vet
run: go vet ./...
- name: Run staticcheck
run: staticcheck ./...
test:
needs: changes
if: needs.changes.outputs.code == 'true'
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Set up Go
uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0
with:
go-version: "1.25"
- name: Run tests
run: go test -race -vet=off ./...
cover:
needs: changes
if: needs.changes.outputs.code == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Set up Go
uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0
with:
go-version: "1.25"
- name: Enforce 100% internal coverage
run: make cover
build:
needs: changes
if: needs.changes.outputs.code == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Set up Go
uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0
with:
go-version: "1.25"
- name: Build binary
run: make build
goreleaser-check:
needs: changes
if: needs.changes.outputs.code == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Set up Go
uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0
with:
go-version: "1.25"
- name: Install GoReleaser
uses: goreleaser/goreleaser-action@f06c13b6b1a9625abc9e6e439d9c05a8f2190e94 # v7.2.3
with:
distribution: goreleaser
version: "~> v2"
install-only: true
- name: Validate GoReleaser config
run: goreleaser check
# Sentinel required-status-check consumed by the branch ruleset + merge queue.
# Runs unconditionally so docs-only PRs still report green. Code-gated jobs
# count as success when they ran AND succeeded, or when they were skipped
# because no code changed; any other result (failure, cancelled) fails the
# gate.
ci-complete:
name: CI Complete
needs: [changes, lint, test, cover, build, goreleaser-check]
if: always()
runs-on: ubuntu-latest
steps:
- name: Aggregate job results
env:
CODE_CHANGED: ${{ needs.changes.outputs.code }}
CHANGES_RESULT: ${{ needs.changes.result }}
LINT_RESULT: ${{ needs.lint.result }}
TEST_RESULT: ${{ needs.test.result }}
COVER_RESULT: ${{ needs.cover.result }}
BUILD_RESULT: ${{ needs.build.result }}
GORELEASER_RESULT: ${{ needs.goreleaser-check.result }}
run: |
echo "code_changed=$CODE_CHANGED"
echo "changes=$CHANGES_RESULT lint=$LINT_RESULT test=$TEST_RESULT cover=$COVER_RESULT build=$BUILD_RESULT goreleaser=$GORELEASER_RESULT"
# Include changes in the validation loop: if the paths-filter job
# itself fails (checkout error, dorny bug), every downstream job
# reports `skipped` and the gate would otherwise pass despite no
# actual verification having run.
# Acceptable per-job results:
# success — ran and passed
# skipped — no code changed, so no work to do
# Anything else (failure, cancelled) fails the gate.
for r in "$CHANGES_RESULT" "$LINT_RESULT" "$TEST_RESULT" "$COVER_RESULT" "$BUILD_RESULT" "$GORELEASER_RESULT"; do
case "$r" in
success|skipped) ;;
*)
echo "one or more required jobs failed: $r"
exit 1
;;
esac
done
echo "all required checks passed"