Files
chencheng (云谦) 1b1f4455d9 ci: build the PR tree once and shorten the required path (#16450)
* ci: build the PR tree once and shorten the required path

The no-AK integration gate, the four web-shell smoke shards and Lint & Static each rebuilt the tree test_build already uploads, and the build cache rarely helped: most runs logged "29 of 29 workspaces changed" because the arch-keyed cache was split across the mixed x64/ARM64 pool and a prefix restore returned the newest entry instead of the base commit's.

- test_build restores the build its base commit saved (ci-build-cache.mjs key --at), drops runner.arch from the key, and runs the serve fast-path closure and core subpath checks against the built tree.
- The no-AK integration gate unpacks that tree and runs as three shards; integration_no_ak_result keeps the required check name.
- The web-shell smoke unpacks the same tree instead of building; consumers recopy the opentui native assets for their host (host-assets).
- The standalone runtime regression moves from unit shard 1 to shard 4.
- A java_only profile skips the TypeScript lanes for changes confined to the Java packages; Lint & Static runs the scripts/tests suites that name them.
- Drafts skip the integration gate and the browser smoke; ready_for_review starts the full run.

* ci: check the settings schema in test_build, which has the core dist

Lint & Static only had core's dist because the serve fast-path check built
it; with that check moved, Generate settings schema failed to import it.

* ci: keep the standalone regression on unit shard 1

Moving it did not shorten shard 1: the regression runs in the side lane,
in parallel with the CLI and core suites, and shard 1's time is its CLI
slice (576 s against 147 s on shard 4 for the same 181 files). That
imbalance is left to duration-based sharding.
2026-11-06 03:30:11 -08:00

7.5 KiB

CI and Release Variables

Several knobs of the CI and release pipelines are exposed as GitHub Actions repository variables (the vars.* context) so operators can retune them without opening a pull request. This page lists the variables that affect test execution in .github/workflows/ci.yml and .github/workflows/release.yml, together with their defaults and where each one applies.

Setting a variable

Repository maintainers set these under Settings → Secrets and variables → Actions → Variables. An unset (or empty) variable uses the fallback in the workflow expression. The worker cap applies only on reserved runners, even when its variable is set.

The workflow files are the source of truth for these levers, and the test suites pin the workflow expressions byte-for-byte: scripts/tests/package-scripts.test.js pins the shared worker-cap expression in release.yml (QWEN_CI_VITEST_MAX_WORKERS with the ecs-qwen- guard), scripts/tests/no-ak-integration-ci.test.js pins the worker-cap expression in ci.yml, and scripts/tests/release-workflow.test.js pins the release.yml retry and workspace-test timeout expressions. scripts/tests/package-scripts.test.js also checks the documented defaults and workflow locations against both workflows. Because this guard runs on the full-profile CI lane (test:scripts) rather than on docs-only checks, verify edits confined to this page locally before opening a pull request with npm run test:scripts (or npx vitest run --config ./scripts/tests/vitest.config.ts scripts/tests/package-scripts.test.js). If the docs and the workflows ever disagree, trust the workflows and update this page in the same change.

Variables

Variable Default Used in Controls
QWEN_CI_VITEST_RETRY 2 ci.yml Retry count for the main CI workspace and script test step
QWEN_RELEASE_VITEST_RETRY 2 release.yml Retry count for the release workspace test shards
QWEN_RELEASE_WORKSPACE_TIMEOUT_MINUTES 45 release.yml Job timeout of each release workspace test shard
QWEN_CI_VITEST_MAX_WORKERS 4 ci.yml, release.yml Worker cap for main CI unit tests and release workspace/quality tests on reserved runners

Retry counts

QWEN_CI_VITEST_RETRY and QWEN_RELEASE_VITEST_RETRY are passed to Vitest as --retry=<n> in the main CI test step (every workspace's test:ci, which that step runs in two concurrent lanes, and npm run test:scripts:unit) and on the release lane (npm run test:release:workspaces) respectively. The two lanes have separate variables so they can be tuned independently.

Vitest reruns failing tests within the same run. This can help with intermittent contention, but a failure that recovers within the retry budget greens the check and is not recorded as a failure by the flaky-rerun tracker. Keep the budget modest instead of using retries to paper over a flaky suite. Both variables also accept the literal value off, which omits the --retry flag entirely instead of passing --retry=0 (a command-line --retry=0 outranks a workspace's own Vitest config and would disable a deliberate retry policy).

Workspace test timeout

QWEN_RELEASE_WORKSPACE_TIMEOUT_MINUTES sets the job-level timeout-minutes of each of the three workspace_tests shards in the release pipeline. The timeout is sized by how busy the reserved host is rather than by the suite itself, so raise it when the host is contended instead of assuming a test regression.

Vitest worker cap on self-hosted runners

QWEN_CI_VITEST_MAX_WORKERS caps the Vitest processes in the steps listed below (VITEST_MAX_THREADS / VITEST_MAX_FORKS, with the matching minimum forced to 1) on the reserved self-hosted runners whose name starts with ecs-qwen-. The variable is exported only by the main CI workspace-test step and the release workspace_tests and quality_scripts steps; other integration tests running Vitest that land on the same reserved pool in the CI and release workflows do not consume it; they use their own Vitest limits. The web-shell E2E smoke runs on the ARM64 pool with its own two Playwright workers, so the cap does not apply to it. On GitHub-hosted runners the variable is ignored and Vitest uses its own defaults.

Each Vitest run applies the cap on its own. The main CI workspace-test step runs two lanes at once (.github/scripts/ci/run-unit-test-lanes.sh): the main lane runs the CLI and core suites at the cap, and the side lane runs the other workspaces' suites and npm run test:scripts:unit at half the cap, at least 1, so at its peak the step runs the cap plus that half. On GitHub-hosted runners the side lane's Vitest runs use 2 workers.

The step runs in eight shards (Linux unit tests (shard N/8)), each passing --shard=N/8 to every one of those Vitest runs. The shards apply the cap on their own too, so the pool sees up to eight such steps at once. They install without the prepare build and unpack the tree test_build built once. Test (ubuntu-latest, Node 22.x) passes only when the build and every shard pass.

test_build is the only job that builds. It builds through scripts/ci-build-cache.mjs: a pull request restores the build its base commit's main push saved (key --at <base>), falling back to the newest cached build, and rebuilds only the workspaces whose inputs changed: a workspace's tracked files other than tests, the root files, patches/ and scripts/ outside scripts/tests/, and the workspaces it depends on. A push to main always builds from scratch and saves that build. The cache key carries no CPU architecture: the x64-labelled pool includes ARM64 hosts, and the only architecture-specific output, the bundle's opentui native library, is recopied for the host after every restore and unpack (ci-build-cache.mjs host-assets).

The unit-test shards, the three Integration Tests (no-AK, shard N/3) shards and the four web-shell smoke shards all unpack that one tree. test_build also runs the checks that read it, the serve fast-path bundle closure, the core subpath exports and the settings-schema freshness check, which Lint & Static used to build for. Integration Tests (no-AK, No Sandbox) passes only when the build and every integration shard pass. A draft pull request skips the integration shards and the browser smoke; marking it ready for review starts the run that includes them. A change confined to packages/managed-agent-server/ or packages/sdk-java/ (except the files TypeScript suites read) classifies as java_only: Lint & Static runs its payload plus the scripts/tests suites that name those trees, and the TypeScript build and test lanes skip.

release.yml also exposes QWEN_RELEASE_STATIC_TIMEOUT_MINUTES (default 60, controlling the quality_static lint lane) and QWEN_RELEASE_BUILD_TIMEOUT_MINUTES (default 45, controlling the quality_build packaging lane). Because they govern static linting and artifact builds rather than test execution, they are outside this page's test execution scope.