mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-11-07 18:36:00 -10:00
fix(ci): report swap and a df -h fallback in the lint lane failure dump (#15095)
* fix(ci): report swap and a df -h fallback in the lint lane's failure dump The lint lane's state-at-failure dump asked /proc/meminfo for MemTotal and MemAvailable only, and ran df -hT with no retry, while the three sibling dumps in the same workflow grep SwapTotal|SwapFree and fall back to df -h. A swap-exhausted death on the contended pool therefore uploaded an artifact that could not show it, and a runner whose df has no -T uploaded no filesystem table at all. Witness it by executing the step body rather than pinning its text: the new case stubs a df that rejects any option cluster carrying T, so the marker can only come from the -h retry, and asserts both swap fields reach the artifact. Red before (the artifact held MemTotal/MemAvailable only and no filesystem table), green after. Co-authored-by: Qwen-Coder <[email protected]> * test(ci): pin the retained dump fields and the three sibling dumps R1-1: the swap/df-fallback case asserted only what the PR adds, so the halves of the two edited lines it kept were unguarded. Collapsing the retry to `df -h || true`, or reordering it to `df -h || df -hT || true`, stayed green while the artifact permanently lost the filesystem Type column; narrowing the grep to swap alone stayed green while the lane this PR fixes stopped reporting RAM at failure. Give the stub's `-hT` arm its own marker so "retried after -hT failed" is distinguishable from "never tried -hT", and assert both RAM fields beside the swap ones. R1-2: the same dump is replicated at four sites and nothing pinned them equal, so dropping the swap fields or the `|| df -h` retry from test, test_macos or test_windows shipped green. Pin the three siblings textually, extending the sampler pin loop idiom already in this file -- textual rather than executed because test_windows greps /proc/meminfo, which Git Bash lacks. Test-only: .github/workflows/ci.yml is byte-unchanged by this commit (md5 31b18cbbaeb407d9348e1f3f51f1c0b4). Verified with a nine-mutant battery run one mutant at a time against ci.yml, restoring and md5- checking after each: all nine are now killed, each by a named assertion. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmvik9qbdof --------- Co-authored-by: Qwen-Coder <[email protected]>
This commit is contained in:
1 parent
3efcf2adf7
commit
d6abebb40c
2 files changed
+105
-2
No files matched your search
@@ -240,6 +240,109 @@ describe('ci.yml disk-pressure evidence', () => {
|
||||
}
|
||||
});
|
||||
|
||||
it('reports swap and falls back to df -h in the failure dump', () => {
|
||||
// The three sibling state-at-failure dumps grep SwapTotal|SwapFree and
|
||||
// retry `df -h` when `df -hT` is unavailable; this one did neither, so a
|
||||
// swap-exhausted death on the contended pool uploaded an artifact that
|
||||
// could not show it, and a runner whose df has no -T (busybox, minimal
|
||||
// images) uploaded no filesystem table at all. Execute the step body
|
||||
// instead of pinning its text: the grep pattern is only worth anything if
|
||||
// the fields actually reach the artifact. The stub rejects any option
|
||||
// cluster carrying T — `df -hT` arrives as ONE argument, so matching on
|
||||
// `-T` alone would let it through and fake the fallback. That arm must stay
|
||||
// ahead of `-h`, and must mark its own output: otherwise "retried after
|
||||
// -hT failed" and "never tried -hT" are indistinguishable.
|
||||
//
|
||||
// The three sibling dumps carry the same two fields and no equality pin
|
||||
// reaches them ('Dump disk state on failure' is this lane's alone). Pin
|
||||
// them textually like the sampler loop above — not executed, because
|
||||
// test_windows greps /proc/meminfo, which Git Bash lacks.
|
||||
for (const jobName of ['test', 'test_macos', 'test_windows']) {
|
||||
const leg = ciJobs[jobName].steps.find(
|
||||
(candidate) => candidate.name === 'Run tests and generate reports',
|
||||
);
|
||||
assert.ok(leg, `missing run-tests step in ${jobName}`);
|
||||
assert.match(
|
||||
leg.run,
|
||||
/df -hT .* \|\| df -h /,
|
||||
`${jobName} lost the df -h retry`,
|
||||
);
|
||||
assert.match(leg.run, /SwapTotal\|SwapFree/, `${jobName} lost swap`);
|
||||
}
|
||||
|
||||
const root = mkdtempSync(join(tmpdir(), 'ci-disk-pressure-'));
|
||||
const df = join(root, 'df');
|
||||
writeFileSync(
|
||||
df,
|
||||
'#!/usr/bin/env bash\ncase "$*" in\n *T*) echo STUB-DF-HT; exit 1 ;;\n *-h*) echo STUB-DF-H ;;\nesac\n',
|
||||
);
|
||||
chmodSync(df, 0o755);
|
||||
|
||||
try {
|
||||
const result = spawnSync(
|
||||
'bash',
|
||||
[
|
||||
'-e',
|
||||
'-o',
|
||||
'pipefail',
|
||||
'-c',
|
||||
lintStep('Dump disk state on failure').run,
|
||||
],
|
||||
{
|
||||
encoding: 'utf8',
|
||||
timeout: 30_000,
|
||||
env: {
|
||||
...process.env,
|
||||
PATH: `${root}:${process.env.PATH}`,
|
||||
RUNNER_TEMP: root,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
assert.equal(result.error, undefined);
|
||||
assert.equal(
|
||||
result.status,
|
||||
0,
|
||||
`signal: ${result.signal}\nstdout: ${result.stdout}\nstderr: ${result.stderr}`,
|
||||
);
|
||||
const samples = readFileSync(
|
||||
join(root, 'disk-pressure-samples.log'),
|
||||
'utf8',
|
||||
);
|
||||
assert.match(
|
||||
samples,
|
||||
/^SwapTotal:/m,
|
||||
`swap exhaustion is invisible in the artifact\nstdout: ${result.stdout}`,
|
||||
);
|
||||
assert.match(samples, /^SwapFree:/m);
|
||||
// All four fields share the one edited grep line, so a pattern narrowed
|
||||
// to swap alone must go red here. `MemAvailable` has to keep the exact
|
||||
// spelling the sampler's `awk '/MemAvailable/'` uses, or the two stop
|
||||
// being comparable.
|
||||
assert.match(
|
||||
samples,
|
||||
/^MemTotal:/m,
|
||||
`RAM total is invisible in the artifact\nstdout: ${result.stdout}`,
|
||||
);
|
||||
assert.match(samples, /^MemAvailable:/m, 'RAM available is invisible');
|
||||
// -hT must be ATTEMPTED, not merely tolerated: a `df -h || true`
|
||||
// collapse or a reorder still yields STUB-DF-H, silently losing the
|
||||
// filesystem Type column.
|
||||
assert.match(
|
||||
samples,
|
||||
/^STUB-DF-HT$/m,
|
||||
`df -hT was never attempted first\nstdout: ${result.stdout}`,
|
||||
);
|
||||
assert.match(
|
||||
samples,
|
||||
/^STUB-DF-H$/m,
|
||||
`df -hT failed and nothing retried with -h\nstdout: ${result.stdout}`,
|
||||
);
|
||||
} finally {
|
||||
rmSync(root, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
it('keeps install failure status while writing the pre-install sample', () => {
|
||||
const root = mkdtempSync(join(tmpdir(), 'ci-disk-pressure-'));
|
||||
// The install step runs `corepack pnpm install`; stub corepack so the
|
||||
|
||||
@@ -1380,9 +1380,9 @@ jobs:
|
||||
DISK_SAMPLES="${RUNNER_TEMP}/disk-pressure-samples.log"
|
||||
{
|
||||
echo "DISKCONTEXT failure-dump $(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
||||
df -hT || true
|
||||
df -hT || df -h || true
|
||||
df -i || true
|
||||
grep -E 'MemTotal|MemAvailable' /proc/meminfo || true
|
||||
grep -E 'MemTotal|MemAvailable|SwapTotal|SwapFree' /proc/meminfo || true
|
||||
} 2>/dev/null | tee -a "$DISK_SAMPLES" || true
|
||||
# Collector for the #10035 telemetry, kept as the LAST step: failure()
|
||||
# is evaluated when the step is reached and never revisited, so at its
|
||||
|
||||
Reference in new issue
Block a user