diff --git a/.github/workflows/no-ai-lines.yml b/.github/workflows/no-ai-lines.yml new file mode 100644 index 0000000..fef9cbb --- /dev/null +++ b/.github/workflows/no-ai-lines.yml @@ -0,0 +1,62 @@ +# Fails when a pull request or a push to main carries an AI session link, +# an AI co-author line or a "generated with" line, in the PR title, the PR +# body or any commit message. A pull request with more than 250 commits +# fails, because the API lists at most 250 and the rest cannot be checked. +name: no-ai-lines + +on: + pull_request: + types: [opened, edited, synchronize, reopened] + push: + branches: [main] + +permissions: + contents: read + pull-requests: read + +jobs: + check: + runs-on: ubuntu-latest + steps: + - name: Check for AI lines + env: + GH_TOKEN: ${{ github.token }} + REPO: ${{ github.repository }} + EVENT_NAME: ${{ github.event_name }} + PR_NUMBER: ${{ github.event.pull_request.number }} + run: | + set -euo pipefail + banned='claude-session:|claude\.ai/code|co-authored-by:.*(claude|anthropic|copilot|cursor|grok|gemini|openai|codex|chatgpt|gpt|aider|devin|windsurf|deepseek|qwen|kimi)|generated (with|by) .*(claude|ai\b)|🤖' + fail=0 + check() { + if grep -qiE "$banned" <<< "$2"; then + echo "::error::AI line found in $1" + echo "Matched in $1:" + grep -iE "$banned" <<< "$2" | sed 's/^/ /' + fail=1 + fi + } + if [ "$EVENT_NAME" = pull_request ]; then + total=$(jq -r '.pull_request.commits // 0' "$GITHUB_EVENT_PATH") + if [ "$total" -gt 250 ]; then + echo "::error::This pull request has $total commits. The API lists at most 250, so the commits could not all be checked." + exit 1 + fi + check "PR title" "$(jq -r '.pull_request.title // ""' "$GITHUB_EVENT_PATH")" + check "PR body" "$(jq -r '.pull_request.body // ""' "$GITHUB_EVENT_PATH")" + gh api --paginate "repos/$REPO/pulls/$PR_NUMBER/commits" \ + --jq '.[] | [.sha, (.commit.message | @base64)] | @tsv' > commits.tsv + while IFS=$'\t' read -r sha msg; do + check "commit $sha" "$(printf '%s' "$msg" | base64 -d)" + done < commits.tsv + else + jq -r '.commits[] | [.id, (.message | @base64)] | @tsv' "$GITHUB_EVENT_PATH" > commits.tsv + while IFS=$'\t' read -r sha msg; do + check "commit $sha" "$(printf '%s' "$msg" | base64 -d)" + done < commits.tsv + fi + if [ "$fail" -ne 0 ]; then + echo "Fix: remove the AI lines. Edit the PR title or body, or rewrite the commit message (git commit --amend, or git rebase -r with reword) and force-push the branch." + exit 1 + fi + echo "No AI lines found."