From 50b41b3912418b3eae105ccf83a45b4c66e58035 Mon Sep 17 00:00:00 2001 From: Jared Jakacky Date: Mon, 18 May 2026 07:44:16 -0500 Subject: [PATCH 1/3] build(ci): verify docs and runnable examples --- .github/workflows/ci.yaml | 23 ++++++ .github/workflows/release.yaml | 25 +++++- Makefile | 4 + README.md | 5 +- scripts/verify-docs-examples.sh | 136 ++++++++++++++++++++++++++++++++ 5 files changed, 191 insertions(+), 2 deletions(-) create mode 100755 scripts/verify-docs-examples.sh diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 0c468b9..b2b3dbe 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -61,3 +61,26 @@ jobs: check-latest: true - name: Run race detector run: make test-race + + docs-examples: + name: Docs and examples (Go ${{ matrix.go-version }}) + runs-on: ubuntu-latest + timeout-minutes: 10 + strategy: + fail-fast: false + matrix: + go-version: + # renovate: datasource=custom.go-supported-floor depName=go-floor versioning=semver-coerced + - '1.25.x' + # renovate: datasource=custom.go-supported-latest depName=go-latest versioning=semver-coerced + - '1.26.x' + steps: + - name: Check out repository + uses: actions/checkout@v6 + - name: Set up Go + uses: actions/setup-go@v6 + with: + go-version: ${{ matrix.go-version }} + check-latest: true + - name: Verify docs and runnable examples + run: make verify-docs-examples diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 25f8997..ac323c5 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -80,9 +80,32 @@ jobs: - name: Run govulncheck run: make govulncheck + docs-examples: + name: Docs and examples (Go ${{ matrix.go-version }}) + runs-on: ubuntu-latest + timeout-minutes: 10 + strategy: + fail-fast: false + matrix: + go-version: + # renovate: datasource=custom.go-supported-floor depName=go-floor versioning=semver-coerced + - '1.25.x' + # renovate: datasource=custom.go-supported-latest depName=go-latest versioning=semver-coerced + - '1.26.x' + steps: + - name: Check out repository + uses: actions/checkout@v6 + - name: Set up Go + uses: actions/setup-go@v6 + with: + go-version: ${{ matrix.go-version }} + check-latest: true + - name: Verify docs and runnable examples + run: make verify-docs-examples + release: name: release - needs: [verify, race, govulncheck] + needs: [verify, race, govulncheck, docs-examples] runs-on: ubuntu-latest permissions: contents: write diff --git a/Makefile b/Makefile index 7f52a98..bdcb2c9 100644 --- a/Makefile +++ b/Makefile @@ -16,6 +16,7 @@ export GOCACHE ?= $(CURDIR)/.cache/go-build .PHONY: \ help \ build-examples \ + verify-docs-examples \ fmt \ fmt-check \ vet \ @@ -36,6 +37,9 @@ build-examples: ## Compile the runnable example programs. @echo "==> build examples" @$(GO) build ./examples/... +verify-docs-examples: ## Run docs and runnable examples verification. + @scripts/verify-docs-examples.sh + fmt: ## Format tracked Go source files. @echo "==> formatting" @$(GOFMT) -w $(GOFILES) diff --git a/README.md b/README.md index 1405cee..e245117 100644 --- a/README.md +++ b/README.md @@ -229,11 +229,14 @@ This repository uses `make` for local verification: ```bash make verify +make verify-docs-examples make test-race make govulncheck ``` -`make verify` checks formatting, runs `go vet`, runs tests, builds the runnable examples, and verifies that `go.mod` and `go.sum` are tidy. CI runs verification and race tests on the supported Go versions, and release tags are gated by verification, race tests, and `govulncheck` before publishing. +`make verify` checks formatting, runs `go vet`, runs tests, builds the runnable examples, and verifies that `go.mod` and `go.sum` are tidy. `make verify-docs-examples` runs the docs/example release gate: tests, example builds, bounded example runs, and stale docs scans. + +CI runs verification, docs/examples verification, and race tests on the supported Go versions. Release tags are gated by those jobs plus `govulncheck` before publishing. ## Issues and Scope diff --git a/scripts/verify-docs-examples.sh b/scripts/verify-docs-examples.sh new file mode 100755 index 0000000..1bc0ade --- /dev/null +++ b/scripts/verify-docs-examples.sh @@ -0,0 +1,136 @@ +#!/bin/sh +set -eu + +GO="${GO:-go}" +TIMEOUT="${TIMEOUT:-timeout}" +SERVER_TIMEOUT="${SERVER_TIMEOUT:-5s}" + +examples=" +basic +telemetry +endpoint-controls +custom-encoding +readiness +logging +cors +external-server +advanced-composition +streaming +reverse-proxy +response-capture +" + +server_examples=" +basic +telemetry +endpoint-controls +custom-encoding +readiness +logging +cors +external-server +advanced-composition +streaming +reverse-proxy +response-capture +" + +check_no_matches() { + pattern="$1" + shift + if rg -U -n "$pattern" "$@" + then + echo "unexpected stale docs/examples reference matched: $pattern" >&2 + exit 1 + fi +} + +check_local_markdown_links() { + find README.md docs examples -name '*.md' -print | while IFS= read -r file + do + sed -n 's/.*](\([^)]*\)).*/\1/p' "$file" | while IFS= read -r link + do + case "$link" in + ""|\#*|http://*|https://*|mailto:*) + continue + ;; + esac + + target="${link%%#*}" + [ -n "$target" ] || continue + + case "$target" in + /*) + echo "absolute local markdown link in $file: $link" >&2 + exit 1 + ;; + *) + base="$(dirname "$file")" + test -e "$base/$target" || { + echo "missing local markdown link target in $file: $link" >&2 + exit 1 + } + ;; + esac + done + done +} + +run_server_example() { + example="$1" + echo "==> go run ./examples/$example (bounded by $SERVER_TIMEOUT)" + set +e + "$TIMEOUT" "$SERVER_TIMEOUT" "$GO" run "./examples/$example" + status="$?" + set -e + if [ "$status" -eq 124 ] + then + return 0 + fi + if [ "$status" -ne 0 ] + then + echo "server example failed before timeout: $example" >&2 + exit "$status" + fi + echo "server example exited before timeout: $example" >&2 + exit 1 +} + +echo "==> checking required tools" +command -v rg >/dev/null 2>&1 || { + echo "rg is required for docs/examples verification" >&2 + exit 1 +} + +echo "==> checking example directories" +for example in $examples +do + test -d "examples/$example" || { + echo "missing examples/$example" >&2 + exit 1 + } +done + +echo "==> go test ./..." +"$GO" test ./... + +echo "==> go build ./examples/..." +"$GO" build ./examples/... + +if command -v "$TIMEOUT" >/dev/null 2>&1 +then + echo "==> server example startup checks" + for example in $server_examples + do + run_server_example "$example" + done +else + echo "==> skipping server example startup checks; timeout command not found" +fi + +echo "==> stale API and link scans" +check_local_markdown_links +check_no_matches '\bWithTelemetry\b|\bWithTracing\b|\bWithMetrics\b|\bWithRequestID\b|\bWithCorrelationID\b|\bWithAccessLog\b|\bWithRecovery\b|\bservekit\.(Start|Shutdown)\(' README.md docs examples +check_no_matches 'examples/(hello|middleware|otel|health|proxy|server|composition)([^a-zA-Z0-9_-]|$)' README.md docs examples + +echo "==> docs/examples verification passed" From 7f8e15e195bb21013ebb6805212223db0d6764cb Mon Sep 17 00:00:00 2001 From: Jared Jakacky Date: Mon, 18 May 2026 13:16:54 -0500 Subject: [PATCH 2/3] build(ci): simplify example verification --- .github/workflows/ci.yaml | 23 ------ .github/workflows/release.yaml | 25 +----- Makefile | 3 +- README.md | 5 +- scripts/verify-docs-examples.sh | 136 -------------------------------- 5 files changed, 5 insertions(+), 187 deletions(-) delete mode 100755 scripts/verify-docs-examples.sh diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index b2b3dbe..0c468b9 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -61,26 +61,3 @@ jobs: check-latest: true - name: Run race detector run: make test-race - - docs-examples: - name: Docs and examples (Go ${{ matrix.go-version }}) - runs-on: ubuntu-latest - timeout-minutes: 10 - strategy: - fail-fast: false - matrix: - go-version: - # renovate: datasource=custom.go-supported-floor depName=go-floor versioning=semver-coerced - - '1.25.x' - # renovate: datasource=custom.go-supported-latest depName=go-latest versioning=semver-coerced - - '1.26.x' - steps: - - name: Check out repository - uses: actions/checkout@v6 - - name: Set up Go - uses: actions/setup-go@v6 - with: - go-version: ${{ matrix.go-version }} - check-latest: true - - name: Verify docs and runnable examples - run: make verify-docs-examples diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index ac323c5..25f8997 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -80,32 +80,9 @@ jobs: - name: Run govulncheck run: make govulncheck - docs-examples: - name: Docs and examples (Go ${{ matrix.go-version }}) - runs-on: ubuntu-latest - timeout-minutes: 10 - strategy: - fail-fast: false - matrix: - go-version: - # renovate: datasource=custom.go-supported-floor depName=go-floor versioning=semver-coerced - - '1.25.x' - # renovate: datasource=custom.go-supported-latest depName=go-latest versioning=semver-coerced - - '1.26.x' - steps: - - name: Check out repository - uses: actions/checkout@v6 - - name: Set up Go - uses: actions/setup-go@v6 - with: - go-version: ${{ matrix.go-version }} - check-latest: true - - name: Verify docs and runnable examples - run: make verify-docs-examples - release: name: release - needs: [verify, race, govulncheck, docs-examples] + needs: [verify, race, govulncheck] runs-on: ubuntu-latest permissions: contents: write diff --git a/Makefile b/Makefile index bdcb2c9..d2fba84 100644 --- a/Makefile +++ b/Makefile @@ -37,8 +37,7 @@ build-examples: ## Compile the runnable example programs. @echo "==> build examples" @$(GO) build ./examples/... -verify-docs-examples: ## Run docs and runnable examples verification. - @scripts/verify-docs-examples.sh +verify-docs-examples: build-examples ## Compatibility target for compiling runnable examples. fmt: ## Format tracked Go source files. @echo "==> formatting" diff --git a/README.md b/README.md index e245117..46c32f8 100644 --- a/README.md +++ b/README.md @@ -229,14 +229,15 @@ This repository uses `make` for local verification: ```bash make verify +make build-examples make verify-docs-examples make test-race make govulncheck ``` -`make verify` checks formatting, runs `go vet`, runs tests, builds the runnable examples, and verifies that `go.mod` and `go.sum` are tidy. `make verify-docs-examples` runs the docs/example release gate: tests, example builds, bounded example runs, and stale docs scans. +`make verify` checks formatting, runs `go vet`, runs tests, builds the runnable examples, and verifies that `go.mod` and `go.sum` are tidy. `make build-examples` and `make verify-docs-examples` are available when you only want to compile the runnable examples. -CI runs verification, docs/examples verification, and race tests on the supported Go versions. Release tags are gated by those jobs plus `govulncheck` before publishing. +CI runs verification and race tests on the supported Go versions. Release tags are gated by those jobs plus `govulncheck` before publishing. ## Issues and Scope diff --git a/scripts/verify-docs-examples.sh b/scripts/verify-docs-examples.sh deleted file mode 100755 index 1bc0ade..0000000 --- a/scripts/verify-docs-examples.sh +++ /dev/null @@ -1,136 +0,0 @@ -#!/bin/sh -set -eu - -GO="${GO:-go}" -TIMEOUT="${TIMEOUT:-timeout}" -SERVER_TIMEOUT="${SERVER_TIMEOUT:-5s}" - -examples=" -basic -telemetry -endpoint-controls -custom-encoding -readiness -logging -cors -external-server -advanced-composition -streaming -reverse-proxy -response-capture -" - -server_examples=" -basic -telemetry -endpoint-controls -custom-encoding -readiness -logging -cors -external-server -advanced-composition -streaming -reverse-proxy -response-capture -" - -check_no_matches() { - pattern="$1" - shift - if rg -U -n "$pattern" "$@" - then - echo "unexpected stale docs/examples reference matched: $pattern" >&2 - exit 1 - fi -} - -check_local_markdown_links() { - find README.md docs examples -name '*.md' -print | while IFS= read -r file - do - sed -n 's/.*](\([^)]*\)).*/\1/p' "$file" | while IFS= read -r link - do - case "$link" in - ""|\#*|http://*|https://*|mailto:*) - continue - ;; - esac - - target="${link%%#*}" - [ -n "$target" ] || continue - - case "$target" in - /*) - echo "absolute local markdown link in $file: $link" >&2 - exit 1 - ;; - *) - base="$(dirname "$file")" - test -e "$base/$target" || { - echo "missing local markdown link target in $file: $link" >&2 - exit 1 - } - ;; - esac - done - done -} - -run_server_example() { - example="$1" - echo "==> go run ./examples/$example (bounded by $SERVER_TIMEOUT)" - set +e - "$TIMEOUT" "$SERVER_TIMEOUT" "$GO" run "./examples/$example" - status="$?" - set -e - if [ "$status" -eq 124 ] - then - return 0 - fi - if [ "$status" -ne 0 ] - then - echo "server example failed before timeout: $example" >&2 - exit "$status" - fi - echo "server example exited before timeout: $example" >&2 - exit 1 -} - -echo "==> checking required tools" -command -v rg >/dev/null 2>&1 || { - echo "rg is required for docs/examples verification" >&2 - exit 1 -} - -echo "==> checking example directories" -for example in $examples -do - test -d "examples/$example" || { - echo "missing examples/$example" >&2 - exit 1 - } -done - -echo "==> go test ./..." -"$GO" test ./... - -echo "==> go build ./examples/..." -"$GO" build ./examples/... - -if command -v "$TIMEOUT" >/dev/null 2>&1 -then - echo "==> server example startup checks" - for example in $server_examples - do - run_server_example "$example" - done -else - echo "==> skipping server example startup checks; timeout command not found" -fi - -echo "==> stale API and link scans" -check_local_markdown_links -check_no_matches '\bWithTelemetry\b|\bWithTracing\b|\bWithMetrics\b|\bWithRequestID\b|\bWithCorrelationID\b|\bWithAccessLog\b|\bWithRecovery\b|\bservekit\.(Start|Shutdown)\(' README.md docs examples -check_no_matches 'examples/(hello|middleware|otel|health|proxy|server|composition)([^a-zA-Z0-9_-]|$)' README.md docs examples - -echo "==> docs/examples verification passed" From c36f765bd1017877ffd91c641d372fffff63a20f Mon Sep 17 00:00:00 2001 From: Jared Jakacky Date: Mon, 18 May 2026 13:27:37 -0500 Subject: [PATCH 3/3] build(ci): simplify example verification --- Makefile | 3 --- README.md | 3 +-- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/Makefile b/Makefile index d2fba84..7f52a98 100644 --- a/Makefile +++ b/Makefile @@ -16,7 +16,6 @@ export GOCACHE ?= $(CURDIR)/.cache/go-build .PHONY: \ help \ build-examples \ - verify-docs-examples \ fmt \ fmt-check \ vet \ @@ -37,8 +36,6 @@ build-examples: ## Compile the runnable example programs. @echo "==> build examples" @$(GO) build ./examples/... -verify-docs-examples: build-examples ## Compatibility target for compiling runnable examples. - fmt: ## Format tracked Go source files. @echo "==> formatting" @$(GOFMT) -w $(GOFILES) diff --git a/README.md b/README.md index 46c32f8..2c2b437 100644 --- a/README.md +++ b/README.md @@ -230,12 +230,11 @@ This repository uses `make` for local verification: ```bash make verify make build-examples -make verify-docs-examples make test-race make govulncheck ``` -`make verify` checks formatting, runs `go vet`, runs tests, builds the runnable examples, and verifies that `go.mod` and `go.sum` are tidy. `make build-examples` and `make verify-docs-examples` are available when you only want to compile the runnable examples. +`make verify` checks formatting, runs `go vet`, runs tests, builds the runnable examples, and verifies that `go.mod` and `go.sum` are tidy. `make build-examples` is available when you only want to compile the runnable examples. CI runs verification and race tests on the supported Go versions. Release tags are gated by those jobs plus `govulncheck` before publishing.