From c4153a91012d16d102309a81dd3878b410ae4165 Mon Sep 17 00:00:00 2001 From: razo Date: Mon, 2 Nov 2026 22:39:09 +0100 Subject: [PATCH 1/2] ci: add no-ai-lines check --- .github/workflows/no-ai-lines.yml | 56 +++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 .github/workflows/no-ai-lines.yml diff --git a/.github/workflows/no-ai-lines.yml b/.github/workflows/no-ai-lines.yml new file mode 100644 index 0000000..bd14d37 --- /dev/null +++ b/.github/workflows/no-ai-lines.yml @@ -0,0 +1,56 @@ +# 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. The pattern is the same +# as tools/no-ai-lines/check.sh; test_check.py asserts the two copies match. +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)|generated (with|by) .*(claude|ai\b)|🤖' + fail=0 + check() { + if printf '%s\n' "$2" | grep -qiE "$banned"; then + echo "::error::AI line found in $1" + echo "Matched in $1:" + printf '%s\n' "$2" | grep -iE "$banned" | sed 's/^/ /' + fail=1 + fi + } + if [ "$EVENT_NAME" = pull_request ]; then + 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." From 12149b10e1c14ee528c52ade1010a9f4949e9fe3 Mon Sep 17 00:00:00 2001 From: "jac-made[bot]" <350467601+jac-made[bot]@users.noreply.github.com> Date: Mon, 2 Nov 2026 23:07:47 +0100 Subject: [PATCH 2/2] ci: update no-ai-lines check --- .github/workflows/no-ai-lines.yml | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/.github/workflows/no-ai-lines.yml b/.github/workflows/no-ai-lines.yml index bd14d37..fef9cbb 100644 --- a/.github/workflows/no-ai-lines.yml +++ b/.github/workflows/no-ai-lines.yml @@ -1,6 +1,7 @@ # 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. The pattern is the same -# as tools/no-ai-lines/check.sh; test_check.py asserts the two copies match. +# 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: @@ -25,17 +26,22 @@ jobs: 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)|generated (with|by) .*(claude|ai\b)|🤖' + 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 printf '%s\n' "$2" | grep -qiE "$banned"; then + if grep -qiE "$banned" <<< "$2"; then echo "::error::AI line found in $1" echo "Matched in $1:" - printf '%s\n' "$2" | grep -iE "$banned" | sed 's/^/ /' + 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" \