check-attribution-trailers.sh used `|| true` on `git log`'s exit status, so a hard failure (bad range, unborn HEAD) and an empty-but-successful range were indistinguishable — both fell through to the same `-z "$commits"` exit-0 path. Capture the status via the `if` guard (exempt from set -e on purpose) and exit 1 on a real git log failure. check-issue-refs.sh piped `git ls-files | xargs grep | grep -v lint:allow`, then swallowed the final exit code with `|| true`. Worse: xargs itself collapses grep's exit 1 (no match) and exit 2+ (real error, e.g. an unreadable file) into the same xargs(1) status (123 either way), so even capturing that status can't tell them apart. Switched to `git grep`, which runs once over the tracked set and hands back its own exit status untouched (0 matched / 1 no match / 2+ error) — then branch on that status explicitly for both the scan and the lint:allow filter step. Refs #4439, #4442
75 lines
2.9 KiB
Shell
Executable file
75 lines
2.9 KiB
Shell
Executable file
#!/bin/sh
|
|
# 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 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 <noreply@anthropic.com>` 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.
|
|
set -eu
|
|
|
|
trailer_re='^[[:space:]]*co-authored-by:.*(claude|anthropic|noreply@anthropic\.com)'
|
|
|
|
if [ -n "${GITHUB_BASE_REF:-}" ]; then
|
|
base="origin/${GITHUB_BASE_REF}"
|
|
else
|
|
base="origin/main"
|
|
fi
|
|
|
|
# actions/checkout@v3 fetches the PR head but may not fetch the base branch
|
|
if ! git rev-parse --verify "$base" >/dev/null 2>&1; then
|
|
branch="${base#origin/}"
|
|
git fetch origin "$branch" 2>/dev/null || true
|
|
if ! git rev-parse --verify "$base" >/dev/null 2>&1; then
|
|
base="origin/main"
|
|
if ! git fetch origin main 2>/dev/null || ! git rev-parse --verify "$base" >/dev/null 2>&1; then
|
|
echo 'check-attribution-trailers: base ref unresolvable — cannot fetch origin/main to diff against, refusing to guess a commit range' >&2
|
|
exit 1
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
# `|| true` here used to erase git log's own exit status, so a genuine
|
|
# failure (bad range, corrupt ref) and a merely-empty range read the same:
|
|
# both fell through to `-z "$commits"` and exited 0 "clean". Capture the
|
|
# status via the `if` guard instead — that's exempt from `set -e` on
|
|
# purpose — so a failure exits loudly and an empty-but-successful range
|
|
# still means "no commits, nothing to check".
|
|
if ! commits="$(git log --reverse --format='%H' "${base}..HEAD")"; then
|
|
echo "check-attribution-trailers: git log failed for range ${base}..HEAD — see error above" >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [ -z "$commits" ]; then
|
|
exit 0
|
|
fi
|
|
|
|
hits=""
|
|
for sha in $commits; do
|
|
msg="$(git log -1 --format='%B' "$sha")"
|
|
if printf '%s\n' "$msg" | grep -qiE "$trailer_re"; then
|
|
subject="$(git log -1 --format='%s' "$sha")"
|
|
hits="${hits}${sha}|${subject}
|
|
"
|
|
fi
|
|
done
|
|
|
|
if [ -n "$hits" ]; then
|
|
printf '%s' "$hits" | while IFS='|' read -r sha subject; do
|
|
[ -z "$sha" ] && continue
|
|
short_sha="$(printf '%s' "$sha" | cut -c1-8)"
|
|
printf '::error title=Attribution trailer found in %s::%s — commit carries a Co-Authored-By trailer with Claude/Anthropic attribution, forbidden by /knowledge/hive-rules.md\n' \
|
|
"$short_sha" "$subject"
|
|
done
|
|
count="$(printf '%s' "$hits" | grep -c '|' || true)"
|
|
printf 'check-attribution-trailers: %s commit(s) with prohibited trailers found\n' "$count" >&2
|
|
exit 1
|
|
fi
|
|
|
|
exit 0
|