Files
deepseek-harness/scripts/test-fixture-cleanup.ts
T
Chinesezjc b5a130063d fix(scripts): widen fixture-cleanup EPERM retry window
The failover Windows pool releases child-process and antivirus file
handles slower than the hosted pool; a 1-second window (10 x 100 ms)
still exhausts before release under load, so install-lefthook fixture
cleanup threw EPERM in rmSync. A 10-second window (50 x 200 ms) covers
the slower release without pinning afterEach cleanup, since a
terminated child's handles drain once rather than reacquire.
2026-08-13 15:16:12 +08:00

50 lines
1.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Junction-safe fixture cleanup for Windows. Test fixtures junction the REAL
* `scripts/`, `node_modules`, and tsx package directories so installer probes
* resolve through them; Windows recursive deletion — both Node's `rmSync` and
* Git's `worktree remove` — follows MOUNT_POINT junctions into their targets
* and would delete the repository's own directories. POSIX `unlink`/`rm`
* already remove symlinks without following them, so the walk is a no-op
* there.
*/
import { lstatSync, readdirSync, rmSync, unlinkSync } from 'node:fs'
import { join } from 'node:path'
/**
* Recursively unlink every symbolic link (junction) under `path`.
* @param path - the fixture tree whose reparse points are unlinked.
*/
export function unlinkFixtureLinks(path: string): void {
const visit = (entry: string): void => {
let stat: ReturnType<typeof lstatSync>
try {
stat = lstatSync(entry)
} catch (error) {
if ((error as NodeJS.ErrnoException).code === 'ENOENT') return
throw error
}
if (stat.isSymbolicLink() || !stat.isDirectory()) {
if (stat.isSymbolicLink()) unlinkSync(entry)
return
}
for (const child of readdirSync(entry)) visit(join(entry, child))
}
visit(path)
}
/**
* Remove one fixture tree after its junctions are unlinked (see
* {@link unlinkFixtureLinks}). Retries the removal: Windows releases child
* process and antivirus file handles asynchronously, and an unretried
* `rmSync` fails immediately with EPERM under load. A 10-second retry window
* (50 attempts × 200 ms) covers the failover pool's slow handle release;
* release is one-shot (a terminated child's handles drain, not reacquired),
* so a bounded window suffices and never pins afterEach cleanup.
* @param path - the fixture tree to remove.
*/
export function removeFixtureSafely(path: string): void {
unlinkFixtureLinks(path)
rmSync(path, { recursive: true, force: true, maxRetries: 50, retryDelay: 200 })
}