Skip to content

Commit fa91974

Browse files
committed
ci: require the BREAKING marker for reference docs only when lines change
The protected-files check fires on any diff under `docs/reference/rc/`, including a pure addition. Documenting a new command is an addition, and `AGENTS.md` asks for exactly that as step 2 of the Breaking Change process — so the check made every additive PR assert in its description that it was breaking, which is both untrue and the kind of marker that stops meaning anything once it is routine. The reference paths now need the marker only when existing lines move: a rewritten sentence, a removed flag, a deleted page. Rename detection is off on purpose, so moving a page counts as a rewrite — where a reader or a link lands does change. The schema, exit-code and config files keep the old rule. A new exit code or config field extends a contract even when nothing existing moves, so an addition there is exactly what the marker is for. Also collects failures instead of exiting on the first, so a PR touching two protected paths learns about both in one run.
1 parent 460427f commit fa91974

1 file changed

Lines changed: 66 additions & 12 deletions

File tree

.github/workflows/ci.yml

Lines changed: 66 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -101,28 +101,82 @@ jobs:
101101
env:
102102
PR_BODY: ${{ github.event.pull_request.body }}
103103
run: |
104+
# Any change to these needs the marker: a new exit code, a new config
105+
# field or a new schema property extends a contract even when nothing
106+
# existing moves.
104107
PROTECTED_FILES=(
105-
"docs/reference/rc/"
106108
"schemas/output_v1.json"
107109
"crates/cli/src/exit_code.rs"
108110
"crates/core/src/config.rs"
109111
)
110112
111-
CHANGED_FILES=$(git diff --name-only origin/${{ github.base_ref }}...HEAD)
113+
# The command reference is a contract too, but adding a section to it
114+
# is how a new command gets documented — required by AGENTS.md § 3 of
115+
# the Breaking Change process, in fact. Demanding the marker for that
116+
# makes every additive PR claim to be breaking, so these paths need it
117+
# only when existing lines move: a rewritten sentence, a removed flag,
118+
# a deleted or renamed page.
119+
ADDITIVE_OK_FILES=(
120+
"docs/reference/rc/"
121+
)
122+
123+
BASE="origin/${{ github.base_ref }}"
124+
CHANGED_FILES=$(git diff --name-only "$BASE"...HEAD)
125+
126+
# Paths under a protected prefix, one per line.
127+
matched_paths() {
128+
local protected_path="${1%/}"
129+
printf '%s\n' "$CHANGED_FILES" \
130+
| grep -E "^$(printf '%s' "$protected_path" | sed 's/[.[\*^$]/\\&/g')(/|$)" || true
131+
}
132+
133+
require_marker() {
134+
local subject="$1"
135+
echo "::warning::Protected file modified: $subject"
136+
echo "This change requires the Breaking Change process. See AGENTS.md."
137+
if ! grep -q "BREAKING" <<< "$PR_BODY"; then
138+
echo "::error::Protected file $subject modified without BREAKING marker in PR description"
139+
return 1
140+
fi
141+
}
142+
143+
status=0
112144
113145
for file in "${PROTECTED_FILES[@]}"; do
114-
protected_path="${file%/}"
115-
if echo "$CHANGED_FILES" | grep -Fxq "$protected_path" || echo "$CHANGED_FILES" | grep -q "^$protected_path/"; then
116-
echo "::warning::Protected file modified: $file"
117-
echo "This change requires the Breaking Change process. See AGENTS.md."
118-
119-
# Check if PR body contains BREAKING marker
120-
if ! grep -q "BREAKING" <<< "$PR_BODY"; then
121-
echo "::error::Protected file $file modified without BREAKING marker in PR description"
122-
exit 1
123-
fi
146+
if [ -n "$(matched_paths "$file")" ]; then
147+
require_marker "$file" || status=1
124148
fi
125149
done
150+
151+
for file in "${ADDITIVE_OK_FILES[@]}"; do
152+
paths=$(matched_paths "$file")
153+
[ -n "$paths" ] || continue
154+
155+
# Rename detection off on purpose: moving a reference page changes
156+
# where readers and links land, so it should count as a rewrite
157+
# rather than as a no-op.
158+
removed=0
159+
count=0
160+
while IFS= read -r path; do
161+
[ -n "$path" ] || continue
162+
count=$((count + 1))
163+
deleted=$(
164+
git diff --numstat --no-renames "$BASE"...HEAD -- "$path" \
165+
| awk '{ total += $2 } END { print total + 0 }'
166+
)
167+
removed=$((removed + deleted))
168+
done <<< "$paths"
169+
170+
if [ "$removed" -gt 0 ]; then
171+
require_marker "$file ($removed line(s) changed or removed)" || status=1
172+
else
173+
echo "$file changed by addition only ($count file(s)); no marker required"
174+
fi
175+
done
176+
177+
if [ "$status" -ne 0 ]; then
178+
exit 1
179+
fi
126180
echo "Protected files check passed"
127181
128182
msrv:

0 commit comments

Comments
 (0)