36 lines
1.4 KiB
JavaScript
36 lines
1.4 KiB
JavaScript
import { lstatSync, mkdirSync, mkdtempSync, readFileSync, realpathSync, rmSync, writeFileSync } from 'node:fs';
|
|
import { basename, join, relative, resolve, sep } from 'node:path';
|
|
import { randomUUID } from 'node:crypto';
|
|
|
|
export const OWNERSHIP_FILE = '.maestro-e2e-owned';
|
|
|
|
export function createOwnedRunDir(cacheRoot, prefix) {
|
|
mkdirSync(cacheRoot, { recursive: true });
|
|
const directory = mkdtempSync(join(realpathSync(cacheRoot), prefix));
|
|
const token = randomUUID();
|
|
writeFileSync(join(directory, OWNERSHIP_FILE), token, { mode: 0o600 });
|
|
return { directory, token };
|
|
}
|
|
|
|
export function cleanupOwnedRunDir({ cacheRoot, directory, prefix, token }) {
|
|
if (!directory || !token || !prefix) return false;
|
|
let rootReal;
|
|
let directoryReal;
|
|
try {
|
|
rootReal = realpathSync(cacheRoot);
|
|
if (lstatSync(directory).isSymbolicLink()) return false;
|
|
directoryReal = realpathSync(directory);
|
|
} catch (error) {
|
|
if (error?.code === 'ENOENT') return false;
|
|
throw error;
|
|
}
|
|
const rel = relative(rootReal, directoryReal);
|
|
if (!rel || rel === '..' || rel.startsWith(`..${sep}`) || resolve(rootReal, rel) !== directoryReal) return false;
|
|
if (!basename(directoryReal).startsWith(prefix)) return false;
|
|
let actualToken;
|
|
try { actualToken = readFileSync(join(directoryReal, OWNERSHIP_FILE), 'utf8'); } catch { return false; }
|
|
if (actualToken !== token) return false;
|
|
rmSync(directoryReal, { recursive: true, force: true });
|
|
return true;
|
|
}
|