mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-09 21:10:59 +00:00
chore(ci): catch OpenAPI drift via a pre-commit hook
v1.4.30 PR push failed CI on first attempt because the two new MeasurementType enums weren't reflected in `docs/api/openapi.yaml`. A separate `chore(openapi): regenerate the public spec` commit was needed before the checks turned green. This hook catches the drift at commit time locally so the contributor sees the regeneration prompt before pushing. New `.githooks/pre-commit`: - Skips when no relevant files (`src/lib/openapi/`, `src/lib/validations/`, `src/app/api/`, the generator script itself) are staged. - Runs `pnpm openapi:check` quietly; exits fast on match. - On drift, runs `pnpm openapi:generate`, re-stages the regenerated `docs/api/openapi.yaml`, and continues the commit. - Skips in CI so the existing `pnpm openapi:check` job in `.github/workflows/security.yml` stays the server-side authoritative gate. `scripts/install-hooks.sh` flips `git config core.hooksPath` to `.githooks/` — idempotent, one-shot per fresh clone. AGENTS.md adds the install command to the contributor workflow section.
This commit is contained in:
1 parent
477e57f1f1
commit
8e432b3172
3 files changed
+91
No files matched your search
Executable
+60
@@ -0,0 +1,60 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# v1.4.31 — Pre-commit hook: OpenAPI spec drift check.
|
||||
#
|
||||
# v1.4.30 PR push failed CI on first attempt because the two new
|
||||
# MeasurementType enums (WALKING_STEADINESS, AUDIO_EXPOSURE_EVENT)
|
||||
# weren't reflected in `docs/api/openapi.yaml`. A separate
|
||||
# `chore(openapi): regenerate the public spec` commit was needed
|
||||
# before the checks turned green. This hook catches that drift
|
||||
# locally so a contributor sees the regeneration prompt at commit
|
||||
# time instead of after a CI round-trip.
|
||||
#
|
||||
# Behaviour:
|
||||
# - Runs only when the staged diff touches a file that is likely
|
||||
# to invalidate the OpenAPI spec (Zod schemas under
|
||||
# src/lib/openapi/ or src/lib/validations/, or API route
|
||||
# handlers under src/app/api/).
|
||||
# - Reruns `pnpm openapi:generate` quietly and re-stages the
|
||||
# regenerated spec if the file diverged.
|
||||
# - Exits non-zero only if regeneration fails entirely.
|
||||
#
|
||||
# Install via `bash scripts/install-hooks.sh` (idempotent) or
|
||||
# `git config core.hooksPath .githooks` from the repo root.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Skip in non-interactive contexts (CI doesn't commit through this
|
||||
# path anyway — the security.yml workflow runs `pnpm openapi:check`
|
||||
# server-side). The check still fires under `git commit` from a
|
||||
# terminal.
|
||||
if [ -n "${CI:-}" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Only run when relevant files are staged. The spec is generated
|
||||
# from Zod schemas + route registrations; nothing else can
|
||||
# legitimately move it.
|
||||
staged_files="$(git diff --cached --name-only --diff-filter=ACMRT)"
|
||||
if ! echo "$staged_files" | grep -qE '^(src/(lib/openapi|lib/validations|app/api)/|scripts/generate-openapi\.ts$)'; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "[openapi pre-commit] Checking the spec against the staged schema changes…"
|
||||
|
||||
# Quick path: run the comparison-only `check-openapi.ts`. If the
|
||||
# generated and committed forms match, exit fast.
|
||||
if pnpm --silent openapi:check >/dev/null 2>&1; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "[openapi pre-commit] Drift detected — regenerating docs/api/openapi.yaml."
|
||||
if ! pnpm --silent openapi:generate; then
|
||||
echo "::error::pnpm openapi:generate failed. Fix the underlying error before committing."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Re-stage the regenerated spec so the commit-in-flight carries it.
|
||||
git add docs/api/openapi.yaml
|
||||
echo "[openapi pre-commit] Spec regenerated and re-staged. Continuing the commit."
|
||||
exit 0
|
||||
@@ -50,6 +50,11 @@ pnpm lint # ESLint
|
||||
pnpm format # Prettier format
|
||||
pnpm format:check # Prettier check
|
||||
pnpm typecheck # TypeScript strict check
|
||||
pnpm openapi:generate # Regenerate docs/api/openapi.yaml from Zod schemas
|
||||
pnpm openapi:check # Verify the spec is in sync (CI gate)
|
||||
|
||||
# Git hooks (one-time per clone)
|
||||
bash scripts/install-hooks.sh # Activates .githooks/, including the OpenAPI pre-commit check
|
||||
|
||||
# Tests
|
||||
pnpm test # Run all tests (vitest)
|
||||
|
||||
Executable
+26
@@ -0,0 +1,26 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# v1.4.31 — install the repository git hooks.
|
||||
#
|
||||
# Run once per fresh clone:
|
||||
#
|
||||
# bash scripts/install-hooks.sh
|
||||
#
|
||||
# Idempotent. The script flips `git config core.hooksPath` to
|
||||
# `.githooks/` so every hook under that directory becomes active.
|
||||
# Currently installed: `pre-commit` (OpenAPI spec drift check).
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
repo_root="$(git rev-parse --show-toplevel)"
|
||||
cd "$repo_root"
|
||||
|
||||
current_path="$(git config --local --get core.hooksPath 2>/dev/null || true)"
|
||||
if [ "$current_path" = ".githooks" ]; then
|
||||
echo "core.hooksPath already points at .githooks — nothing to do."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
git config --local core.hooksPath .githooks
|
||||
echo "Set core.hooksPath = .githooks. Installed hooks:"
|
||||
ls -1 .githooks | grep -v '^README' | sed 's/^/ /' || true
|
||||
Reference in new issue
Block a user