S38: verifiers — API doc sync + SPDX header coverage
- SHA
2d175a5801d0a0e8309952c7cf52f8a3a82103e3- Parents
-
0104569 - Tree
f5ed7c8
2d175a5
2d175a5801d0a0e8309952c7cf52f8a3a82103e30104569
f5ed7c8| Status | File | + | - |
|---|---|---|---|
| A |
scripts/verify-api-docs.sh
|
62 | 0 |
| A |
scripts/verify-spdx-headers.sh
|
65 | 0 |
scripts/verify-api-docs.shadded@@ -0,0 +1,62 @@ | ||
| 1 | +#!/usr/bin/env bash | |
| 2 | +# SPDX-License-Identifier: AGPL-3.0-or-later | |
| 3 | +# | |
| 4 | +# Verify that every /api/v1 route registered in | |
| 5 | +# internal/web/handlers/api/ appears in docs/public/api/. | |
| 6 | +# | |
| 7 | +# We don't enforce on a per-page basis — the route just needs to | |
| 8 | +# show up *somewhere* under docs/public/api/. The page-organization | |
| 9 | +# is the human's call; the script catches the most common drift | |
| 10 | +# bug (a new endpoint shipped, no doc updated). | |
| 11 | +# | |
| 12 | +# Excluded by design: planned-only endpoints in the docs that | |
| 13 | +# the source doesn't yet implement (routes mentioned in tables | |
| 14 | +# under "## Planned routes" headings). The verifier is route → | |
| 15 | +# doc, not doc → route. | |
| 16 | + | |
| 17 | +set -euo pipefail | |
| 18 | + | |
| 19 | +ROOT="$(cd "$(dirname "$0")/.." && pwd)" | |
| 20 | +cd "$ROOT" | |
| 21 | + | |
| 22 | +# Collect every /api/v1 route literal from chi route registrations. | |
| 23 | +# Pattern: r.<Method>("/api/v1/<path>", ...) | |
| 24 | +ROUTES_FILE="$(mktemp)" | |
| 25 | +trap 'rm -f "$ROUTES_FILE"' EXIT | |
| 26 | + | |
| 27 | +grep -rhoE 'r\.(Get|Post|Put|Patch|Delete|Head|Options)\("/api/v1[^"]*"' \ | |
| 28 | + internal/web/handlers/api 2>/dev/null \ | |
| 29 | + | sed -E 's/^r\.[A-Z][a-z]+\("([^"]+)".*$/\1/' \ | |
| 30 | + | sort -u > "$ROUTES_FILE" | |
| 31 | + | |
| 32 | +route_count=$(wc -l < "$ROUTES_FILE" | tr -d ' ') | |
| 33 | +if [[ "$route_count" -eq 0 ]]; then | |
| 34 | + echo "verify-api-docs: no API routes found — is the package layout right?" >&2 | |
| 35 | + exit 2 | |
| 36 | +fi | |
| 37 | + | |
| 38 | +# Concatenate every doc page into one searchable blob. | |
| 39 | +DOC_BLOB="$(cat docs/public/api/*.md 2>/dev/null || true)" | |
| 40 | +if [[ -z "$DOC_BLOB" ]]; then | |
| 41 | + echo "verify-api-docs: docs/public/api/ is empty" >&2 | |
| 42 | + exit 2 | |
| 43 | +fi | |
| 44 | + | |
| 45 | +missing_count=0 | |
| 46 | +while IFS= read -r route; do | |
| 47 | + if ! grep -qF "$route" <<<"$DOC_BLOB"; then | |
| 48 | + if [[ "$missing_count" -eq 0 ]]; then | |
| 49 | + echo "verify-api-docs: the following routes are not documented under docs/public/api/:" >&2 | |
| 50 | + fi | |
| 51 | + echo " $route" >&2 | |
| 52 | + missing_count=$((missing_count + 1)) | |
| 53 | + fi | |
| 54 | +done < "$ROUTES_FILE" | |
| 55 | + | |
| 56 | +if [[ "$missing_count" -gt 0 ]]; then | |
| 57 | + echo "" >&2 | |
| 58 | + echo "Add a section that mentions the route literal verbatim." >&2 | |
| 59 | + exit 1 | |
| 60 | +fi | |
| 61 | + | |
| 62 | +echo "verify-api-docs: all ${route_count} routes documented" | |
scripts/verify-spdx-headers.shadded@@ -0,0 +1,65 @@ | ||
| 1 | +#!/usr/bin/env bash | |
| 2 | +# SPDX-License-Identifier: AGPL-3.0-or-later | |
| 3 | +# | |
| 4 | +# Every Go source file in this repo must carry an SPDX header | |
| 5 | +# identifying the license. The convention: | |
| 6 | +# | |
| 7 | +# // SPDX-License-Identifier: AGPL-3.0-or-later | |
| 8 | +# | |
| 9 | +# Exemptions: | |
| 10 | +# - sqlc-generated files under */sqlc/ — the sqlc tool emits a | |
| 11 | +# different header. We don't maintain those by hand. | |
| 12 | +# - Test fixtures under testdata/. | |
| 13 | +# - Vendored code (we don't vendor today; this is forward-looking). | |
| 14 | +# | |
| 15 | +# Run: ./scripts/verify-spdx-headers.sh | |
| 16 | + | |
| 17 | +set -euo pipefail | |
| 18 | + | |
| 19 | +ROOT="$(cd "$(dirname "$0")/.." && pwd)" | |
| 20 | +cd "$ROOT" | |
| 21 | + | |
| 22 | +WANT='// SPDX-License-Identifier: AGPL-3.0-or-later' | |
| 23 | +missing=() | |
| 24 | + | |
| 25 | +# Find every committed .go file outside the exemptions, then | |
| 26 | +# check the first 3 lines for the header. | |
| 27 | +while IFS= read -r f; do | |
| 28 | + case "$f" in | |
| 29 | + */sqlc/*) continue ;; | |
| 30 | + */testdata/*) continue ;; | |
| 31 | + */vendor/*) continue ;; | |
| 32 | + esac | |
| 33 | + if ! head -n 3 "$f" | grep -qF "$WANT"; then | |
| 34 | + missing+=("$f") | |
| 35 | + fi | |
| 36 | +done < <(git ls-files '*.go') | |
| 37 | + | |
| 38 | +if [[ ${#missing[@]} -gt 0 ]]; then | |
| 39 | + echo "verify-spdx-headers: the following files are missing the SPDX header:" >&2 | |
| 40 | + printf ' %s\n' "${missing[@]}" >&2 | |
| 41 | + echo "" >&2 | |
| 42 | + echo "Add this line at the top (line 1 ideally, line 3 max):" >&2 | |
| 43 | + echo " $WANT" >&2 | |
| 44 | + exit 1 | |
| 45 | +fi | |
| 46 | + | |
| 47 | +# Also enforce the same on shell scripts in scripts/ and deploy/. | |
| 48 | +sh_missing=() | |
| 49 | +SH_WANT='# SPDX-License-Identifier: AGPL-3.0-or-later' | |
| 50 | +while IFS= read -r f; do | |
| 51 | + if ! head -n 5 "$f" | grep -qF "$SH_WANT"; then | |
| 52 | + sh_missing+=("$f") | |
| 53 | + fi | |
| 54 | +done < <(git ls-files 'scripts/*.sh' 'deploy/**/*.sh') | |
| 55 | + | |
| 56 | +if [[ ${#sh_missing[@]} -gt 0 ]]; then | |
| 57 | + echo "verify-spdx-headers: the following shell scripts are missing the SPDX header:" >&2 | |
| 58 | + printf ' %s\n' "${sh_missing[@]}" >&2 | |
| 59 | + echo "" >&2 | |
| 60 | + echo "Add this line within the first 5 lines:" >&2 | |
| 61 | + echo " $SH_WANT" >&2 | |
| 62 | + exit 1 | |
| 63 | +fi | |
| 64 | + | |
| 65 | +echo "verify-spdx-headers: ok" | |