fix(ci): route helper-test tmpdir off the host /tmp tmpfs (#15979)

* fix(ci): route helper-test tmpdir off the host /tmp tmpfs

* test(ci): pin the helper-battery tmpdir routing against comments

Two gaps in the #15979 change:

- The pin used toContain/indexOf on the raw run block, so prefixing the
  export with `# ` disabled the routing while the guard stayed green.
  Filter comment lines before asserting, like the lint-history pin already
  does, and pin the private dir + EXIT trap the step now creates.
- The step routed TMPDIR straight into the shared RUNNER_TEMP root. Copy
  the unit lanes' second half too: mktemp -d under RUNNER_TEMP and reap it
  on EXIT, so a cancelled battery does not leave its fixture tree behind
  (its own after() hook does not run on a signal).

Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-closeout/jmvvyiud548

---------

Co-authored-by: Qwen-Coder <[email protected]>
This commit is contained in:
易良andQwen-Coder authored and GitHub committed 2026-10-31 15:35:03 +08:00
1 parent 324a7df828
commit cd4c917fcf
2 files changed
+40

No files matched your search

+14
View File
@@ -1476,6 +1476,20 @@ jobs:
- name: 'Run .github/scripts helper tests'
if: "${{ needs.classify_pr.outputs.skip_ci != 'true' && steps.ci_profile.outputs.ci_profile == 'full' }}"
run: |-
# Route os.tmpdir() off the host /tmp tmpfs: its inode quota is
# small and shared across the pool, and e2e-build.test.mjs creates
# thousands of chunk files there (#15965 ENOSPC with 202G free).
# Same routing as the unit lanes (#10035), private per-run dir
# included: RUNNER_TEMP is shared, and a cancelled battery never
# reaches its own after() cleanup, so the fixture tree would stick
# around in _work/_temp. A hard kill stays uncovered either way.
export TMPDIR="${RUNNER_TEMP:-${TMPDIR:-/tmp}}"
HELPER_TMPDIR="$(mktemp -d "${TMPDIR}/helper-tests-XXXXXX" 2>/dev/null)" || HELPER_TMPDIR=""
if [ -n "$HELPER_TMPDIR" ]; then
TMPDIR="$HELPER_TMPDIR"
export TMPDIR
trap 'rm -rf "$HELPER_TMPDIR"' EXIT
fi
node --test --test-concurrency=1 ${{ env.HELPER_TESTS }} 2>&1 | tee "${RUNNER_TEMP}/helper-attempt1.log" || {
node --test --test-concurrency=1 ${{ env.HELPER_TESTS }} &&
echo "::warning::helper tests failed once and the retry absorbed it; attempt-1 failures: $(grep -E '^not ok' "${RUNNER_TEMP}/helper-attempt1.log" | head -5 | paste -sd ';' -) — count this toward the #12772 helper-battery recurrence threshold"
+26
View File
@@ -1258,6 +1258,32 @@ describe('GitHub helper tests', () => {
}
});
it('routes the battery tmpdir off the host /tmp tmpfs', () => {
// #15965: e2e-build.test.mjs mkdtemps thousands of chunk files via
// os.tmpdir(); on the ECS pool that defaulted to the shared /tmp tmpfs,
// whose ~1M inode quota filled up and failed the lane with ENOSPC while
// 202G of capacity stayed free. RUNNER_TEMP sits on the roomy data disk,
// same routing the unit lanes already use (#10035).
const step = (ci.jobs.lint_and_static.steps ?? []).find(
(candidate) => candidate.name === 'Run .github/scripts helper tests',
);
expect(step, 'helper-tests step missing').toBeDefined();
// Comments stripped: the rationale above the export names it too, so a pin
// satisfied by prose would stay green with the routing commented out.
const run = String(step.run)
.split('\n')
.filter((line) => !line.trim().startsWith('#'))
.join('\n');
expect(run).toContain('export TMPDIR="${RUNNER_TEMP:-${TMPDIR:-/tmp}}"');
expect(run.indexOf('export TMPDIR')).toBeLessThan(
run.indexOf('node --test'),
);
// The routed dir is private to the run and reaped on exit: RUNNER_TEMP
// itself is shared with every other job on the host.
expect(run).toContain('mktemp -d "${TMPDIR}/helper-tests-XXXXXX"');
expect(run).toContain('trap \'rm -rf "$HELPER_TMPDIR"\' EXIT');
});
it('retries the full-profile battery once against pool contention', () => {
// #12772: the battery spawns real subprocess loops on the shared ECS
// pool, and a push to main has no flaky-rerun patrol — one host hiccup