diff --git a/scripts/check-attribution-trailers.sh b/scripts/check-attribution-trailers.sh index 1eb96dbc..300e7e88 100755 --- a/scripts/check-attribution-trailers.sh +++ b/scripts/check-attribution-trailers.sh @@ -2,14 +2,51 @@ # Flags Co-Authored-By trailers with Claude/Anthropic attribution in PR commits. # The hive convention in /knowledge/hive-rules.md forbids these trailers. # -# Pattern is loose (case-insensitive, matches `claude`, `anthropic`, or -# `noreply@anthropic.com` anywhere after `co-authored-by`) to cover variants -# like `Co-Authored-By: Claude Sonnet 4.5 `. +# Pattern is anchored to an actual git trailer line — `^[[:space:]]*co-authored-by:` +# — not any mention of the phrase, then requires the value name `claude`, +# `anthropic`, or `noreply@anthropic.com`. Matching is case-insensitive and +# per-line (git trailers are one `Key: value` per line) to cover variants like +# `Co-Authored-By: Claude Sonnet 4.5 ` while letting a +# commit subject/body that merely *discusses* the trailer (e.g. this script's +# own "add CI lint for Co-Authored-By/Claude/Anthropic trailers") pass. # # Scope is the PR's commits (BASE..HEAD), not entire history — old commits # must not fail the build. +# +# Run with --self-test to check the pattern against fixtures instead of +# scanning git history. set -eu +trailer_re='^[[:space:]]*co-authored-by:.*(claude|anthropic|noreply@anthropic\.com)' + +if [ "${1:-}" = "--self-test" ]; then + fail=0 + + assert_match() { + if ! printf '%s\n' "$2" | grep -qiE "$trailer_re"; then + echo "self-test FAILED (expected match): $1" >&2 + fail=1 + fi + } + assert_no_match() { + if printf '%s\n' "$2" | grep -qiE "$trailer_re"; then + echo "self-test FAILED (expected no match): $1" >&2 + fail=1 + fi + } + + assert_match "bare trailer" 'Co-Authored-By: Claude' + assert_match "full trailer with email" 'Co-Authored-By: Claude Sonnet 4.5 ' + assert_match "lowercase trailer" 'co-authored-by: claude' + assert_no_match "ordinary message" 'fix: correct off-by-one in parser' + assert_no_match "prose mention of the trailer" 'ops: add CI lint for Co-Authored-By/Claude/Anthropic trailers' + + if [ "$fail" -eq 0 ]; then + echo "self-test OK" + fi + exit "$fail" +fi + if [ -n "${GITHUB_BASE_REF:-}" ]; then base="origin/${GITHUB_BASE_REF}" else @@ -35,7 +72,7 @@ fi hits="" for sha in $commits; do msg="$(git log -1 --format='%B' "$sha")" - if printf '%s\n' "$msg" | grep -qiE 'co-authored-by.*claude|co-authored-by.*anthropic|co-authored-by.*noreply@anthropic\.com'; then + if printf '%s\n' "$msg" | grep -qiE "$trailer_re"; then subject="$(git log -1 --format='%s' "$sha")" hits="${hits}${sha}|${subject} "