#!/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 ` 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