Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
149 changes: 141 additions & 8 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,157 @@ on:
- master
pull_request:

permissions:
contents: read

jobs:
run-tests:
name: lint-and-test
lint:
name: lint
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.26.x'
check-latest: true
- name: Check gofmt
run: |
unformatted="$(gofmt -l .)"
if [ -n "$unformatted" ]; then
echo "gofmt required for:"; echo "$unformatted"; exit 1
fi
- name: Verify go.mod/go.sum are tidy
run: |
go mod tidy
git diff --exit-code go.mod go.sum
- name: golangci-lint
uses: golangci/golangci-lint-action@v8
with:
version: v2.12.2

test:
name: test (go ${{ matrix.go-version }}, ${{ matrix.os }})
strategy:
fail-fast: false
matrix:
go-version: [1.18, 1.22]
# Support policy: the two most recent Go releases (go.mod
# declares the minimum).
go-version: ['1.25.x', '1.26.x']
os: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{ matrix.os }}
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: ${{ matrix.go-version }}
- name: golangci-lint
uses: golangci/golangci-lint-action@v6
check-latest: true
- name: Vet
run: go vet ./...
- name: Run unit tests (race detector)
run: go test -race -count=1 ./...

govulncheck:
name: govulncheck
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.26.x'
check-latest: true
- name: Scan for known vulnerabilities
run: |
go install golang.org/x/vuln/cmd/govulncheck@v1.1.4

# Full human-readable report always appears in the log.
govulncheck ./... || true

# Gate policy: vulnerabilities reachable in this module's own
# dependencies FAIL the build — those ship to users through
# go.mod. Standard-library findings are advisory here: consumers
# compile this library with their own Go toolchain, so the CI
# toolchain's stdlib is not shipped, and a strict gate would turn
# red on an unrelated schedule every time a Go patch release lands
# before the runner image's cached toolchain catches up.
# `check-latest: true` above keeps the toolchain current, so
# stdlib findings should normally be empty anyway.
govulncheck -format json ./... > govulncheck.json || true

dependency_vulns="$(jq -s -r '
[ .[]
| select(.finding)
| .finding
| select(.trace[0].function) # reachable symbol
| select(.trace[0].module != "stdlib") # not the toolchain
| .osv ] | unique | .[]' govulncheck.json)"

stdlib_vulns="$(jq -s -r '
[ .[]
| select(.finding)
| .finding
| select(.trace[0].function)
| select(.trace[0].module == "stdlib")
| .osv ] | unique | .[]' govulncheck.json)"

if [ -n "$stdlib_vulns" ]; then
echo "::warning title=Go stdlib vulnerabilities::Advisory only (fixed by a newer Go toolchain): $(echo "$stdlib_vulns" | tr '\n' ' ')"
fi

if [ -n "$dependency_vulns" ]; then
echo "::error title=Dependency vulnerabilities::$(echo "$dependency_vulns" | tr '\n' ' ')"
exit 1
fi

echo "No reachable dependency vulnerabilities."

fuzz-smoke:
name: fuzz (smoke)
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.26.x'
check-latest: true
- name: Fuzz parsers and redaction briefly
run: |
for target in FuzzBuildThreads FuzzRedactURL FuzzRetryAfter FuzzSplitQualifiedFunction FuzzSafeMultipartName; do
go test -run "^$target$" -fuzz "^$target$" -fuzztime 20s .
done

cross-compile:
name: build (${{ matrix.target }})
runs-on: ubuntu-latest
timeout-minutes: 15
strategy:
fail-fast: false
matrix:
target:
- linux/amd64
- linux/arm
- linux/arm64
- windows/amd64
- darwin/arm64
- freebsd/amd64
steps:
- uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
version: v1.60
- name: Run Unit Tests
go-version: '1.26.x'
check-latest: true
- name: Build
run: |
go test
export GOOS="${TARGET%/*}" GOARCH="${TARGET#*/}"
go build ./...
go vet ./...
env:
TARGET: ${{ matrix.target }}
14 changes: 14 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# OS artifacts
.DS_Store

# Test and coverage artifacts
*.test
coverage.out

# Example tracer output
tracedir/
tracelog

# Editor directories
.idea/
.vscode/
24 changes: 24 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# golangci-lint v2 configuration.
version: "2"

linters:
# standard: errcheck, govet, ineffassign, staticcheck, unused.
default: standard
settings:
errcheck:
# Response/file Close in defers is intentionally best-effort.
exclude-functions:
- (net/http.ResponseWriter).Write
- (io.Closer).Close
- (*os.File).Close
- (net.Listener).Close
exclusions:
rules:
# Test helpers may ignore errors for brevity.
- path: _test\.go
linters:
- errcheck

formatters:
enable:
- gofmt
42 changes: 42 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
.PHONY: help build test race vet fmt fmt-check lint tidy cover cross clean

help: ## Show this help
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "%-12s %s\n", $$1, $$2}'

build: ## Build all packages
go build ./...

test: ## Run unit tests
go test -count=1 ./...

race: ## Run unit tests with the race detector
go test -race -count=1 ./...

vet: ## Run go vet
go vet ./...

fmt: ## Format all Go files
gofmt -w .

fmt-check: ## Fail if any file needs formatting
@unformatted="$$(gofmt -l .)"; if [ -n "$$unformatted" ]; then \
echo "gofmt required for:"; echo "$$unformatted"; exit 1; fi

lint: ## Run golangci-lint (requires golangci-lint v2)
golangci-lint run ./...

tidy: ## Verify go.mod/go.sum are tidy
go mod tidy

cover: ## Run tests with coverage report
go test -race -count=1 -coverprofile=coverage.out ./...
go tool cover -func=coverage.out | tail -1

cross: ## Cross-compile for all supported platforms
@for target in linux/amd64 linux/arm linux/arm64 windows/amd64 darwin/arm64 freebsd/amd64; do \
echo "building $$target"; \
GOOS=$${target%/*} GOARCH=$${target#*/} go build ./... || exit 1; \
done

clean: ## Remove build artifacts
rm -f coverage.out
Loading
Loading