From 27ca8d4f4a728d7ff7edd46cd205faa81bb3b0b1 Mon Sep 17 00:00:00 2001 From: theDancingDeveloper Date: Fri, 9 Oct 2026 15:36:13 +0000 Subject: [PATCH 1/2] Fail clippy only on diagnostics overlapping a changed hunk A file-level gate still inherits a file's whole upstream lint debt: a PR editing one line of message_processor.rs was failed for a let_and_return elsewhere in that file. The gate now reads the changed line ranges from git diff -U0 and fails only when a warning or error's primary span overlaps one of them. A pure deletion (count 0) overlaps nothing. --- .github/workflows/fork-ci.yml | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/.github/workflows/fork-ci.yml b/.github/workflows/fork-ci.yml index 7e0dac9d6428..a39914dc5c78 100644 --- a/.github/workflows/fork-ci.yml +++ b/.github/workflows/fork-ci.yml @@ -155,34 +155,40 @@ jobs: cargo fmt -p "$crate" -- --check done <<< "${{ steps.crates.outputs.list }}" - # -D warnings would fail the PR on upstream lint debt in files it never + # -D warnings would fail the PR on upstream lint debt in lines it never # touched, and a fork patch must not have to fix that. Clippy runs without - # it, and only a warning or error whose primary span is in a file this PR - # changed fails the build. Compile errors are fatal regardless, which + # it, and only a warning or error whose primary span overlaps a hunk this + # PR changed fails the build. Compile errors are fatal regardless, which # `cargo check` above already enforces. - name: cargo clippy shell: bash run: | set -euo pipefail - printf '%s\n' "${{ steps.crates.outputs.files }}" | sed 's|^codex-rs/||' > /tmp/changed-files + base="$(git merge-base HEAD origin/fork/main 2>/dev/null || git merge-base HEAD fork/main)" + # One changed line range per hunk, as "file start count" with the + # codex-rs/ prefix stripped. A count of 0 is a pure deletion. + git diff -U0 "$base"...HEAD -- codex-rs \ + | awk '/^diff --git/{file=$4; sub("^b/codex-rs/","",file)} + /^@@/{split($3,a,","); sub("^+","",a[1]); print file, a[1], (a[2] == "" ? 1 : a[2])}' \ + > /tmp/changed-lines while IFS= read -r crate; do [[ -z "$crate" ]] && continue cargo clippy -p "$crate" --all-targets --message-format=json -- -W clippy::all \ > "/tmp/clippy-${crate}.json" || true - # --slurpfile yields an array of strings. Iterating each element - # again (`[$changed[][]]`) is a jq runtime error, and jq exits 0 on - # those, so the gate would pass every PR. `any` compares strings - # directly. stderr goes to a file synchronously: a process - # substitution can still be writing when the check runs, which - # would let a jq error pass again. + # stderr goes to a file synchronously. jq exits 0 on a runtime + # error, so without this check a broken filter would pass the gate. rm -f /tmp/jq-err - offenders="$(jq -r --slurpfile changed <(jq -R . /tmp/changed-files) ' + offenders="$(jq -r --slurpfile lines <(jq -R 'split(" ") | {file:.[0], start:.[1]|tonumber, count:.[2]|tonumber}' /tmp/changed-lines) ' select(.reason == "compiler-message") | .message | select(.level == "warning" or .level == "error") | . as $m | ($m.spans[] | select(.is_primary)) as $s - | select(any($changed[]; . == $s.file_name)) + | select(any($lines[]; + .file == $s.file_name + and .count > 0 + and $s.line_start <= (.start + .count - 1) + and $s.line_end >= .start)) | "\($s.file_name):\($s.line_start):\($s.column_start): \($m.level): \($m.message)" ' "/tmp/clippy-${crate}.json" 2>/tmp/jq-err)" if [[ -s /tmp/jq-err ]]; then @@ -192,7 +198,7 @@ jobs: fi if [[ -n "$offenders" ]]; then echo "$offenders" - echo "clippy findings in files this PR changes" >&2 + echo "clippy findings in lines this PR changes" >&2 exit 1 fi done <<< "${{ steps.crates.outputs.list }}" From e874aeca7353127e098f378764138608c03dc34c Mon Sep 17 00:00:00 2001 From: theDancingDeveloper Date: Fri, 9 Oct 2026 15:38:49 +0000 Subject: [PATCH 2/2] Use awk regex literals so mawk accepts the diff parse --- .github/workflows/fork-ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/fork-ci.yml b/.github/workflows/fork-ci.yml index a39914dc5c78..5640c121815b 100644 --- a/.github/workflows/fork-ci.yml +++ b/.github/workflows/fork-ci.yml @@ -168,8 +168,8 @@ jobs: # One changed line range per hunk, as "file start count" with the # codex-rs/ prefix stripped. A count of 0 is a pure deletion. git diff -U0 "$base"...HEAD -- codex-rs \ - | awk '/^diff --git/{file=$4; sub("^b/codex-rs/","",file)} - /^@@/{split($3,a,","); sub("^+","",a[1]); print file, a[1], (a[2] == "" ? 1 : a[2])}' \ + | awk '/^diff --git/{file=$4; sub(/^b\/codex-rs\//, "", file)} + /^@@/{split($3,a,","); sub(/^\+/, "", a[1]); print file, a[1], (a[2] == "" ? 1 : a[2])}' \ > /tmp/changed-lines while IFS= read -r crate; do [[ -z "$crate" ]] && continue