Skip to content

Commit bb7d91f

Browse files
baozhoutaoclaude
andauthored
ci(lint): add a .sh arm to select-gate-families's pm_dispatch_gates read-set (#16986)
The dispatch-gates self-test grew a live census that reads the CONTENT of every tracked `.sh` file (comment-masked watch-hint extraction), but the selector's pm_dispatch_gates read-set had no `*.sh` rule. A modified shell script under packages/**, apps/**, examples/**, docs/** or content/** that does not sit in a scripts/ subdirectory classified to a class whose arm skips the gate, silently disarming a required merge check for the one path shape `*/scripts/*` does not already cover. Add `case "$path" in *.sh) return 0 ;; esac` beside the existing `.gitignore` arm, before the class switch, so any `.sh` file runs the family whatever class it sits in -- a strict superset of what already ran, so it can only widen coverage, never narrow it. Add a self-test case that drives the window directly: a `packages/a/foo.sh` modified at status M now selects pm_dispatch_gates (asserted red against the unmodified script, green after), with a negative control confirming a docs-class file at status M still skips every family. Claude-Session: https://claude.ai/code/session_012GKcPZbMoGq7WPzKLfRBTU Co-authored-by: Claude <noreply@anthropic.com>
1 parent 09c1d91 commit bb7d91f

2 files changed

Lines changed: 24 additions & 13 deletions

File tree

scripts/ci/select-gate-families.selftest.sh

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ printf '{"name":"a"}\n' > "$UP/packages/a/package.json"
6767
printf 'export const a = 1;\n' > "$UP/packages/a/src/index.ts"
6868
printf 'export const t = 1;\n' > "$UP/packages/a/src/index.test.ts"
6969
printf '{"rows":[]}\n' > "$UP/packages/a/src/data.json"
70+
printf '#!/usr/bin/env bash\necho foo\n' > "$UP/packages/a/foo.sh"
7071
printf 'dist/\n' > "$UP/packages/a/.gitignore"
7172
printf 'console.log(1);\n' > "$UP/packages/a/scripts/build.mjs"
7273
printf 'export const site = 1;\n' > "$UP/apps/site/src/page.tsx"
@@ -428,6 +429,13 @@ expect_rc 0
428429
expect_warnings '' ''
429430
expect_verdicts
430431

432+
S=$(scenario M:packages/a/foo.sh)
433+
run_case 'merge_group: a workspace shell script outside scripts/ is still gate source, not a skipped non-source workspace file (#16769)' "$REPO" merge_group '' "$C0"
434+
expect_rc 0
435+
expect_warnings '' ''
436+
expect_verdicts pm_dispatch_gates
437+
expect_reason pm_dispatch_gates packages/a/foo.sh
438+
431439
S=$(scenario M:packages/a/scripts/build.mjs)
432440
run_case 'merge_group: a package-local script is a gate source (PM) and a masked source (corpus)' "$REPO" merge_group '' "$C0"
433441
expect_rc 0

scripts/ci/select-gate-families.sh

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -61,17 +61,17 @@
6161
# `AGENTS.md`, `CLAUDE.md`, `tsconfig.json`,
6262
# .gitignore, and sweeps `git ls-files` for hint
6363
# reachability and test-file residue. It also reads
64-
# the CONTENT of every JS/TS file in the tree: the
65-
# compound-anchor census of `function ...SelfTest...(`
66-
# declarations asserts none is unlisted, and the
67-
# exposed-scratch-dir sweep reads every
68-
# mkdtempSync/mkdirSync caller and consults nested
69-
# .gitignore files. So any masked source file and any
70-
# .gitignore runs it, and because the name sweep reads
71-
# the tracked NAME set an ADDED file anywhere runs it
72-
# too; only modifications of docs, changesets and
73-
# non-source workspace files that are neither a
74-
# manifest nor a script skip it.
64+
# the CONTENT of every JS/TS and shell (`.sh`) file in
65+
# the tree: the compound-anchor census of
66+
# `function ...SelfTest...(` declarations asserts none
67+
# is unlisted, and the exposed-scratch-dir sweep reads
68+
# every mkdtempSync/mkdirSync caller and consults
69+
# nested .gitignore files. So any masked source file,
70+
# any `.sh` file, and any .gitignore runs it, and
71+
# because the name sweep reads the tracked NAME set an
72+
# ADDED file anywhere runs it too; only modifications
73+
# of docs, changesets and non-source workspace files
74+
# that are neither a manifest nor a script skip it.
7575
# query_options_erasure `pnpm check:query-options-erasure`. Lints
7676
# packages/**/*.{ts,tsx,mts,cts} under
7777
# `eslint.config.mjs`, reads its baseline
@@ -314,9 +314,12 @@ family_reads() {
314314
# The self-test reads the CONTENT of every JS/TS file in the tree (the
315315
# compound-anchor census of `function ...SelfTest...(` declarations, the
316316
# exposed-scratch-dir sweep of every mkdtempSync/mkdirSync caller) and
317-
# consults nested .gitignore files, so any masked source and any
318-
# .gitignore runs it whatever class it sits in.
317+
# every tracked `.sh` file (the same watch-hint extraction, run through
318+
# the comment mask), and consults nested .gitignore files, so any masked
319+
# source, any `.sh` file, and any .gitignore runs it whatever class it
320+
# sits in.
319321
is_masked_source "$path" && return 0
322+
case "$path" in *.sh) return 0 ;; esac
320323
case "$path" in */.gitignore) return 0 ;; esac
321324
case "$class" in
322325
docs|changeset) return 1 ;;

0 commit comments

Comments
 (0)