fix(install): preserve the backup and warn loudly on a failed materialize rollback

If renameSync(partial, dest) failed AND the rollback renameSync(backup, dest)
also failed, the grammar was left unmaterialized (node_modules/<name> missing)
with only a generic "could not materialize" warning — the recoverable backup at
<dest>.materialize-bak was unmentioned. Emit a CRITICAL warning naming the
backup path and the recovery command on that double-failure, and document that
the fail-soft catch removes only the scratch `partial`, never the `backup`
(which may be the sole recoverable copy). Never-throw / exit-0 contract intact.
This commit is contained in:
Gergo Magyar 2026-06-09 13:12:17 +00:00
parent 8f795ff352
commit 19e5859e11

View file

@ -63,20 +63,31 @@ for (const name of VENDORED_GRAMMARS) {
fs.renameSync(partial, dest);
} catch (renameErr) {
// Best-effort rollback: restore the previous dest from backup.
let restored = false;
if (fs.existsSync(backup)) {
try {
fs.renameSync(backup, dest);
restored = true;
} catch {
// If rollback also fails, the prior backup directory still exists on
// disk — the catch block below surfaces both errors via the warning.
// Rollback also failed — dest is now missing. Leave the backup in
// place (the catch below will NOT remove it) and surface where it is.
}
}
if (!restored && fs.existsSync(backup)) {
console.warn(
`[gitnexus] CRITICAL: could not materialize vendor/${name} AND could not restore the ` +
`previous node_modules/${name}. A recoverable copy remains at ${backup} — ` +
`restore it (e.g. \`mv ${backup} ${dest}\`) or reinstall to recover ${name}.`,
);
}
throw renameErr;
}
fs.rmSync(backup, { recursive: true, force: true });
} catch (err) {
// Fail-soft: a single locked/inaccessible file (common on Windows) must not
// abort the whole gitnexus install. Matches build-tree-sitter-*.cjs pattern.
// Only remove the scratch `partial`; never the `backup` (it may be the sole
// recoverable copy after a failed rollback above).
fs.rmSync(partial, { recursive: true, force: true });
console.warn(`[gitnexus] Could not materialize vendor/${name}: ${err.message}`);
console.warn(