Build ErsatzTV Image / Docs update reminder (pull_request) Successful in 10s
Build ErsatzTV Image / decisions.md append-only (pull_request) Successful in 11s
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (pull_request) Successful in 14s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 5m9s
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 6m0s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
Adversarial review (PR #305, MERGEABLE) nits: - H3: `grep -iE` so a root `Screenshot.PNG` is caught too (was lowercase-only). - decisions-guard.sh: comment the trailing-newline assumption (dropping the final newline would make git render the next append as a last-line modify -> false-block; self-correcting via [decisions-edit], .editorconfig enforces the newline). - docs: clarify CI is PR-wide (`range`) vs Husky per-commit (`staged`) — shared detection logic, deliberately different granularity; local hook is the stricter gate. Replaces the slightly-overstated "can't drift" wording. Touches the committed H9 decisions.md entry, hence the [decisions-edit] token. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
55 lines
2.6 KiB
Bash
Executable File
55 lines
2.6 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# ersatztv#303 H9 — docs/decisions.md is append-only. This blocks a commit / PR that DELETES or
|
|
# MODIFIES an existing line of that file; pure INSERTIONS anywhere are always allowed (adding a new
|
|
# entry inserts a TOC line near the top AND appends a block at the bottom — both are insertions, so
|
|
# numstat reports 0 deleted lines). A genuine factual fix to a past entry is the one legitimate edit:
|
|
# put the literal token [decisions-edit] in the commit message to override.
|
|
#
|
|
# Fail-open: any tooling trouble (unknown mode, non-numeric numstat, missing refs) -> allow. The point
|
|
# is to catch the accidental rewrite-history case, never to wedge a legitimate commit.
|
|
#
|
|
# Assumes decisions.md ends with a trailing newline (it does; .editorconfig enforces it). If that final
|
|
# newline were ever dropped, git would render the next append as a modify of the last line (deleted=1)
|
|
# and this would false-block the append until the author adds [decisions-edit] — cheap and self-correcting.
|
|
#
|
|
# Modes:
|
|
# staged <msgfile> pre-commit/commit-msg — staged diff vs HEAD; trailer read from <msgfile>
|
|
# range <base> <head> CI (PR) — merge-base diff base...head; trailer scanned across base..head msgs
|
|
set -euo pipefail
|
|
|
|
FILE="docs/decisions.md"
|
|
mode="${1:-}"
|
|
|
|
case "$mode" in
|
|
staged)
|
|
deleted=$(git diff --cached --numstat -- "$FILE" 2>/dev/null | awk '{print $2}' | head -1)
|
|
msg=$(cat "${2:-/dev/null}" 2>/dev/null || true)
|
|
;;
|
|
range)
|
|
base="${2:-}"; head="${3:-}"
|
|
[ -n "$base" ] && [ -n "$head" ] || exit 0 # missing refs -> fail-open
|
|
deleted=$(git diff --numstat "$base...$head" -- "$FILE" 2>/dev/null | awk '{print $2}' | head -1)
|
|
msg=$(git log --format='%B' "$base..$head" 2>/dev/null || true)
|
|
;;
|
|
*)
|
|
exit 0 # unknown mode -> fail-open
|
|
;;
|
|
esac
|
|
|
|
# Empty (no change to the file) or '-' (binary) -> treat as 0 (fail-open / nothing to guard).
|
|
deleted="${deleted:-0}"
|
|
case "$deleted" in ''|*[!0-9]*) deleted=0 ;; esac
|
|
[ "$deleted" -gt 0 ] || exit 0 # pure insertion / no change -> allow
|
|
|
|
# Explicit override for a documented factual fix.
|
|
if printf '%s' "$msg" | grep -qiF '[decisions-edit]'; then
|
|
exit 0
|
|
fi
|
|
|
|
{
|
|
echo "decisions-guard (ersatztv#303 H9): docs/decisions.md is append-only — this change deletes/modifies ${deleted} existing line(s)."
|
|
echo " Append new entries at the bottom (plus a TOC line in the Index); do not rewrite settled entries."
|
|
echo " To fix a genuine factual error in a past entry, add the token [decisions-edit] to the commit message."
|
|
} >&2
|
|
exit 1
|