* 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.
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.
Related variables outside test execution
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.