mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-11-05 21:02:34 +01:00
64 lines
2.2 KiB
TypeScript
64 lines
2.2 KiB
TypeScript
import fs from "node:fs";
|
|
import path from "node:path";
|
|
|
|
/**
|
|
* Removes a per-worker test home at worker exit.
|
|
*
|
|
* A plain recursive `rmSync` is not enough: a native session materializes its
|
|
* runtime-context bundles under the instance root and makes every directory
|
|
* in them read-only (`makeDirectoriesReadOnly` in
|
|
* services/native-runtime/runtime-context.ts), and a run that is still
|
|
* finishing when the worker exits can leave such a bundle under the worker's
|
|
* home. Removing a file inside a read-only directory fails with `EACCES`,
|
|
* Vitest reports it as an unhandled error, and the shard fails after every
|
|
* test passed. So on a permission error every directory in the tree is made
|
|
* writable first and the removal is tried once more.
|
|
*/
|
|
export function removeTestHome(home: string): void {
|
|
const options = { recursive: true, force: true, maxRetries: 8, retryDelay: 50 } as const;
|
|
try {
|
|
fs.rmSync(home, options);
|
|
} catch (error) {
|
|
const permissionError = isPermissionError(error);
|
|
if (!permissionError && (error as NodeJS.ErrnoException | null)?.code !== "ENOTEMPTY") throw error;
|
|
const restored = makeDirectoriesWritable(home);
|
|
if (!permissionError && !restored) throw error;
|
|
fs.rmSync(home, options);
|
|
}
|
|
}
|
|
|
|
function isPermissionError(error: unknown): boolean {
|
|
const code = (error as NodeJS.ErrnoException | null)?.code;
|
|
return code === "EACCES" || code === "EPERM";
|
|
}
|
|
|
|
function makeDirectoriesWritable(directory: string): boolean {
|
|
let stat: fs.Stats;
|
|
try {
|
|
stat = fs.lstatSync(directory);
|
|
} catch {
|
|
return false;
|
|
}
|
|
if (!stat.isDirectory()) return false;
|
|
// Owner read, write and search: enough to list and unlink the children.
|
|
// A symlink is not followed, so nothing outside the home is touched.
|
|
let restored = false;
|
|
if ((stat.mode & 0o700) !== 0o700) {
|
|
try {
|
|
fs.chmodSync(directory, stat.mode | 0o700);
|
|
restored = true;
|
|
} catch {
|
|
}
|
|
}
|
|
let entries: fs.Dirent[];
|
|
try {
|
|
entries = fs.readdirSync(directory, { withFileTypes: true });
|
|
} catch {
|
|
return restored;
|
|
}
|
|
for (const entry of entries) {
|
|
if (entry.isDirectory() && makeDirectoriesWritable(path.join(directory, entry.name))) restored = true;
|
|
}
|
|
return restored;
|
|
}
|