From 1064edc7d06b58b94b6e86c25480edb1c9f10ff5 Mon Sep 17 00:00:00 2001 From: Valentin Kiselev Date: Fri, 20 Sep 2024 11:42:07 +0300 Subject: [PATCH 1/2] fix: support whitespaces in directory names for scripts --- internal/git/state.go | 9 +++-- internal/lefthook/runner/exec/execute_unix.go | 4 +- internal/lefthook/runner/exec/executor.go | 2 +- internal/lefthook/runner/prepare_command.go | 8 ++-- .../lefthook/runner/prepare_command_test.go | 37 ++++++++++--------- internal/lefthook/runner/prepare_script.go | 13 +++---- internal/lefthook/runner/runner.go | 2 +- internal/lefthook/runner/runner_test.go | 4 +- 8 files changed, 40 insertions(+), 39 deletions(-) diff --git a/internal/git/state.go b/internal/git/state.go index 69d6dfbf..09e96ea8 100644 --- a/internal/git/state.go +++ b/internal/git/state.go @@ -21,18 +21,19 @@ var refBranchRegexp = regexp.MustCompile(`^ref:\s*refs/heads/(.+)$`) func (r *Repository) State() State { branch := r.Branch() - if r.isMergeState() { + if r.inMergeState() { return State{ Branch: branch, Step: MergeStep, } } - if r.isRebaseState() { + if r.inRebaseState() { return State{ Branch: branch, Step: RebaseStep, } } + return State{ Branch: branch, Step: NilStep, @@ -65,14 +66,14 @@ func (r *Repository) Branch() string { return "" } -func (r *Repository) isMergeState() bool { +func (r *Repository) inMergeState() bool { if _, err := r.Fs.Stat(filepath.Join(r.GitPath, "MERGE_HEAD")); os.IsNotExist(err) { return false } return true } -func (r *Repository) isRebaseState() bool { +func (r *Repository) inRebaseState() bool { if _, mergeErr := r.Fs.Stat(filepath.Join(r.GitPath, "rebase-merge")); os.IsNotExist(mergeErr) { if _, applyErr := r.Fs.Stat(filepath.Join(r.GitPath, "rebase-apply")); os.IsNotExist(applyErr) { return false diff --git a/internal/lefthook/runner/exec/execute_unix.go b/internal/lefthook/runner/exec/execute_unix.go index 45efaab0..17854bbf 100644 --- a/internal/lefthook/runner/exec/execute_unix.go +++ b/internal/lefthook/runner/exec/execute_unix.go @@ -74,8 +74,8 @@ func (e CommandExecutor) Execute(ctx context.Context, opts Options, in io.Reader return nil } -func (e CommandExecutor) execute(ctx context.Context, cmdstr string, args *executeArgs) error { - command := exec.CommandContext(ctx, "sh", "-c", cmdstr) +func (e CommandExecutor) execute(ctx context.Context, cmd []string, args *executeArgs) error { + command := exec.CommandContext(ctx, "sh", "-c", strings.Join(cmd, " ")) command.Dir = args.root command.Env = append(os.Environ(), args.envs...) diff --git a/internal/lefthook/runner/exec/executor.go b/internal/lefthook/runner/exec/executor.go index 520536ad..75691c67 100644 --- a/internal/lefthook/runner/exec/executor.go +++ b/internal/lefthook/runner/exec/executor.go @@ -8,7 +8,7 @@ import ( // Options contains the data that controls the execution. type Options struct { Name, Root string - Commands []string + Commands [][]string Env map[string]string Interactive, UseStdin bool } diff --git a/internal/lefthook/runner/prepare_command.go b/internal/lefthook/runner/prepare_command.go index e4e11378..c928b823 100644 --- a/internal/lefthook/runner/prepare_command.go +++ b/internal/lefthook/runner/prepare_command.go @@ -15,7 +15,7 @@ import ( // An object that describes the single command's run option. type run struct { - commands []string + commands [][]string files []string } @@ -205,7 +205,7 @@ func escapeFiles(files []string) []string { func replaceInChunks(str string, templates map[string]*template, maxlen int) *run { if len(templates) == 0 { return &run{ - commands: []string{str}, + commands: [][]string{strings.Split(str, " ")}, } } @@ -230,7 +230,7 @@ func replaceInChunks(str string, templates map[string]*template, maxlen int) *ru } var exhausted int - commands := make([]string, 0) + commands := make([][]string, 0) for { command := str for name, template := range templates { @@ -244,7 +244,7 @@ func replaceInChunks(str string, templates map[string]*template, maxlen int) *ru } log.Debug("[lefthook] executing: ", command) - commands = append(commands, command) + commands = append(commands, strings.Split(command, " ")) if exhausted >= len(templates) { break } diff --git a/internal/lefthook/runner/prepare_command_test.go b/internal/lefthook/runner/prepare_command_test.go index c4ead4e9..2b4d608a 100644 --- a/internal/lefthook/runner/prepare_command_test.go +++ b/internal/lefthook/runner/prepare_command_test.go @@ -2,6 +2,7 @@ package runner import ( "fmt" + "slices" "testing" ) @@ -103,7 +104,7 @@ func TestReplaceInChunks(t *testing.T) { }, maxlen: 300, res: &run{ - commands: []string{"echo file1 file2 file3"}, + commands: [][]string{{"echo", "file1", "file2", "file3"}}, files: []string{"file1", "file2", "file3"}, }, }, @@ -117,10 +118,10 @@ func TestReplaceInChunks(t *testing.T) { }, maxlen: 10, res: &run{ - commands: []string{ - "echo file1", - "echo file2", - "echo file3", + commands: [][]string{ + {"echo", "file1"}, + {"echo", "file2"}, + {"echo", "file3"}, }, files: []string{"file1", "file2", "file3"}, }, @@ -135,9 +136,9 @@ func TestReplaceInChunks(t *testing.T) { }, maxlen: 49, // (49 - 17(len of command without templates)) / 2 = 16, but we need 17 (3 words + 2 spaces) res: &run{ - commands: []string{ - "echo file1 file2 && git add file1 file2", - "echo file3 && git add file3", + commands: [][]string{ + {"echo", "file1", "file2", "&&", "git", "add", "file1", "file2"}, + {"echo", "file3", "&&", "git", "add", "file3"}, }, files: []string{"file1", "file2", "file3"}, }, @@ -152,8 +153,8 @@ func TestReplaceInChunks(t *testing.T) { }, maxlen: 51, res: &run{ - commands: []string{ - "echo file1 file2 file3 && git add file1 file2 file3", + commands: [][]string{ + {"echo", "file1", "file2", "file3", "&&", "git", "add", "file1", "file2", "file3"}, }, files: []string{"file1", "file2", "file3"}, }, @@ -172,9 +173,9 @@ func TestReplaceInChunks(t *testing.T) { }, maxlen: 10, res: &run{ - commands: []string{ - "echo push-file && git add file1", - "echo push-file && git add file2", + commands: [][]string{ + {"echo", "push-file", "&&", "git", "add", "file1"}, + {"echo", "push-file", "&&", "git", "add", "file2"}, }, files: []string{"push-file", "file1", "file2"}, }, @@ -193,10 +194,10 @@ func TestReplaceInChunks(t *testing.T) { }, maxlen: 27, res: &run{ - commands: []string{ - "echo push1 && git add file1", - "echo push2 && git add file2", - "echo push3 && git add file2", + commands: [][]string{ + {"echo", "push1", "&&", "git", "add", "file1"}, + {"echo", "push2", "&&", "git", "add", "file2"}, + {"echo", "push3", "&&", "git", "add", "file2"}, }, files: []string{"push1", "push2", "push3", "file1", "file2"}, }, @@ -212,7 +213,7 @@ func TestReplaceInChunks(t *testing.T) { t.Errorf("expected commands to be %d instead of %d", len(tt.res.commands), len(res.commands)) } else { for i, command := range res.commands { - if command != tt.res.commands[i] { + if !slices.Equal(command, tt.res.commands[i]) { t.Errorf("expected command %v to be equal to %v", command, tt.res.commands[i]) } } diff --git a/internal/lefthook/runner/prepare_script.go b/internal/lefthook/runner/prepare_script.go index 7ce8dee1..554caac9 100644 --- a/internal/lefthook/runner/prepare_script.go +++ b/internal/lefthook/runner/prepare_script.go @@ -2,32 +2,31 @@ package runner import ( "os" - "strings" "github.com/evilmartians/lefthook/internal/config" "github.com/evilmartians/lefthook/internal/log" ) -func (r *Runner) prepareScript(script *config.Script, path string, file os.FileInfo) (string, error) { +func (r *Runner) prepareScript(script *config.Script, path string, file os.FileInfo) ([]string, error) { if script.DoSkip(r.Repo.State()) { - return "", &skipError{"settings"} + return nil, &skipError{"settings"} } if intersect(r.Hook.ExcludeTags, script.Tags) { - return "", &skipError{"excluded tags"} + return nil, &skipError{"excluded tags"} } // Skip non-regular files (dirs, symlinks, sockets, etc.) if !file.Mode().IsRegular() { log.Debugf("[lefthook] file %s is not a regular file, skipping", file.Name()) - return "", &skipError{"not a regular file"} + return nil, &skipError{"not a regular file"} } // Make sure file is executable if (file.Mode() & executableMask) == 0 { if err := r.Repo.Fs.Chmod(path, executableFileMode); err != nil { log.Errorf("Couldn't change file mode to make file executable: %s", err) - return "", err + return nil, err } } @@ -39,5 +38,5 @@ func (r *Runner) prepareScript(script *config.Script, path string, file os.FileI args = append(args, path) args = append(args, r.GitArgs...) - return strings.Join(args, " "), nil + return args, nil } diff --git a/internal/lefthook/runner/runner.go b/internal/lefthook/runner/runner.go index 7579dc79..91b002fa 100644 --- a/internal/lefthook/runner/runner.go +++ b/internal/lefthook/runner/runner.go @@ -358,7 +358,7 @@ func (r *Runner) runScript(ctx context.Context, script *config.Script, path stri ok := r.run(ctx, exec.Options{ Name: file.Name(), Root: r.Repo.RootPath, - Commands: []string{command}, + Commands: [][]string{command}, Interactive: script.Interactive && !r.DisableTTY, UseStdin: script.UseStdin, Env: script.Env, diff --git a/internal/lefthook/runner/runner_test.go b/internal/lefthook/runner/runner_test.go index 2af044e6..fe8c7b70 100644 --- a/internal/lefthook/runner/runner_test.go +++ b/internal/lefthook/runner/runner_test.go @@ -29,10 +29,10 @@ type ( ) func (e executor) Execute(_ctx context.Context, opts exec.Options, _in io.Reader, _out io.Writer) (err error) { - if strings.HasPrefix(opts.Commands[0], "success") { + if strings.HasPrefix(opts.Commands[0][0], "success") { err = nil } else { - err = errors.New(opts.Commands[0]) + err = errors.New(opts.Commands[0][0]) } return From 0fc15a83c4d3dc9c999c536a0a61b91874178e91 Mon Sep 17 00:00:00 2001 From: Valentin Kiselev Date: Fri, 20 Sep 2024 13:51:59 +0300 Subject: [PATCH 2/2] fix: fix for windows --- internal/lefthook/runner/exec/execute_windows.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/internal/lefthook/runner/exec/execute_windows.go b/internal/lefthook/runner/exec/execute_windows.go index 47d72757..a4967db5 100644 --- a/internal/lefthook/runner/exec/execute_windows.go +++ b/internal/lefthook/runner/exec/execute_windows.go @@ -66,11 +66,10 @@ func (e CommandExecutor) Execute(ctx context.Context, opts Options, in io.Reader return nil } -func (e CommandExecutor) execute(cmdstr string, args *executeArgs) error { - cmdargs := strings.Split(cmdstr, " ") - command := exec.Command(cmdargs[0]) +func (e CommandExecutor) execute(cmd []string, args *executeArgs) error { + command := exec.Command(cmd[0]) command.SysProcAttr = &syscall.SysProcAttr{ - CmdLine: strings.Join(cmdargs, " "), + CmdLine: strings.Join(cmd, " "), } command.Dir = args.root command.Env = append(os.Environ(), args.envs...)