From f6ba77cc170920a7103c0a1ebb5ea6d860c764b2 Mon Sep 17 00:00:00 2001 From: tryeverything24 Date: Tue, 21 Jul 2026 08:26:07 -0700 Subject: [PATCH] fix(ci): fail loudly instead of silently running full suite on shard-lookup failure The shard-file lookup used `mapfile -t SHARD_FILES < <(node -e ...)`, which runs the node one-liner in a process-substitution subshell whose exit code mapfile does not propagate. If matrix.shard doesn't match a key in shard-assignment.json, or the file is missing/corrupt, the node script throws, exits non-zero, but mapfile still returns 0 -- leaving SHARD_FILES empty and silently making vitest run the entire suite instead of its slice. Capture the node output via a checked command-substitution assignment instead (same idiom used to fix compose_file_args in #7765), so a lookup failure now fails the step with an explicit ::error:: annotation and a non-zero exit. Closes #7767 --- .github/workflows/ci.yml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index adb70df6d1..15ec6f6dd0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1041,7 +1041,16 @@ jobs: # write output at all if that's ever violated, so a bug here fails this step loudly rather # than silently dropping a test file from CI. node --experimental-strip-types scripts/compute-test-shards.ts --shards=3 --timing=test-timing.json --output=shard-assignment.json - mapfile -t SHARD_FILES < <(node -e "console.log(JSON.parse(require('fs').readFileSync('shard-assignment.json','utf8'))['${{ matrix.shard }}'].join('\n'))") + # #7767: capture via a checked assignment so a throw in the node one-liner (bad matrix.shard + # value, corrupt/missing shard-assignment.json) actually aborts this step -- the old + # `mapfile -t SHARD_FILES < <(node -e ...)` ran node in a subshell whose non-zero exit was + # swallowed (mapfile itself returns 0), leaving SHARD_FILES empty and silently making vitest + # run the entire suite instead of its slice. Same fix idiom as compose_file_args (#7765). + if ! SHARD_FILES_RAW="$(node -e "console.log(JSON.parse(require('fs').readFileSync('shard-assignment.json','utf8'))['${{ matrix.shard }}'].join('\n'))")"; then + echo "::error::failed to resolve shard file list for shard ${{ matrix.shard }}" + exit 1 + fi + mapfile -t SHARD_FILES <<< "$SHARD_FILES_RAW" npm run test:coverage -- --maxWorkers=4 "${SHARD_FILES[@]}" --reporter=default --reporter=blob --reporter=junit --outputFile.blob=blob-report/report-${{ matrix.shard }}.blob --outputFile.junit=reports/junit/vitest.xml "${EXCLUDE_ARGS[@]}" fi - name: Test failure guidance