Skip to content

Commit e4737b6

Browse files
fix(ci): 分片 affected 集合从 merge-base 起算,不再吃冻结的 base.sha (#6195) (#6591)
Co-authored-by: Claude <noreply@anthropic.com>
1 parent d0a5ceb commit e4737b6

1 file changed

Lines changed: 99 additions & 2 deletions

File tree

.github/workflows/ci.yml

Lines changed: 99 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -215,13 +215,110 @@ jobs:
215215
# naming the cause, not a silently empty shard. An EMPTY shard file must
216216
# short-circuit the test step below: `turbo run test` with zero --filter
217217
# args runs the entire workspace.
218+
#
219+
# WHERE THE AFFECTED DIFF STARTS (#6195, the #6129 family's third
220+
# consumer). The one thing this base must never be is
221+
# `github.event.pull_request.base.sha`.
222+
#
223+
# The payload's `base.sha` is frozen when the PR is OPENED and does not
224+
# move on `synchronize`. HEAD, meanwhile, is the merge ref
225+
# (`refs/pull/N/merge`) that the checkout above resolves by default on a
226+
# `pull_request` event — no `ref:` is given, so this job stands on a merge
227+
# commit containing everything main has today. Everything main gained
228+
# while the PR sat open therefore lands between the two, and
229+
# `turbo ls --affected` reads it as this PR's own changes: packages only
230+
# SOMEBODY ELSE's merged PR touched get tested on this shard.
231+
#
232+
# The direction is conservative — the frozen base is an ancestor of HEAD,
233+
# so its file set is a strict SUPERSET of this PR's own. Nothing that
234+
# should run is skipped; what degrades is the optimisation itself, and it
235+
# degrades with how long the PR has been open. At ~18 merges a day an
236+
# affected-only shard drifts back toward a full run.
237+
#
238+
# Measured on turbo 2.10.7 against a real merge-ref fixture (base branch
239+
# moved 1 commit touching pkg-b; this PR touched pkg-a only):
240+
# TURBO_SCM_BASE=<frozen base.sha> -> pkg-a, pkg-b
241+
# TURBO_SCM_BASE=merge-base(origin/main,HEAD) -> pkg-a
242+
#
243+
# Four spellings that look like the fix and are not:
244+
# - `base.sha...HEAD` (three dots). Three-dot means
245+
# `merge-base(base.sha, HEAD)..HEAD`, and the frozen sha is ALREADY an
246+
# ancestor of HEAD, so it IS its own merge base and the set does not
247+
# move. Measured: still both packages. (pr-automation.yml records the
248+
# same result for its own diff — same fact, one family.)
249+
# - `HEAD^1`. Correct on a merge ref and silently wrong the day someone
250+
# gives this checkout a `ref:`, where parent^1 becomes the PR's
251+
# previous commit. `merge-base` is right under BOTH checkouts.
252+
# - Dropping the variable and letting turbo default to `main`. Measured
253+
# in a CI-shaped clone: `fetch-depth: 0` populates
254+
# `refs/remotes/origin/*`, NOT local heads, so there is no local `main`
255+
# — and turbo does not error, it silently returns the ENTIRE
256+
# workspace, untouched packages included. Safe, and the whole
257+
# optimisation gone.
258+
# - `TURBO_SCM_BASE=origin/$BASE_REF`, letting turbo resolve the ref.
259+
# Measured correct today, but it rests the shard's package set on
260+
# `turbo ls`'s internal choice of dot-ness — undocumented, and
261+
# `turbo ls` is experimental (see above). Resolving to a commit here
262+
# leaves turbo no choice to make.
218263
- name: Compute this shard's package set
219264
env:
220-
TURBO_SCM_BASE: ${{ github.event.pull_request.base.sha }}
265+
BASE_REF: ${{ github.event.pull_request.base.ref }}
266+
PINNED_BASE_SHA: ${{ github.event.pull_request.base.sha }}
221267
run: |
268+
SCM_BASE=''
222269
if [ "${{ github.event_name }}" = "pull_request" ]; then
223-
pnpm exec turbo ls --affected --output=json > "$RUNNER_TEMP/turbo-ls.json"
270+
if [ -z "$BASE_REF" ]; then
271+
echo "::warning::This pull_request event carries no base branch, so the affected-set diff base cannot be computed."
272+
else
273+
# `fetch-depth: 0` above already makes this resolve — the fetch is
274+
# the guard for the day that changes, not the normal path.
275+
#
276+
# `git cat-file -e` rather than the more idiomatic strict
277+
# `git rev-parse` spelling, and the reason is not style.
278+
# check-shard-attestation.mjs classifies a job as an aggregate GATE
279+
# when the job's joined `run:` text contains BOTH the string
280+
# "check-shard-attestation.mjs" and, as a bare substring, that
281+
# script's own dash-dash-verify flag. This job already carries the
282+
# first (its --emit step at the bottom), so spelling that flag
283+
# anywhere in this step — including in a comment, since comments
284+
# are part of `run:` — silently reclassifies the shard job as a
285+
# gate. The drift guard then fails with two complaints that name
286+
# nothing to do with the real edit ("its job-level if: is not
287+
# always()", "never downloads the shard attestations it claims to
288+
# count"). Measured both ways on this very change; do not "tidy"
289+
# this back.
290+
if ! git cat-file -e "refs/remotes/origin/$BASE_REF^{commit}" 2>/dev/null; then
291+
git fetch --no-tags --quiet origin "+refs/heads/$BASE_REF:refs/remotes/origin/$BASE_REF" \
292+
|| echo "::warning::Could not fetch origin/$BASE_REF; the merge-base resolution below will decide."
293+
fi
294+
# `if !` rather than a bare assignment on purpose: these steps run
295+
# under `bash -e`, where a failing command substitution kills the
296+
# step with no message at all.
297+
if ! SCM_BASE=$(git merge-base "refs/remotes/origin/$BASE_REF" HEAD); then
298+
SCM_BASE=''
299+
fi
300+
fi
301+
fi
302+
if [ -n "$SCM_BASE" ]; then
303+
# The drift is printed, not just corrected: nothing in this log ever
304+
# said which commit the affected diff started from, which is why the
305+
# decay was invisible.
306+
DRIFT=$(git rev-list --count "$PINNED_BASE_SHA..$SCM_BASE" 2>/dev/null || echo '?')
307+
echo "Affected-set diff base: $SCM_BASE (merge-base of origin/$BASE_REF and HEAD)"
308+
echo "Frozen payload base.sha: $PINNED_BASE_SHA -- $BASE_REF has moved $DRIFT commit(s) since it was frozen, and that drift is exactly what this step used to charge to this PR."
309+
TURBO_SCM_BASE="$SCM_BASE" pnpm exec turbo ls --affected --output=json > "$RUNNER_TEMP/turbo-ls.json"
224310
else
311+
# Falling back to the FULL package list, never to the frozen
312+
# base.sha. This is not the #4690 silent-skip anti-pattern: that is
313+
# about a gate PASSING on input it could not read, and the full list
314+
# is a strict superset of the affected one — this shard still runs
315+
# everything it would have run and more. Cost is minutes; the
316+
# alternative is a red Test Core on a PR with nothing wrong with it.
317+
# Push and merge-queue builds take this branch by design (the queue
318+
# result IS the next main, so it gets main's validation).
319+
if [ "${{ github.event_name }}" = "pull_request" ]; then
320+
echo "::warning::Could not resolve merge-base(origin/$BASE_REF, HEAD); falling back to the full package list for this shard rather than diffing from the frozen base.sha (#6195)."
321+
fi
225322
pnpm exec turbo ls --output=json > "$RUNNER_TEMP/turbo-ls.json"
226323
fi
227324
node scripts/partition-test-shards.mjs "$RUNNER_TEMP/turbo-ls.json" \

0 commit comments

Comments
 (0)