Files
docker_practice/.github/workflows/identity-guard.yaml
T
yeasy 38c89065b6 ci: fix Identity Guard trigger — it was never actually running
上一版给 push 只写了 tags-ignore: ['**'],本意是让发布 tag 不再触发一次
重复的全量审计。但 GitHub 的过滤规则是"只给了 tag 过滤器,就只在 tag 上跑",
因此这条配置的真实含义变成"只跑 tag,但忽略所有 tag" —— 工作流一次都不会触发。

症状很有迷惑性:工作流在 API 里状态是 active、名字也正常显示,
只是 runs 数恒为 0,同一次推送里 CI 和 Preview 都正常跑。
改为 branches: ['**'],覆盖全部分支且天然排除 tag,达到原本的意图。

教训记一句:光看"工作流已注册/active"不能算验证,必须确认它真的产生了 run。
2026-07-22 15:37:50 -07:00

137 lines
6.4 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
name: Identity Guard
# 拒绝把 AI 助手写成提交作者/提交者或写进 Co-authored-by 尾注
# 本地 .git/hooks 不随仓库分发云端会话推上来的提交不受其保护
# 因此这道检查必须放在服务端
on:
push:
# 'branches: [**]' 覆盖所有分支且天然排除 tag
# 注意不能写成只有 tags-ignoreGitHub 的规则是"只给了 tag 过滤器就只跑 tag"
# 于是 tags-ignore: ['**'] 等于"只跑 tag,但忽略所有 tag" —— 工作流永不触发
branches: ['**']
pull_request:
workflow_dispatch:
permissions: {}
jobs:
check-commit-identity:
permissions:
contents: read
runs-on: ubuntu-24.04
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
persist-credentials: false
- name: Reject AI assistant identities and co-author trailers
env:
EVENT_NAME: ${{ github.event_name }}
BEFORE_SHA: ${{ github.event.before }}
HEAD_SHA: ${{ github.sha }}
PR_BASE_SHA: ${{ github.event.pull_request.base.sha }}
PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }}
run: |
set -euo pipefail
ZERO=0000000000000000000000000000000000000000
US=$'\037' # 用不可能出现在姓名/邮箱里的 unit separator 作分隔符
# 确定扫描范围三种情况
# 1. pull_requestbase..head只看本 PR 新增的提交
# 2. push before 指向一个真实存在的提交before..head
# 3. 其余新建分支 before 0force-push before 不可达
# workflow_dispatch):退化为全量审计扫描 HEAD 的完整历史
if [ "$EVENT_NAME" = "pull_request" ]; then
RANGE="${PR_BASE_SHA}..${PR_HEAD_SHA}"
elif [ "$EVENT_NAME" = "push" ] \
&& [ -n "${BEFORE_SHA:-}" ] \
&& [ "$BEFORE_SHA" != "$ZERO" ] \
&& git cat-file -e "${BEFORE_SHA}^{commit}" 2>/dev/null; then
RANGE="${BEFORE_SHA}..${HEAD_SHA}"
else
RANGE="$HEAD_SHA"
fi
echo "Scanning range: ${RANGE}"
# 只有"确实没有 head 提交"例如删除分支的 push才允许放行
# 其余任何解析不出范围的情况一律 fail closed不能静默通过
if [ -z "${HEAD_SHA:-}" ] || [ "$HEAD_SHA" = "$ZERO" ]; then
echo "No head commit (branch deletion?); nothing to check."
exit 0
fi
if ! COMMITS="$(git rev-list "$RANGE" 2>&1)"; then
echo "::error::cannot resolve commit range ${RANGE}: ${COMMITS}"
exit 1
fi
COUNT="$(printf '%s' "$COMMITS" | grep -c . || true)"
echo "Commits in range: ${COUNT}"
if [ "$COUNT" -eq 0 ]; then
echo "No commits to check."
exit 0
fi
# 身份判定刻意收窄避免误伤
# - 邮箱在 AI 厂商域名下含子域
# - 姓名恰好等于助手名因此真人 "Claude Dubois" 不受影响
# 正文里出现 Claude/Anthropic 是完全合法的本仓库群里有专讲 Claude
# 的书),所以只检查身份字段与 Co-authored-by 尾注绝不扫描自由文本
BAD_EMAIL='@([a-z0-9.-]+\.)?(anthropic|openai)\.com$'
BAD_NAME='^(claude([ ._-]?code)?|codex|chatgpt|copilot|anthropic|openai)(\[bot\])?$'
BAD_TRAILER='^[[:space:]]*co-authored-by:.*(claude|anthropic|codex|copilot|openai)'
# 粗筛BAD_EMAIL BAD_NAME 命中的行必然含下列词之一
# 因此它是二者的超集用来跳过绝大多数干净提交不会漏判
BAD_ANY='(claude|anthropic|codex|chatgpt|copilot|openai)'
failed=0
# 身份检查命令替换先落地set -e 才能捕获 git 失败
# while 循环用 here-string 喂数据避免管道子 shell 吞掉 failed 赋值
IDENTS="$(git log --format="%H${US}%an${US}%ae${US}%cn${US}%ce" "$RANGE")"
# here-string 而不是管道set -o pipefail grep -q 命中即退出会让
# 左侧进程收到 SIGPIPE整条管道返回非 0if 就会跳过检查——
# 而且只在输入大到写不进管道缓冲区时才发生是典型的隐性漏判
if grep -qiE "$BAD_ANY" <<< "$IDENTS"; then
while IFS="$US" read -r sha an ae cn ce; do
[ -n "$sha" ] || continue
for role in author committer; do
if [ "$role" = author ]; then name="$an"; mail="$ae"; else name="$cn"; mail="$ce"; fi
lname="$(printf '%s' "$name" | tr '[:upper:]' '[:lower:]')"
lmail="$(printf '%s' "$mail" | tr '[:upper:]' '[:lower:]')"
if printf '%s' "$lmail" | grep -qE "$BAD_EMAIL" \
|| printf '%s' "$lname" | grep -qE "$BAD_NAME"; then
echo "::error::${sha} ${role} identity is an AI assistant: ${name} <${mail}>"
failed=1
fi
done
done <<< "$IDENTS"
fi
# Co-authored-by 尾注检查行首锚定并要求冒号避免匹配正文叙述)。
# 先用一次 git log 流式扫全部正文只有确实命中时才逐个提交定位
# 否则全量审计要为每个提交起一个 git 子进程1500+ 提交约 50 )。
BODIES="$(git log --format='%B' "$RANGE")"
if grep -qiE "$BAD_TRAILER" <<< "$BODIES"; then
while read -r sha; do
[ -n "$sha" ] || continue
body="$(git log -1 --format='%B' "$sha")"
if printf '%s' "$body" | grep -qiE "$BAD_TRAILER"; then
echo "::error::${sha} has a Co-authored-by trailer referencing an AI assistant"
printf '%s' "$body" | grep -iE "$BAD_TRAILER" | sed 's/^/ /'
failed=1
fi
done <<< "$COMMITS"
fi
if [ "$failed" -ne 0 ]; then
echo ""
echo "AI assistant attribution found in the commits above."
echo "Rewrite them before pushing, e.g.:"
echo " git rebase -i --exec 'git commit --amend --no-edit --reset-author' <base>"
exit 1
fi
echo "OK: no AI assistant identity or Co-authored-by trailer in ${COUNT} commit(s)."