diff --git a/.githooks/pre-commit b/.githooks/pre-commit new file mode 100755 index 0000000..f713676 --- /dev/null +++ b/.githooks/pre-commit @@ -0,0 +1,40 @@ +#!/usr/bin/env bash +# Keep docs/traceability.md in step with the tags in the tree. +# +# The gate regenerates the matrix in CI and fails if the result differs from +# what is committed. That is the right check — a matrix that disagrees with the +# tree is worse than none, because it is read as current — but it fails *after* +# a push, on a commit that is otherwise fine, and it has now done so on six +# commits in a row because adding a `TRACES:` tag and regenerating the matrix +# are two actions and only the first is on anyone's mind. +# +# So it happens here instead, where the tags are being changed. +# +# Only when something that can carry a tag is staged: a commit touching +# workflows, packaging or the matrix itself pays nothing. +set -euo pipefail + +staged="$(git diff --cached --name-only --diff-filter=ACMR)" +if ! grep -qE '\.(rs|slint|yaml|md)$' <<< "${staged}"; then + exit 0 +fi +# The matrix is generated from the tree, so regenerating it because it was +# itself edited would be circular. +if [ "$(tr -d '[:space:]' <<< "${staged}")" = "docs/traceability.md" ]; then + exit 0 +fi + +repo="$(git rev-parse --show-toplevel)" +cd "${repo}" + +# Quiet unless it has something to say. A hook that prints on every commit is +# a hook people start passing --no-verify to. +if ! cargo run -q -p traceability -- report >/dev/null 2>&1; then + echo "pre-commit: could not run the traceability report; leaving the matrix alone" >&2 + exit 0 +fi + +if ! git diff --quiet -- docs/traceability.md; then + git add docs/traceability.md + echo "pre-commit: regenerated docs/traceability.md and staged it" +fi