Skip to content

ci: one docsite-test job builds the docsite once and runs the data tests and browser contracts on it - #8891

Open
cixzhang wants to merge 23 commits into
ci/test-scope-heavy-lanesfrom
ci/docsite-one-job
Open

cixzhang wants to merge 23 commits into
ci/test-scope-heavy-lanesfrom
ci/docsite-one-job

Conversation

@cixzhang

@cixzhang cixzhang commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

Depends on #8919 (the last of #8888's split: each job decides its own scope); this PR targets its branch so the diff is only this change.

Maintainer impact

A docsite-affecting pull request ran three jobs: docsite-data (built core and the canary packages, generated the docsite data, ran its tests), docsite-browser (built core, the canary packages and every theme again, ran the docsite production build, which generates the data again, then the Playwright contracts) and the docsite-test join. That is two package builds and two data generations per run, about 16,000 job-minutes a day, most of it on 4-core runners. Now one job builds once and runs both suites on that build in about the browser job's time, and the required docsite-test context reports straight from the work instead of from a join.

Change

One docsite-test job (the required context, AST-030 FR7) replaces docsite-data, docsite-browser and the join:

  • It decides its own scope (ci: fold fixture-contrast into test-build #8916–ci: the heavy lanes decide their component scope; drop the scope jobs #8919's step). The spec-only lane (and any run whose scope step could not decide) validates spec records here as before, the Node-tooling lane skips, and every other change builds and tests.
  • It builds the docsite workspace dependencies once, then runs the docsite production build, which generates the data, once.
  • test-docsite (the docsite data tests) runs in the background on that build while test-docsite-e2e (the Playwright browser contracts) drives it in its own named step. A later move of the e2e step to another tier can take it alone. A result step reports the data tests even after a contract failure, so neither suite can leave the context green when it failed.
  • The browser evidence upload is unchanged.
  • Throttling: the workflow's per-PR cancel-in-progress already covers the job. Skipping drafts safely needs ready_for_review in the workflow's triggers, a workflow-wide change, so it is not done per job here.

Tests

  • ci-docsite-browser.test.mjs is rewritten for the one-job contract. It checks:
    • docsite-data and docsite-browser are gone, docsite-test has no needs, and there is exactly one docsite build;
    • every build and test step is gated on the active lane;
    • the start step, run under bash -e with a pnpm stub, records the data tests' exit code (0 and 1), with errexit off inside its background subshell and the exit file written whole;
    • the start step's id, the result step's condition and its lack of continue-on-error, that the result step waits while no exit code is recorded, and that it passes only on a zero exit;
    • the evidence upload.
  • spec-owner-workflow.test.mjs and ci-tooling-routing.test.mjs follow the merged job, and CONTRIBUTING.md names the docsite-test step instead of the removed job.
  • The workflow contract suites pass locally (207 tests): ci-docsite-browser, spec-owner-workflow, ci-tooling-routing, ci-heavy-lanes, ci-duration-guard, ci-test-routing and release-check-workflow.
  • Every .github/scripts suite passes on this head (76 files, 1,422 tests).

Lines

+163 / −217, net −54 against #8919 b85cca0: ci.yml +40/−115, tests +120/−99, CONTRIBUTING.md +3/−3.

…thout deepening history

Checking out the PR head (#8812) made pr-scope-touches.sh deepen a shallow
history back to the PR's recorded base. On long-lived branches that merge
main often, the interaction-scope decide step went from a few seconds to up
to three minutes, and one run hit the job's five-minute timeout, which fails
the required test join. Both scope jobs now check out the merge commit with
its parents and pass its first parent as the base, so the helper diffs the
PR's own change against the base it merged into with no deepening. The merge
queue keeps its base. A contract test pins the checkout and asserts no
deepening on a branch that merged main four times.
…dates

Pull request updates keep the fast checks and every required context. The
visual, accessibility and RTL owners run for release-check, which Main now
dispatches on each push to main (one at a time, skipping a commit main has
moved past, failing Main when it fails), and which the release gate already
runs before a cut. A pull request opts in with the ci:heavy-lanes label. A
main failure is fixed forward. AST-030 FR12 and the new DEC-7 record the
change.
One run per workflow and ref at a time, never cancelling a running check, so a
burst of pushes is checked once at its newest commit, and Main's fast checks
are no longer held behind the heavy wait.
lint.yml classifies a pull request's files with the trusted base classifier and
skips full ESLint for a spec-only, docsite-only or tooling-only change. Its
depth-1 checkout never let the merge commit reach its merge base, so the
classifier was never fed and every pull request ran full lint (about four
minutes of ESLint on a spec-only change). The checkout now takes the same
50-commit history ci.yml's check-scope does. A contract test runs the real
step and classifier with real git.
The job checked out the whole history on a 4-core runner for every pull
request into main, then learned from the branch name that only a release
merge-back has anything to validate: about six minutes of larger-runner time
per run for nothing. Its condition now names the merge-back branch pattern,
so other pull requests skip it outright; the exact branch contract is still
checked after the checkout, and required lint still validates merge-backs.
Internal Registry's dependency check runs only when a dependency
declaration or the lockfile changes, the CLI smoke test only when a
package, the workspace configuration or its own scripts change (its
paths-ignore becomes an allowlist), and the hardening-issue opener only
when Core component sources change. A registry records every workflow's
kind and reason, and a contract test holds each workflow to it: no
paths-ignore, no match-all glob, and no path filter on a workflow that
reports a required context.
…rds why (review)

Internal Registry's header no longer says to make its check required:
behind its path filter it never starts on a pull request that touches no
dependency file. Re-enabling gating means dropping the filter and
registering it as change-scope. A review-event workflow on
pull_request_target now records in the registry why every pull-request
update matters to review state, and the contract test requires it.
Removes the scope classifier jobs (check-scope, interaction-scope,
native-swift-scope, check-components). Each job that owns tests now runs
.github/actions/test-scope right after a sparse checkout: it reads the pull
request's file list and the base commit's trusted classifier, and skips the
job's remaining steps with success when nothing in its scope changed.
Anything it cannot decide puts everything in scope (spec:AST-030 FR3, FR4).
native-swift-runner is the one pre-job left, because only a job's runner has
to be chosen before it starts.

Also:
- fixture-contrast's steps run in test-build (same Playwright runner);
- registry-contract becomes test-registry-e2e and runs for release-check
  only; its network-free production gate runs with the unit tests;
- ci-duration and the weekly a11y and RTL workflows are removed (the full
  audits run on main);
- the Internal Registry job becomes test-deps, scoped by its trigger paths;
- the test join requires release-bump on a release-branch pull request.
…sts and browser contracts on it

docsite-data, docsite-browser and the docsite-test join become the one
required docsite-test job (AST-030 FR7's name). It builds the packages and
the docsite production build (which generates the data) once, then runs
test-docsite (the data tests, in the background) and test-docsite-e2e (the
Playwright contracts, its own named step) side by side on that build; a
result step reports the data tests even after a contract failure. The
spec-only lane still validates spec records here; the tooling lane skips.

ci-docsite-browser.test.mjs pins the one-job shape, the step gates, the
data-result step's exit code and the evidence upload; spec-owner-workflow
and ci-tooling-routing follow the merged job.
@vercel

vercel Bot commented Oct 29, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
astryx Ready Ready Preview Oct 30, 2026 2:48am UTC

Request Review

@astracat-bot

astracat-bot Bot commented Oct 29, 2026 •

Copy link
Copy Markdown

AI review status for this pull request.

Review status Updated
🟢 Reviewed (for maintainers only) Oct 30, 2026, 3:09 AM UTC

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Meta Open Source bot. label Oct 29, 2026
The main-tier release-check dispatcher runs on every landed commit, like the
Main workflow, so it is a landed-commit workflow.

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes requested: the single docsite-test job keeps the required context and builds the docsite once, but a failing docsite data test is not reported as written. GitHub runs these run: steps with bash -e, so the backgrounded subshell in test-docsite (start the data tests) exits before writing docsite-data.exit, and test-docsite (data test results) then polls until the 15-minute job timeout without printing docsite-data.log.

  • Turn errexit off inside that subshell, for example (set +e; pnpm -F @astryxdesign/docsite test > docsite-data.log 2>&1; echo $? > docsite-data.exit.tmp; mv docsite-data.exit.tmp docsite-data.exit) &, and consider bounding the wait loop.
  • In ci-docsite-browser.test.mjs, run the start step's script under bash -e with a failing pnpm stub and assert the start step's id: data and the result step's missing continue-on-error, since either regression currently lets a data-test failure pass with every test green.
  • Update the CONTRIBUTING.md sentence that still says a docsite-browser job feeds docsite-test.

[Automated review]

Comment thread .github/workflows/ci.yml Outdated
- name: test-docsite (start the data tests)
id: data
if: steps.scope.outputs.spec_only != 'true' && steps.scope.outputs.tooling_only != 'true'
run: (pnpm -F @astryxdesign/docsite test > docsite-data.log 2>&1; echo $? > docsite-data.exit) &

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This subshell inherits the runner's bash -e (the workflow sets no shell:), so when pnpm -F @astryxdesign/docsite test fails it exits before echo $? > docsite-data.exit. The result step's while [ ! -f docsite-data.exit ] loop then runs until the job's 15-minute timeout, and docsite-data.log is never printed. Turning errexit off in the subshell, and writing the file atomically, fixes it:

(set +e; pnpm -F @astryxdesign/docsite test > docsite-data.log 2>&1; echo $? > docsite-data.exit.tmp; mv docsite-data.exit.tmp docsite-data.exit) &

Comment on lines +59 to +72
it.each([
[0, true],
[1, false],
])('the data-test result step passes only on exit %i', (code, passes) => {
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'docsite-data-'));
fs.writeFileSync(path.join(dir, 'docsite-data.log'), 'log\n');
fs.writeFileSync(path.join(dir, 'docsite-data.exit'), `${code}\n`);
const result = spawnSync(
'bash',
['-e', '-c', step('test-docsite (data test results)').run],
{cwd: dir},
);
fs.rmSync(dir, {recursive: true, force: true});
expect(result.status === 0).toBe(passes);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This runs only the result step against a pre-written exit file, so the start step's behaviour under bash -e is untested. Running step('test-docsite (start the data tests)').run with bash -e and a pnpm stub that exits 1, then expecting docsite-data.exit to hold 1, would catch it. Asserting that the start step's id is data and that the result step has no continue-on-error would pin the two other edges that currently let a data-test failure leave docsite-test green without failing any test.

The backgrounded subshell inherited the runner's errexit, so a failing data
test run exited before writing docsite-data.exit and the result step waited
for the job timeout. errexit is off inside the subshell and the exit file is
written whole (tmp + mv). The contract test now runs the start step under
bash -e with a pnpm stub (exit 1 fails on the previous head) and pins the
start step's id and the result step's lack of continue-on-error.
CONTRIBUTING.md names the docsite-test step instead of the removed job.

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Recommended for approval: docsite-test keeps the required context, builds the docsite once, and fails on either a data-test or a browser-contract failure, with the Vitest log printed by the result step. The backgrounded data-test step now records its exit code under bash -e, the contract test pins that path's fail-closed edges, and CONTRIBUTING.md names the new browser-contract step. CI has not run the merged job yet because this branch targets #8888's branch, so please check its wall time against the 15-minute limit on its first run against main.

[Automated review]

…per job

Review follow-ups:
- The registry contract, ci-duration, the weekly sweeps and the Internal
  Registry rename move to their own pull request, so this one only removes
  the scope classifier jobs and folds fixture-contrast into test-build.
  registry-contract stays a pull-request job here, deciding its own scope.
- A ci:heavy-lanes pull request keeps its visual and RTL audits on the
  affected components (spec:AST-030 FR13): the scope action can also run the
  base's component-audit-scope.cjs and list the changed files, and the RTL
  and visual jobs read those instead of a classification job.
- The interaction and native Swift scopes include the scope action, so a
  change to it runs the jobs it gates.

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Recommended for approval: merging #8888's branch keeps the one-job docsite-test valid by dropping the two deleted docsite jobs from ci-duration's needs, and data-test and browser-contract failures still fail the required check with their logs. No changes are needed; because this branch targets #8888's branch, CI has not run the merged job yet, so please check its wall time against the 15-minute limit on its first run against main.

[Automated review]

…ocsite-test validates

Review follow-ups:
- On a ci:heavy-lanes pull request, the accessibility, visual and RTL jobs
  decide their own component scope and skip their work, downloads included,
  when it does not apply, so a spec-, docsite- or tooling-only change no
  longer fails on a missing analysis artifact. The RTL join accepts shards
  that decided their scope and did nothing.
- The scope action reports whether it decided. docsite-test validates spec
  records when its own scope is spec-only or undecided, so a failed lookup
  can never pass the required check unvalidated (spec:AST-030 FR4).
# Conflicts:
#	.github/scripts/spec-owner-workflow.test.mjs
#	.github/workflows/ci.yml

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Recommended for approval: after merging #8888's latest branch, the one docsite-test job still validates spec records on the spec-only lane and whenever its scope step cannot decide, and data-test and browser-contract failures still fail the required check with their logs. No changes are needed; because this branch targets #8888's branch, CI has not run the merged job yet, so please check its wall time against the 15-minute limit on its first run against main.

[Automated review]

…csite-test

#8888 was split into #8916-#8919 on main. This carries the one-job
docsite-test change onto #8919 as a 3-way merge against its v8: the
docsite jobs are identical there, so the delta applies unchanged.
@cixzhang
cixzhang changed the base branch from ci/test-scope-consolidation to ci/test-scope-heavy-lanes October 30, 2026 02:10

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes requested: the single docsite-test job keeps the required context, builds the docsite once and fails on either suite with its log, but no test pins the result step's wait for the background data tests. With while [ ! -f docsite-data.exit ]; do sleep 2; done in .github/workflows/ci.yml replaced by [ -f docsite-data.exit ] || exit 0, the workflow suites in .github/scripts stay green, yet a failing data-test run that finishes after the browser contracts would then leave the check green, and AST-030 FR9 asks that removing any fail-closed edge make a test fail. Please add a test to ci-docsite-browser.test.mjs that starts test-docsite (data test results) with only docsite-data.log present, checks that it is still running, then writes 1 to docsite-data.exit (through a temporary file and a rename) and expects exit 1. Because this branch targets #8919's branch, CI has not run the merged job yet, so please check its wall time against the 15-minute limit on its first run against main.

[Automated review]

it.each([
[0, true],
[1, false],
])('the data-test result step passes only on exit %i', (code, passes) => {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both cases write docsite-data.exit before the step runs, so this pins the exit-code mapping but not the wait: with while [ ! -f docsite-data.exit ]; do sleep 2; done replaced by [ -f docsite-data.exit ] || exit 0, the workflow suites in .github/scripts stay green, while a data-test run that fails after the browser contracts would then leave docsite-test green (AST-030 FR9). A case that starts the step before the exit file exists would pin it:

it('the data-test result step waits for the data tests to finish', async () => {
  const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'docsite-data-'));
  fs.writeFileSync(path.join(dir, 'docsite-data.log'), 'log\n');
  const child = spawn('bash', ['-e', '-c', step('test-docsite (data test results)').run], {cwd: dir});
  const exited = new Promise(resolve => child.on('exit', resolve));
  await new Promise(resolve => setTimeout(resolve, 500));
  expect(child.exitCode).toBeNull();
  fs.writeFileSync(path.join(dir, 'docsite-data.exit.tmp'), '1\n');
  fs.renameSync(path.join(dir, 'docsite-data.exit.tmp'), path.join(dir, 'docsite-data.exit'));
  expect(await exited).toBe(1);
  fs.rmSync(dir, {recursive: true, force: true});
});

… code

A case starts the result step before docsite-data.exit exists: it must
still be waiting 3 s later, then exit with the recorded code once the file
appears. An early exit 0 in place of the wait now fails the suite
(AST-030 FR9).

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Recommended for approval: docsite-test keeps the required context, builds the docsite once and fails on either a data-test or a browser-contract failure with its log, and ci-docsite-browser.test.mjs now fails if the result step stops waiting for the background data tests. No changes are needed. Because this branch targets #8919's branch, CI has not run the merged job yet, so please check its wall time against the 15-minute limit on its first run against main.

[Automated review]

This branch was successfully deployed

1 active deployment
Preview — fbb5cca5 Deployed Oct 30, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Meta Open Source bot.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant