Files
HealthLog/.githooks/pre-commit
T
Marc-André Bombeck 8e432b3172 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.
2026-05-16 14:08:54 +02:00

61 lines
2.3 KiB
Bash
Executable File

#!/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