Files
gstack/lib/path-spelling.ts
T
e6edff0265 v1.91.89.0 fix(windows,setup): Git Bash hook false-positive recovery, incoming-checker auto-update, setup never deletes its own checkout (Wave 9 Lane A0) (#3140)
Git Bash hands native Bun C:/ paths while gstack-hook-check looked them up as
/c/, so every TypeScript hook was refused and team-mode auto-update held every
release since 1.91.31. The checker maps Bun's paths back to the shell spelling,
the auto-updater lets the incoming release's own checker decide (installed
checker only as a fallback, hook-check-unavailable when neither verifies), and
docs/troubleshooting.md gives stuck installs one recovery command.
/gstack-upgrade and /spec guards use the --show-prefix root check, gstack-wtree
treats drive-letter --git-path answers as absolute, and setup refuses to
replace a runtime root that is or contains its own checkout, Kiro's in-place
root included (#3084), with --status suggesting the move. New sourced helper
bin/gstack-path-spelling.sh and TS twin lib/path-spelling.ts.

Co-authored-by: yuriysan <63948747+yuriysan@users.noreply.github.com>
Co-authored-by: y$un_ <3579304+szsunyuan@users.noreply.github.com>
Co-authored-by: Frederik Reventlow <267160539+dmfrede@users.noreply.github.com>
Co-authored-by: skleinbe-ny <226563731+skleinbe-ny@users.noreply.github.com>
Co-authored-by: bkermen <184220642+bkermen@users.noreply.github.com>
Co-authored-by: Drew Jankowski <3200063+andrewkjankowski@users.noreply.github.com>
2026-10-11 16:48:11 +00:00

40 lines
1.8 KiB
TypeScript

/**
* One spelling for paths on Windows shells. Git Bash (MSYS) spells a drive
* path /c/x, native programs (git.exe, bun.exe) answer C:/x or C:\x, and MSYS
* converts /c/x arguments to C:/x before a native program starts. Comparing two
* spellings as strings is the bug class behind #3088, #3100 and #3101.
* Bash twin: bin/gstack-path-spelling.sh.
*/
import * as fs from 'node:fs';
/** Git Bash's drive spelling (/c/x) of a native path on Windows; unchanged elsewhere. */
export function shellPath(value: string, platform: string = process.platform): string {
if (platform !== 'win32') return value;
return value.replace(/\\/g, '/').replace(/^([A-Za-z]):(?=\/|$)/, (_, drive: string) => `/${drive.toLowerCase()}`);
}
/** The native spelling (C:/x) of a Git Bash drive path on Windows; unchanged elsewhere. */
export function nativePath(value: string, platform: string = process.platform): string {
if (platform !== 'win32') return value;
const slashed = value.replace(/\\/g, '/');
const drive = /^\/([A-Za-z])(?=\/|$)/.exec(slashed);
if (!drive) return slashed;
return `${drive[1]!.toUpperCase()}:${slashed.slice(2) || '/'}`;
}
/** True when a and b name the same file: filesystem identity when both exist, else equal spellings. */
export function samePath(a: string, b: string, platform: string = process.platform): boolean {
const left = nativePath(a, platform);
const right = nativePath(b, platform);
try {
const sa = fs.statSync(left, { bigint: true });
const sb = fs.statSync(right, { bigint: true });
return sa.dev === sb.dev && sa.ino === sb.ino;
} catch {
const trim = (v: string) => (v.length > 1 ? v.replace(/\/+$/, '') : v);
return platform === 'win32'
? trim(left).toLowerCase() === trim(right).toLowerCase()
: trim(left) === trim(right);
}
}