Three builder runs tonight carried a Co-Authored-By: Claude trailer despite bold instructions forbidding it. A rule in a prompt is not a control — this gate is. Flags commits in a PR's range carrying co-authored-by + (claude OR anthropic OR noreply@anthropic.com), case-insensitive, covering all observed spellings. Wired into CI (new attribution-trailer job) and pre-push hook. Mirrors existing lint scripts in structure and exit conventions. Pattern tested against all three variants plus a normal commit.
85 lines
3.6 KiB
Shell
Executable file
85 lines
3.6 KiB
Shell
Executable file
#!/bin/sh
|
|
# CI lint: flags Co-Authored-By trailers with Claude/Anthropic attribution in
|
|
# PR commits. The hive convention in /knowledge/hive-rules.md forbids these
|
|
# trailers — an attribution line in a prompt is not a control. This gate is.
|
|
#
|
|
# Emits a CI error annotation per offending commit and exits 1 if any commit
|
|
# in the PR's range carries a prohibited trailer. Runs as its own CI job and
|
|
# IS a required check on the forge (branch protection) — a hit blocks merge.
|
|
#
|
|
# Pattern: case-insensitive `co-authored-by` followed by `claude`, `anthropic`,
|
|
# or `noreply@anthropic.com` anywhere in the line. Covers variants seen in the
|
|
# wild: `Co-Authored-By: Claude`, `Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>`.
|
|
#
|
|
# Scope: commits in the PR's range (BASE..HEAD), not the entire history — old
|
|
# commits must not fail the build. In CI, BASE is the target branch
|
|
# (GITHUB_BASE_REF / github.event.pull_request.base.ref in GH Actions; similar
|
|
# vars in Forgejo). Falls back to origin/main for local runs.
|
|
set -eu
|
|
|
|
# Determine base ref: in CI (pull_request event), use the PR's base branch;
|
|
# locally, default to origin/main. CI env vars: GITHUB_BASE_REF (GitHub
|
|
# Actions), similar for Forgejo/Gitea (not standardized across runners, so we
|
|
# check both common patterns).
|
|
if [ -n "${GITHUB_BASE_REF:-}" ]; then
|
|
# GitHub Actions / Forgejo Actions pull_request context
|
|
base="origin/${GITHUB_BASE_REF}"
|
|
elif [ -n "${CI_COMMIT_REF_NAME:-}" ]; then
|
|
# GitLab-style variable (some Forgejo setups)
|
|
base="origin/main"
|
|
else
|
|
# Local run or unknown CI — default to origin/main
|
|
base="origin/main"
|
|
fi
|
|
|
|
# Ensure the base ref exists (in CI, actions/checkout@v3 fetches the PR head
|
|
# but may not fetch the base branch by default — fetch it if missing).
|
|
if ! git rev-parse --verify "$base" >/dev/null 2>&1; then
|
|
# Extract branch name and fetch it
|
|
branch="${base#origin/}"
|
|
git fetch origin "$branch" 2>/dev/null || true
|
|
# If still missing, fall back to main
|
|
if ! git rev-parse --verify "$base" >/dev/null 2>&1; then
|
|
base="origin/main"
|
|
git fetch origin main 2>/dev/null || base="HEAD~10"
|
|
fi
|
|
fi
|
|
|
|
# Get all commits in the PR's range. `--reverse` lists oldest first (clearer
|
|
# for annotation output). `--format=%H` gets the full commit sha.
|
|
commits="$(git log --reverse --format='%H' "${base}..HEAD" 2>/dev/null || true)"
|
|
|
|
if [ -z "$commits" ]; then
|
|
# No commits in range — either not on a PR branch, or nothing new. Pass.
|
|
exit 0
|
|
fi
|
|
|
|
# Check each commit's full message for the prohibited trailer pattern.
|
|
# Case-insensitive grep for `co-authored-by` + any of `claude`, `anthropic`,
|
|
# `noreply@anthropic.com`.
|
|
hits=""
|
|
for sha in $commits; do
|
|
msg="$(git log -1 --format='%B' "$sha")"
|
|
# Match case-insensitive co-authored-by AND (claude OR anthropic OR noreply@anthropic.com)
|
|
# Use grep -E with -i for case-insensitive matching
|
|
if printf '%s\n' "$msg" | grep -qiE 'co-authored-by.*claude|co-authored-by.*anthropic|co-authored-by.*noreply@anthropic\.com'; then
|
|
# Found a hit — build annotation
|
|
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
|