mirror of
https://github.com/garrytan/gstack.git
synced 2026-09-12 16:08:59 +02:00
fix(redact): install-prepush-hook refreshes a stale managed hook
The marker check returned before the only writer, so once a repo had the hook, no later change to the wrapper could ever reach it. The `printf x` fail-open fix (v1.64.0.0) has still not landed in any repo that received the hook before it, and a wrapper naming a gstack that has since moved stays pointed at a dead path for the same reason. Compare the body against what this version generates: rewrite on drift, stay a no-op when identical. The chained pre-push.local is untouched on both paths. The existing trailing-newline regression test cannot catch this — it installs into a repo with no prior managed hook, the one case that was never broken.
This commit is contained in:
+33
-13
@@ -62,19 +62,6 @@ function installPrepushHook(): void {
|
||||
const hookPath = path.join(dir, "pre-push");
|
||||
const prepushBin = path.join(import.meta.dir, "gstack-redact-prepush");
|
||||
|
||||
// If a non-managed hook exists, preserve it as pre-push.local and chain it.
|
||||
if (fs.existsSync(hookPath)) {
|
||||
const existing = fs.readFileSync(hookPath, "utf8");
|
||||
if (existing.includes(MANAGED_MARKER)) {
|
||||
process.stdout.write("gstack-redact: pre-push hook already installed.\n");
|
||||
return;
|
||||
}
|
||||
const localPath = path.join(dir, "pre-push.local");
|
||||
fs.renameSync(hookPath, localPath);
|
||||
fs.chmodSync(localPath, 0o755);
|
||||
process.stdout.write("gstack-redact: preserved existing hook as pre-push.local (chained).\n");
|
||||
}
|
||||
|
||||
// stdin is single-consume: capture it once, feed both the chained hook and ours.
|
||||
// The `printf x` sentinel preserves the trailing newline that `$(cat)` strips.
|
||||
// Without it, a chained shell pre-push.local built on `while read` silently
|
||||
@@ -91,6 +78,39 @@ if [ -x "$_local" ]; then
|
||||
fi
|
||||
printf '%s' "$_input" | bun "${prepushBin}" "$@"
|
||||
`;
|
||||
|
||||
// If a non-managed hook exists, preserve it as pre-push.local and chain it.
|
||||
if (fs.existsSync(hookPath)) {
|
||||
const existing = fs.readFileSync(hookPath, "utf8");
|
||||
if (existing.includes(MANAGED_MARKER)) {
|
||||
// A hook we already own. Returning here unconditionally froze every
|
||||
// existing install on whatever wrapper it first received: the `printf x`
|
||||
// fail-open fix landed in v1.64.0.0 and still had not reached a single
|
||||
// repo that got the hook before it, because the only writer is gated on
|
||||
// this branch. A wrapper naming a gstack that has since been moved or
|
||||
// removed stays pointed at that dead path for the same reason.
|
||||
//
|
||||
// Rewrite when the body has drifted from what this version generates;
|
||||
// stay a no-op when it has not, so the command is still idempotent. The
|
||||
// chained pre-push.local is never touched on either path — it is the
|
||||
// user's, not ours.
|
||||
if (existing === wrapper) {
|
||||
process.stdout.write("gstack-redact: pre-push hook already installed.\n");
|
||||
return;
|
||||
}
|
||||
fs.writeFileSync(hookPath, wrapper, { mode: 0o755 });
|
||||
fs.chmodSync(hookPath, 0o755);
|
||||
process.stdout.write(
|
||||
`gstack-redact: refreshed stale managed pre-push hook at ${hookPath}\n`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
const localPath = path.join(dir, "pre-push.local");
|
||||
fs.renameSync(hookPath, localPath);
|
||||
fs.chmodSync(localPath, 0o755);
|
||||
process.stdout.write("gstack-redact: preserved existing hook as pre-push.local (chained).\n");
|
||||
}
|
||||
|
||||
fs.writeFileSync(hookPath, wrapper, { mode: 0o755 });
|
||||
fs.chmodSync(hookPath, 0o755);
|
||||
process.stdout.write(`gstack-redact: installed pre-push hook at ${hookPath}\n`);
|
||||
|
||||
Reference in New Issue
Block a user