mirror of
https://github.com/garrytan/gstack.git
synced 2026-09-09 22:48:57 +02:00
fs.mkdirSync(dir, { recursive: true }) is a no-op on an existing directory
in Node, but bun on Windows throws EEXIST - crashing hook install on any
repo whose .git/hooks already existed, leaving the repo unprotected.
Add lib/fs-utils.ts mkdirpSync: swallow EEXIST only when statSync confirms
the path is an existing directory; a regular file occupying the path, a
stat failure, or any other errno still rethrows. Use it in
installPrepushHook().
The regression test emulates the Windows bun fs semantics via a
bun --preload fixture, so the exact crash path runs (and fails on the old
code) on any platform, including CI Linux.
Absorbed from PR #2641 with authorship preserved.
Fixes #2635
28 lines
979 B
TypeScript
28 lines
979 B
TypeScript
import { mkdirSync, statSync } from "fs";
|
|
|
|
/**
|
|
* mkdir -p that tolerates the target directory already existing.
|
|
*
|
|
* Node's mkdirSync(dir, { recursive: true }) is a no-op when dir already
|
|
* exists, but bun on Windows throws EEXIST in the same situation (#2635),
|
|
* which crashed `gstack-redact install-prepush-hook` on any repo whose
|
|
* .git/hooks already existed. Swallow EEXIST only when statSync confirms the
|
|
* path is an existing directory; anything else - a regular file occupying the
|
|
* path, a stat failure, a different errno - rethrows the original error, so a
|
|
* real collision still fails loudly.
|
|
*/
|
|
export function mkdirpSync(dir: string): void {
|
|
try {
|
|
mkdirSync(dir, { recursive: true });
|
|
} catch (e) {
|
|
if ((e as NodeJS.ErrnoException | null)?.code === "EEXIST") {
|
|
try {
|
|
if (statSync(dir).isDirectory()) return;
|
|
} catch {
|
|
// stat failed - fall through and rethrow the original mkdir error
|
|
}
|
|
}
|
|
throw e;
|
|
}
|
|
}
|