fix(ci): pin COREPACK_HOME for Linux test shards on cold-HOME runs (#16328)

The Linux unit-test shards set HOME to a fresh per-run directory for
isolation. scripts/tests/corepack-warmup.js (vitest globalSetup) spawns
`corepack pnpm --version` before any worker forks, and on a cold HOME
corepack downloads the pinned pnpm from registry.npmjs.org. The ECS
egress intermittently times out on that fetch (ETIMEDOUT to 104.16.9.34),
which kills the shard after every test file has already passed — today
that produced 7 PR-facing failures across random shard numbers (2/8,
5/8, 3/8, etc.), all on the same self-hosted host.

Install dependencies runs before the test step and warms the corepack
cache under the runner's real HOME, but the test step's HOME override
makes that cache invisible. Pin COREPACK_HOME to a per-run path in both
steps so they share the same cache without touching the HOME isolation.

Per-run, not persistent: corepack does not re-verify extracted entries
on cache hit, so a shared cache would be a poisoning channel (the same
reason lint_and_static keeps its COREPACK_HOME per-run).

Co-authored-by: Qwen-Coder <[email protected]>
This commit is contained in:
易良andQwen-Coder authored and GitHub committed 2026-11-05 01:19:56 +08:00
1 parent 8e9b429ff8
commit 2b9c4a98f4
2 files changed
+33 -4

No files matched your search

+13
View File
@@ -1078,6 +1078,12 @@ jobs:
env:
QWEN_SKIP_PREPARE: '1'
QWEN_SKIP_NOTICE_GENERATION: '1'
# Pin COREPACK_HOME to a per-run path so the corepack cache written
# here is visible to the test step below, which overrides HOME for
# isolation. corepack does not re-verify extracted entries on cache
# hit, so this must NOT be a shared/persistent path (poisoning
# channel) — per-run is safe and sufficient.
COREPACK_HOME: '${{ runner.temp }}/qwen-ci-corepack'
run: |-
DISK_SAMPLES="${RUNNER_TEMP}/disk-pressure-samples.log"
echo "DISKCONTEXT $(date -u +%Y-%m-%dT%H:%M:%SZ 2>/dev/null) runner[${RUNNER_NAME:-unknown}] run[${GITHUB_RUN_ID:-local}/${GITHUB_RUN_ATTEMPT:-1}] job[${GITHUB_JOB:-test}] cpus[$(nproc 2>/dev/null || echo unknown)]" > "$DISK_SAMPLES" 2>/dev/null || true
@@ -1246,6 +1252,13 @@ jobs:
QWEN_CI_COVERAGE: "${{ github.event_name == 'push' && '1' || '' }}"
HOME: '${{ runner.temp }}/qwen-ci-home'
USERPROFILE: '${{ runner.temp }}/qwen-ci-home'
# HOME above hides the runner's corepack cache; without this the
# vitest globalSetup (scripts/tests/corepack-warmup.js) spawns
# `corepack pnpm --version` on a cold cache and downloads the pinned
# pnpm from npmjs, which the ECS egress can time out on. Must match
# the Install dependencies step's COREPACK_HOME so it hits the cache
# install already warmed.
COREPACK_HOME: '${{ runner.temp }}/qwen-ci-corepack'
# Bound each Vitest process on the shared ECS host. Operators can
# tune the maximum without changing this workflow.
VITEST_MAX_THREADS: "${{ startsWith(runner.name, 'ecs-qwen-') && (vars.QWEN_CI_VITEST_MAX_WORKERS || '4') || '' }}"
+20 -4
View File
@@ -833,10 +833,23 @@ describe('post-merge push lane', () => {
// build failed, while lint has no such dependency — so it is
// stripped here and each side is pinned explicitly below.
if (job !== 'test_build' && s.name === 'Install dependencies') {
expect(s.env).toEqual({
QWEN_SKIP_PREPARE: '1',
QWEN_SKIP_NOTICE_GENERATION: '1',
});
if (job === 'test') {
expect(s.env).toEqual({
QWEN_SKIP_PREPARE: '1',
QWEN_SKIP_NOTICE_GENERATION: '1',
// The shards' test step overrides HOME, which would hide the
// corepack cache install warms here; pin COREPACK_HOME to a
// per-run path so the warmup hits it. lint_and_static routes
// HOME and COREPACK_HOME together via GITHUB_ENV, so its
// install needs no step-level override.
COREPACK_HOME: '${{ runner.temp }}/qwen-ci-corepack',
});
} else {
expect(s.env).toEqual({
QWEN_SKIP_PREPARE: '1',
QWEN_SKIP_NOTICE_GENERATION: '1',
});
}
const { env: _installEnv, if: _installIf, ...install } = s;
return [
s.name,
@@ -883,6 +896,9 @@ describe('post-merge push lane', () => {
expect(install('test').env).toEqual({
QWEN_SKIP_PREPARE: '1',
QWEN_SKIP_NOTICE_GENERATION: '1',
// The test step's HOME override would hide the corepack cache install
// warms here; pin COREPACK_HOME to a per-run path so the warmup hits it.
COREPACK_HOME: '${{ runner.temp }}/qwen-ci-corepack',
});
const namesIn = (job) => (ci.jobs[job].steps ?? []).map((x) => x.name);
// Values alone are order-blind, and order is load-bearing here: `steps`