forked from lvrach/git-hooks
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: implement hierarchical Git hook configuration and execution
This commit introduces support for configuring and running Git hooks in a hierarchical manner. It includes: - Global hook configuration in ~/.git-hooks - Local repository-specific hooks in $GIT_DIR/.git-hooks - Backwards compatibility with standard Git hooks The system now supports all Git hook types and allows for flexible, multi-level hook management.
- Loading branch information
Showing
7 changed files
with
238 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
#!/bin/sh | ||
echo "echo from .git-hooks/pre-commit.d/echo.sh" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
package commands | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
"path/filepath" | ||
|
||
"github.com/urfave/cli/v2" | ||
) | ||
|
||
var Add = &cli.Command{ | ||
Name: "add", | ||
Usage: "adds a new git hook", | ||
Subcommands: []*cli.Command{ | ||
{ | ||
Name: "gitleaks", | ||
Usage: "pre-commit hook to run gitleaks detect", | ||
Action: func(c *cli.Context) error { | ||
return addGitLeaks() | ||
}, | ||
}, | ||
}, | ||
} | ||
|
||
func addGitLeaks() error { | ||
hooksDir := filepath.Join(os.Getenv("HOME"), ".git-hooks", "pre-commit.d") | ||
|
||
// Create the pre-commit.d directory if it doesn't exist | ||
err := os.MkdirAll(hooksDir, 0755) | ||
if err != nil { | ||
return fmt.Errorf("failed to create pre-commit.d directory: %w", err) | ||
} | ||
|
||
// Create the gitleaks script | ||
scriptPath := filepath.Join(hooksDir, "gitleaks") | ||
script := `#!/bin/bash | ||
go run github.com/zricethezav/gitleaks/[email protected] protect --staged | ||
` | ||
|
||
err = os.WriteFile(scriptPath, []byte(script), 0755) | ||
if err != nil { | ||
return fmt.Errorf("failed to create gitleaks script: %w", err) | ||
} | ||
|
||
fmt.Printf("Gitleaks pre-commit hook installed at: %s\n", scriptPath) | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
package commands | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
"os/exec" | ||
"path/filepath" | ||
|
||
"github.com/urfave/cli/v2" | ||
) | ||
|
||
var Config = &cli.Command{ | ||
Name: "config", | ||
Usage: "", | ||
Action: func(c *cli.Context) error { | ||
return configureGitHooks() | ||
}, | ||
} | ||
|
||
var gitHooks = []string{ | ||
"applypatch-msg", "pre-applypatch", "post-applypatch", | ||
"pre-commit", "pre-merge-commit", "prepare-commit-msg", "commit-msg", "post-commit", | ||
"pre-rebase", "post-checkout", "post-merge", "pre-push", "pre-receive", | ||
"update", "proc-receive", "post-receive", "post-update", "reference-transaction", | ||
"push-to-checkout", "pre-auto-gc", "post-rewrite", "sendemail-validate", | ||
"fsmonitor-watchman", "p4-changelist", "p4-prepare-changelist", "p4-post-changelist", "p4-pre-submit", | ||
"post-index-change", | ||
} | ||
|
||
func configureGitHooks() error { | ||
hooksDir := filepath.Join(os.Getenv("HOME"), ".git-hooks") | ||
|
||
// Create the directory if it doesn't exist | ||
err := os.MkdirAll(hooksDir, 0755) | ||
if err != nil { | ||
return fmt.Errorf("failed to create hooks directory: %w", err) | ||
} | ||
|
||
// Create a script for each Git hook | ||
for _, hook := range gitHooks { | ||
scriptContent := fmt.Sprintf(`#!/bin/sh | ||
git-hooks hook %s "$@" | ||
`, hook) | ||
|
||
scriptPath := filepath.Join(hooksDir, hook) | ||
err = os.WriteFile(scriptPath, []byte(scriptContent), 0755) | ||
if err != nil { | ||
return fmt.Errorf("failed to create script for %s: %w", hook, err) | ||
} | ||
} | ||
|
||
// Configure Git to use the directory | ||
cmd := exec.Command("git", "config", "--global", "core.hooksPath", hooksDir) | ||
if err := cmd.Run(); err != nil { | ||
return fmt.Errorf("failed to configure Git hooks: %w", err) | ||
} | ||
|
||
fmt.Printf("Git hooks configured to use directory: %s\n", hooksDir) | ||
fmt.Println("Scripts created for all Git hooks") | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
package commands | ||
|
||
import ( | ||
"os" | ||
"os/exec" | ||
"path/filepath" | ||
|
||
"github.com/urfave/cli/v2" | ||
) | ||
|
||
var Hooks = &cli.Command{ | ||
Name: "hook", | ||
Usage: "", | ||
Action: func(c *cli.Context) error { | ||
if c.NArg() == 0 { | ||
return cli.ShowAppHelp(c) | ||
} | ||
|
||
hookName := c.Args().First() | ||
return executeHook(hookName) | ||
}, | ||
} | ||
|
||
func executeHook(hookName string) error { | ||
// 1. Execute global scripts | ||
err := executeScriptsInDir(filepath.Join(os.Getenv("HOME"), ".git-hooks", hookName+".d")) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
err = executeScriptsInDir(filepath.Join(".git-hooks", hookName+".d")) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
// 3. Execute standard Git hook for backwards compatibility | ||
gitDir := os.Getenv("GIT_DIR") | ||
if gitDir == "" { | ||
gitDir = ".git" | ||
} | ||
return executeStandardGitHook(filepath.Join(gitDir, "hooks", hookName)) | ||
} | ||
|
||
func executeScriptsInDir(dir string) error { | ||
files, err := os.ReadDir(dir) | ||
if err != nil { | ||
if os.IsNotExist(err) { | ||
return nil // Directory doesn't exist, which is fine | ||
} | ||
return err | ||
} | ||
|
||
for _, file := range files { | ||
if !file.IsDir() { | ||
scriptPath := filepath.Join(dir, file.Name()) | ||
if err := executeScript(scriptPath); err != nil { | ||
return err | ||
} | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func executeScript(path string) error { | ||
cmd := exec.Command(path) | ||
cmd.Stdout = os.Stdout | ||
cmd.Stderr = os.Stderr | ||
return cmd.Run() | ||
} | ||
|
||
func executeStandardGitHook(path string) error { | ||
if _, err := os.Stat(path); err == nil { | ||
return executeScript(path) | ||
} | ||
return nil // Hook doesn't exist, which is fine | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
module github.com/lvrach/git-hooks | ||
|
||
go 1.22.4 | ||
|
||
require github.com/urfave/cli/v2 v2.27.4 | ||
|
||
require ( | ||
github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect | ||
github.com/russross/blackfriday/v2 v2.1.0 // indirect | ||
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4= | ||
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= | ||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= | ||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= | ||
github.com/urfave/cli/v2 v2.27.4 h1:o1owoI+02Eb+K107p27wEX9Bb8eqIoZCfLXloLUSWJ8= | ||
github.com/urfave/cli/v2 v2.27.4/go.mod h1:m4QzxcD2qpra4z7WhzEGn74WZLViBnMpb1ToCAKdGRQ= | ||
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4= | ||
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
package main | ||
|
||
import ( | ||
"log" | ||
"os" | ||
|
||
"github.com/lvrach/git-hooks/commands" | ||
"github.com/urfave/cli/v2" | ||
) | ||
|
||
func main() { | ||
app := &cli.App{ | ||
Name: "git-hooks-manager", | ||
Usage: "Manage and execute Git hooks", | ||
Flags: []cli.Flag{ | ||
&cli.BoolFlag{ | ||
Name: "configure", | ||
Usage: "Configure Git to use this tool for hooks", | ||
}, | ||
}, | ||
Commands: []*cli.Command{ | ||
commands.Config, | ||
commands.Hooks, | ||
commands.Add, | ||
}, | ||
} | ||
|
||
err := app.Run(os.Args) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
} |