From 991c6248ca88e668976c7b86ef9998e0905cd71f Mon Sep 17 00:00:00 2001 From: Steve Yegge Date: Thu, 6 Nov 2025 15:00:15 -0800 Subject: [PATCH] Add git hooks support and refactor info command --- cmd/bd/hooks.go | 107 ++++++++++++++++++++++++++++++++++ cmd/bd/info.go | 8 +++ examples/git-hooks/post-merge | 1 + examples/git-hooks/pre-commit | 1 + examples/git-hooks/pre-push | 1 + 5 files changed, 118 insertions(+) create mode 100644 cmd/bd/hooks.go diff --git a/cmd/bd/hooks.go b/cmd/bd/hooks.go new file mode 100644 index 00000000..c1de65e4 --- /dev/null +++ b/cmd/bd/hooks.go @@ -0,0 +1,107 @@ +package main + +import ( + "bufio" + "fmt" + "os" + "path/filepath" + "strings" +) + +const hookVersionPrefix = "# bd-hooks-version: " + +// HookStatus represents the status of a single git hook +type HookStatus struct { + Name string + Installed bool + Version string + Outdated bool +} + +// CheckGitHooks checks the status of bd git hooks in .git/hooks/ +func CheckGitHooks() ([]HookStatus, error) { + hooks := []string{"pre-commit", "post-merge", "pre-push"} + statuses := make([]HookStatus, 0, len(hooks)) + + for _, hookName := range hooks { + status := HookStatus{ + Name: hookName, + } + + // Check if hook exists + hookPath := filepath.Join(".git", "hooks", hookName) + version, err := getHookVersion(hookPath) + if err != nil { + // Hook doesn't exist or couldn't be read + status.Installed = false + } else { + status.Installed = true + status.Version = version + + // Check if outdated (compare to current bd version) + if version != "" && version != Version { + status.Outdated = true + } + } + + statuses = append(statuses, status) + } + + return statuses, nil +} + +// getHookVersion extracts the version from a hook file +func getHookVersion(path string) (string, error) { + file, err := os.Open(path) + if err != nil { + return "", err + } + defer file.Close() + + scanner := bufio.NewScanner(file) + // Read first few lines looking for version marker + lineCount := 0 + for scanner.Scan() && lineCount < 10 { + line := scanner.Text() + if strings.HasPrefix(line, hookVersionPrefix) { + version := strings.TrimSpace(strings.TrimPrefix(line, hookVersionPrefix)) + return version, nil + } + lineCount++ + } + + // No version found (old hook) + return "", nil +} + +// FormatHookWarnings returns a formatted warning message if hooks are outdated +func FormatHookWarnings(statuses []HookStatus) string { + var warnings []string + + missingCount := 0 + outdatedCount := 0 + + for _, status := range statuses { + if !status.Installed { + missingCount++ + } else if status.Outdated { + outdatedCount++ + } + } + + if missingCount > 0 { + warnings = append(warnings, fmt.Sprintf("⚠️ Git hooks not installed (%d missing)", missingCount)) + warnings = append(warnings, " Run: examples/git-hooks/install.sh") + } + + if outdatedCount > 0 { + warnings = append(warnings, fmt.Sprintf("⚠️ Git hooks are outdated (%d hooks)", outdatedCount)) + warnings = append(warnings, " Run: examples/git-hooks/install.sh") + } + + if len(warnings) > 0 { + return strings.Join(warnings, "\n") + } + + return "" +} diff --git a/cmd/bd/info.go b/cmd/bd/info.go index 286b0817..c26754da 100644 --- a/cmd/bd/info.go +++ b/cmd/bd/info.go @@ -196,6 +196,14 @@ Examples: } } + // Check git hooks status + hookStatuses, err := CheckGitHooks() + if err == nil { + if warning := FormatHookWarnings(hookStatuses); warning != "" { + fmt.Printf("\n%s\n", warning) + } + } + fmt.Println() }, } diff --git a/examples/git-hooks/post-merge b/examples/git-hooks/post-merge index 0b3dc139..676cd251 100755 --- a/examples/git-hooks/post-merge +++ b/examples/git-hooks/post-merge @@ -1,4 +1,5 @@ #!/bin/sh +# bd-hooks-version: 0.22.0 # # bd (beads) post-merge hook # diff --git a/examples/git-hooks/pre-commit b/examples/git-hooks/pre-commit index e2dd2992..ffdeeb5c 100755 --- a/examples/git-hooks/pre-commit +++ b/examples/git-hooks/pre-commit @@ -1,4 +1,5 @@ #!/bin/sh +# bd-hooks-version: 0.22.0 # # bd (beads) pre-commit hook # diff --git a/examples/git-hooks/pre-push b/examples/git-hooks/pre-push index 05ca5feb..e096e830 100755 --- a/examples/git-hooks/pre-push +++ b/examples/git-hooks/pre-push @@ -1,4 +1,5 @@ #!/bin/sh +# bd-hooks-version: 0.22.0 # # bd (beads) pre-push hook #