mirror of https://github.com/garrytan/gstack.git
fix: stage legacy team hook deletion
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
parent
5f1e896f90
commit
7233eb0d60
|
|
@ -220,6 +220,9 @@ HOOK_EOF
|
|||
# registered successfully. Other project hooks are left untouched.
|
||||
if [ -e "$HOOKS_DIR/check-gstack.sh" ] || [ -L "$HOOKS_DIR/check-gstack.sh" ]; then
|
||||
rm "$HOOKS_DIR/check-gstack.sh"
|
||||
if (cd "$REPO_ROOT" && git ls-files --error-unmatch -- .claude/hooks/check-gstack.sh >/dev/null 2>&1); then
|
||||
GENERATED+=(".claude/hooks/check-gstack.sh")
|
||||
fi
|
||||
echo " - .claude/hooks/check-gstack.sh — removed legacy enforcement hook"
|
||||
fi
|
||||
else
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
<li>The generated hook resolves the user home in <code>HOME</code>, <code>USERPROFILE</code>, then <code>os.homedir()</code> order so POSIX, Git Bash, PowerShell, and cmd runners use the expected global install root.</li>
|
||||
<li>The hook contract follows <code>docs/spikes/claude-code-hook-mutation.md</code>: pass-through writes <code>{}</code>; denial writes a <code>hookSpecificOutput</code> object for <code>PreToolUse</code> with <code>permissionDecision: "deny"</code>.</li>
|
||||
<li>Missing or unreadable gstack is an intentional denial with exit code zero. Install instructions are present in stderr and <code>permissionDecisionReason</code>, so the runner does not report a hook execution error.</li>
|
||||
<li>Rerunning required mode replaces a matching legacy <code>check-gstack.sh</code> registration, removes duplicate matching registrations, and deletes the obsolete Bash hook only after the CommonJS hook is generated and registered successfully. Unrelated project hooks remain unchanged.</li>
|
||||
<li>Rerunning required mode replaces a matching legacy <code>check-gstack.sh</code> registration, removes duplicate matching registrations, and deletes the obsolete Bash hook only after the CommonJS hook is generated and registered successfully. If Git tracks the deleted hook, the printed <code>git add</code> command includes its path so the suggested migration commit stages the deletion; untracked removed hooks are omitted because they have no index change. Unrelated project hooks remain unchanged.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Deviations</h2>
|
||||
|
|
|
|||
|
|
@ -302,7 +302,7 @@ describe('gstack-team-init', () => {
|
|||
});
|
||||
|
||||
test('required: creates enforcement hook', () => {
|
||||
run(`${TEAM_INIT} required`, { cwd: tmpDir });
|
||||
const result = run(`${TEAM_INIT} required`, { cwd: tmpDir });
|
||||
const hookPath = path.join(tmpDir, '.claude', 'hooks', 'check-gstack.cjs');
|
||||
expect(fs.existsSync(hookPath)).toBe(true);
|
||||
expect(
|
||||
|
|
@ -319,6 +319,7 @@ describe('gstack-team-init', () => {
|
|||
expect(hook).toContain("permissionDecision: 'deny'");
|
||||
expect(hook).toContain('BLOCKED: gstack is not installed');
|
||||
expect(hook).not.toContain('#!/bin/bash');
|
||||
expect(result.stdout).not.toMatch(/git add .*check-gstack\.sh/);
|
||||
});
|
||||
|
||||
test('required: registers one shell-neutral project hook', () => {
|
||||
|
|
@ -534,22 +535,45 @@ describe('gstack-team-init', () => {
|
|||
execSync('git add .claude', { cwd: tmpDir });
|
||||
execSync('git commit -m "add legacy hook"', { cwd: tmpDir });
|
||||
|
||||
run(`${TEAM_INIT} required`, { cwd: tmpDir });
|
||||
const migration = run(`${TEAM_INIT} required`, { cwd: tmpDir });
|
||||
expect(fs.existsSync(legacyHook)).toBe(false);
|
||||
expect(fs.readFileSync(unrelatedHook, 'utf-8')).toBe(unrelatedHookContents);
|
||||
const suggestedGitAdd = migration.stdout
|
||||
.split('\n')
|
||||
.find(line => line.startsWith(' git add '))
|
||||
?.trim();
|
||||
if (!suggestedGitAdd) {
|
||||
throw new Error('gstack-team-init did not print a git add command');
|
||||
}
|
||||
expect(suggestedGitAdd.split(/\s+/)).toContain(
|
||||
'.claude/hooks/check-gstack.sh',
|
||||
);
|
||||
execSync(suggestedGitAdd, { cwd: tmpDir });
|
||||
execSync('git commit -m "migrate required hook"', { cwd: tmpDir });
|
||||
expect(
|
||||
execSync('git status --short', { cwd: tmpDir, encoding: 'utf-8' }),
|
||||
).toBe('');
|
||||
|
||||
run(`${TEAM_INIT} required`, { cwd: tmpDir });
|
||||
const rerun = run(`${TEAM_INIT} required`, { cwd: tmpDir });
|
||||
const settings = JSON.parse(
|
||||
fs.readFileSync(path.join(tmpDir, '.claude', 'settings.json'), 'utf-8'),
|
||||
);
|
||||
expect(fs.existsSync(legacyHook)).toBe(false);
|
||||
expect(fs.readFileSync(unrelatedHook, 'utf-8')).toBe(unrelatedHookContents);
|
||||
const rerunGitAdd = rerun.stdout
|
||||
.split('\n')
|
||||
.find(line => line.startsWith(' git add '))
|
||||
?.trim();
|
||||
if (!rerunGitAdd) {
|
||||
throw new Error('gstack-team-init rerun did not print a git add command');
|
||||
}
|
||||
expect(rerunGitAdd.split(/\s+/)).not.toContain(
|
||||
'.claude/hooks/check-gstack.sh',
|
||||
);
|
||||
execSync(rerunGitAdd, { cwd: tmpDir });
|
||||
expect(
|
||||
execSync('git status --short -- .claude/hooks/check-gstack.sh', {
|
||||
cwd: tmpDir,
|
||||
encoding: 'utf-8',
|
||||
}),
|
||||
).toBe(' D .claude/hooks/check-gstack.sh\n');
|
||||
execSync('git status --short', { cwd: tmpDir, encoding: 'utf-8' }),
|
||||
).toBe('');
|
||||
expect(settings.hooks.PreToolUse).toHaveLength(1);
|
||||
expect(settings.hooks.PreToolUse[0].hooks).toHaveLength(1);
|
||||
expect(settings.hooks.PreToolUse[0].hooks[0].command).toContain(
|
||||
|
|
|
|||
Loading…
Reference in New Issue