diff --git a/internal/lefthook/run.go b/internal/lefthook/run.go index 5e51143a..5a7f4f80 100644 --- a/internal/lefthook/run.go +++ b/internal/lefthook/run.go @@ -116,9 +116,6 @@ Run 'lefthook install' manually.`, return err } - startTime := time.Now() - resultChan := make(chan runner.Result, len(hook.Commands)+len(hook.Scripts)) - if args.FilesFromStdin { paths, err := io.ReadAll(os.Stdin) if err != nil { @@ -160,7 +157,6 @@ Run 'lefthook install' manually.`, Hook: hook, HookName: hookName, GitArgs: gitArgs, - ResultChan: resultChan, LogSettings: logSettings, DisableTTY: cfg.NoTTY || args.NoTTY, Files: args.Files, @@ -169,15 +165,8 @@ Run 'lefthook install' manually.`, }, ) - go func() { - r.RunAll(ctx, sourceDirs) - close(resultChan) - }() - - var results []runner.Result - for res := range resultChan { - results = append(results, res) - } + startTime := time.Now() + results := r.RunAll(ctx, sourceDirs) if ctx.Err() != nil { return errors.New("Interrupted") @@ -186,7 +175,7 @@ Run 'lefthook install' manually.`, printSummary(time.Since(startTime), results, logSettings) for _, result := range results { - if result.Err != nil { + if result.Failure() { return errors.New("") // No error should be printed } } @@ -227,7 +216,7 @@ func printSummary( if logSettings.LogSuccess() { for _, result := range results { - if result.Err != nil { + if !result.Success() { continue } @@ -237,11 +226,11 @@ func printSummary( if logSettings.LogFailure() { for _, result := range results { - if result.Err == nil { + if !result.Failure() { continue } - failText := result.Err.Error() + failText := result.Text() if len(failText) != 0 { failText = fmt.Sprintf(": %s", failText) } diff --git a/internal/lefthook/runner/exec/executor.go b/internal/lefthook/runner/exec/executor.go index 713fadb1..d4e67628 100644 --- a/internal/lefthook/runner/exec/executor.go +++ b/internal/lefthook/runner/exec/executor.go @@ -7,7 +7,7 @@ import ( // Options contains the data that controls the execution. type Options struct { - Name, Root, FailText string + Name, Root 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 e1b3cc77..f021a37a 100644 --- a/internal/lefthook/runner/prepare_command.go +++ b/internal/lefthook/runner/prepare_command.go @@ -1,7 +1,6 @@ package runner import ( - "errors" "fmt" "runtime" "strings" @@ -36,35 +35,30 @@ const ( func (r *Runner) prepareCommand(name string, command *config.Command) (*run, error) { if command.DoSkip(r.Repo.State()) { - return nil, errors.New("settings") + return nil, &skipError{"settings"} } if intersect(r.Hook.ExcludeTags, command.Tags) { - return nil, errors.New("tags") + return nil, &skipError{"tags"} } if intersect(r.Hook.ExcludeTags, []string{name}) { - return nil, errors.New("name") + return nil, &skipError{"name"} } if err := command.Validate(); err != nil { - r.fail(name, err) return nil, err } - args, err, skipReason := r.buildRun(command) + args, err := r.buildRun(command) if err != nil { - log.Error(err) - return nil, errors.New("error") - } - if skipReason != nil { - return nil, skipReason + return nil, err } return args, nil } -func (r *Runner) buildRun(command *config.Command) (*run, error, error) { +func (r *Runner) buildRun(command *config.Command) (*run, error) { filesCmd := r.Hook.Files if len(command.Files) > 0 { filesCmd = command.Files @@ -118,12 +112,12 @@ func (r *Runner) buildRun(command *config.Command) (*run, error, error) { files, err := fn() if err != nil { - return nil, fmt.Errorf("error replacing %s: %w", filesType, err), nil + return nil, fmt.Errorf("error replacing %s: %w", filesType, err) } files = filter.Apply(command, files) if !r.Force && len(files) == 0 { - return nil, nil, errors.New("no files for inspection") + return nil, &skipError{"no files for inspection"} } templ.files = files @@ -135,13 +129,13 @@ func (r *Runner) buildRun(command *config.Command) (*run, error, error) { if !r.Force && len(filesCmd) > 0 && templates[config.SubFiles] == nil { files, err := filesFns[config.SubFiles]() if err != nil { - return nil, fmt.Errorf("error calling replace command for %s: %w", config.SubFiles, err), nil + return nil, fmt.Errorf("error calling replace command for %s: %w", config.SubFiles, err) } files = filter.Apply(command, files) if len(files) == 0 { - return nil, nil, errors.New("no files for inspection") + return nil, &skipError{"no files for inspection"} } } @@ -160,30 +154,30 @@ func (r *Runner) buildRun(command *config.Command) (*run, error, error) { result := replaceInChunks(runString, templates, maxlen) if r.Force || len(result.files) != 0 { - return result, nil, nil + return result, nil } if config.HookUsesStagedFiles(r.HookName) { ok, err := canSkipCommand(command, templates[config.SubStagedFiles], r.Repo.StagedFiles) if err != nil { - return nil, err, nil + return nil, err } if ok { - return nil, nil, errors.New("no matching staged files") + return nil, &skipError{"no matching staged files"} } } if config.HookUsesPushFiles(r.HookName) { ok, err := canSkipCommand(command, templates[config.SubPushFiles], r.Repo.PushFiles) if err != nil { - return nil, err, nil + return nil, err } if ok { - return nil, nil, errors.New("no matching push files") + return nil, &skipError{"no matching push files"} } } - return result, nil, nil + return result, nil } func canSkipCommand(command *config.Command, template *template, filesFn func() ([]string, error)) (bool, error) { diff --git a/internal/lefthook/runner/prepare_script.go b/internal/lefthook/runner/prepare_script.go index 1e971993..7ce8dee1 100644 --- a/internal/lefthook/runner/prepare_script.go +++ b/internal/lefthook/runner/prepare_script.go @@ -1,7 +1,6 @@ package runner import ( - "errors" "os" "strings" @@ -11,25 +10,24 @@ import ( func (r *Runner) prepareScript(script *config.Script, path string, file os.FileInfo) (string, error) { if script.DoSkip(r.Repo.State()) { - return "", errors.New("settings") + return "", &skipError{"settings"} } if intersect(r.Hook.ExcludeTags, script.Tags) { - return "", errors.New("excluded tags") + return "", &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 "", errors.New("not a regular file") + return "", &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) - r.fail(file.Name(), nil) - return "", errors.New("system error") + return "", err } } diff --git a/internal/lefthook/runner/result.go b/internal/lefthook/runner/result.go new file mode 100644 index 00000000..947cfbf9 --- /dev/null +++ b/internal/lefthook/runner/result.go @@ -0,0 +1,40 @@ +package runner + +type status int8 + +const ( + success status = iota + failure + skip +) + +// Result contains name of a command/script and an optional fail string. +type Result struct { + Name string + status status + text string +} + +func (r Result) Success() bool { + return r.status == success +} + +func (r Result) Failure() bool { + return r.status == failure +} + +func (r Result) Text() string { + return r.text +} + +func skipped(name string) Result { + return Result{Name: name, status: skip} +} + +func succeeded(name string) Result { + return Result{Name: name, status: success} +} + +func failed(name, text string) Result { + return Result{Name: name, status: failure, text: text} +} diff --git a/internal/lefthook/runner/runner.go b/internal/lefthook/runner/runner.go index fbfff985..80534882 100644 --- a/internal/lefthook/runner/runner.go +++ b/internal/lefthook/runner/runner.go @@ -40,7 +40,6 @@ type Options struct { Hook *config.Hook HookName string GitArgs []string - ResultChan chan Result LogSettings log.Settings DisableTTY bool Force bool @@ -57,12 +56,6 @@ type Runner struct { executor exec.Executor } -// Result contains name of a command/script and an optional fail string. -type Result struct { - Name string - Err error -} - func New(opts Options) *Runner { return &Runner{ Options: opts, @@ -70,6 +63,15 @@ func New(opts Options) *Runner { } } +// skipError implements error interface but indicates that the execution needs to be skipped. +type skipError struct { + reason string +} + +func (r *skipError) Error() string { + return r.reason +} + type executable interface { *config.Command | *config.Script ExecutionPriority() int @@ -77,14 +79,16 @@ type executable interface { // RunAll runs scripts and commands. // LFS hook is executed at first if needed. -func (r *Runner) RunAll(ctx context.Context, sourceDirs []string) { +func (r *Runner) RunAll(ctx context.Context, sourceDirs []string) []Result { + results := make([]Result, 0, len(r.Hook.Commands)+len(r.Hook.Scripts)) + if err := r.runLFSHook(ctx); err != nil { log.Error(err) } if r.Hook.DoSkip(r.Repo.State()) { r.logSkip(r.HookName, "hook setting") - return + return results } if !r.DisableTTY && !r.Hook.Follow { @@ -102,21 +106,14 @@ func (r *Runner) RunAll(ctx context.Context, sourceDirs []string) { r.preHook() for _, dir := range scriptDirs { - r.runScripts(ctx, dir) + results = append(results, r.runScripts(ctx, dir)...) } - r.runCommands(ctx) + results = append(results, r.runCommands(ctx)...) r.postHook() -} - -func (r *Runner) fail(name string, err error) { - r.ResultChan <- Result{Name: name, Err: err} - r.failed.Store(true) -} -func (r *Runner) success(name string) { - r.ResultChan <- Result{Name: name, Err: nil} + return results } func (r *Runner) runLFSHook(ctx context.Context) error { @@ -238,10 +235,10 @@ func (r *Runner) postHook() { } } -func (r *Runner) runScripts(ctx context.Context, dir string) { +func (r *Runner) runScripts(ctx context.Context, dir string) []Result { files, err := afero.ReadDir(r.Repo.Fs, dir) // ReadDir already sorts files by .Name() if err != nil || len(files) == 0 { - return + return nil } scripts := make([]string, 0, len(files)) @@ -254,12 +251,14 @@ func (r *Runner) runScripts(ctx context.Context, dir string) { interactiveScripts := make([]os.FileInfo, 0) var wg sync.WaitGroup + resChan := make(chan Result, len(r.Hook.Scripts)) + results := make([]Result, 0, len(r.Hook.Scripts)) for _, name := range scripts { file := filesMap[name] if ctx.Err() != nil { - return + return nil } script, ok := r.Hook.Scripts[file.Name()] @@ -282,20 +281,24 @@ func (r *Runner) runScripts(ctx context.Context, dir string) { if r.Hook.Parallel { wg.Add(1) - go func(script *config.Script, path string, file os.FileInfo) { + go func(script *config.Script, path string, file os.FileInfo, resChan chan Result) { defer wg.Done() - r.runScript(ctx, script, path, file) - }(script, path, file) + resChan <- r.runScript(ctx, script, path, file) + }(script, path, file, resChan) } else { - r.runScript(ctx, script, path, file) + results = append(results, r.runScript(ctx, script, path, file)) } } wg.Wait() + close(resChan) + for result := range resChan { + results = append(results, result) + } for _, file := range interactiveScripts { if ctx.Err() != nil { - return + return nil } script := r.Hook.Scripts[file.Name()] @@ -305,15 +308,24 @@ func (r *Runner) runScripts(ctx context.Context, dir string) { } path := filepath.Join(dir, file.Name()) - r.runScript(ctx, script, path, file) + results = append(results, r.runScript(ctx, script, path, file)) } + + return results } -func (r *Runner) runScript(ctx context.Context, script *config.Script, path string, file os.FileInfo) { +func (r *Runner) runScript(ctx context.Context, script *config.Script, path string, file os.FileInfo) Result { command, err := r.prepareScript(script, path, file) if err != nil { r.logSkip(file.Name(), err.Error()) - return + + var skipErr *skipError + if errors.As(err, &skipErr) { + return skipped(file.Name()) + } + + r.failed.Store(true) + return failed(file.Name(), err.Error()) } if script.Interactive && !r.DisableTTY && !r.Hook.Follow { @@ -321,28 +333,36 @@ func (r *Runner) runScript(ctx context.Context, script *config.Script, path stri defer log.StartSpinner() } - finished := r.run(ctx, exec.Options{ + ok := r.run(ctx, exec.Options{ Name: file.Name(), Root: r.Repo.RootPath, Commands: []string{command}, - FailText: script.FailText, Interactive: script.Interactive && !r.DisableTTY, UseStdin: script.UseStdin, Env: script.Env, }, r.Hook.Follow) - if finished && config.HookUsesStagedFiles(r.HookName) && script.StageFixed { + if !ok { + r.failed.Store(true) + return failed(file.Name(), script.FailText) + } + + result := succeeded(file.Name()) + + if config.HookUsesStagedFiles(r.HookName) && script.StageFixed { files, err := r.Repo.StagedFiles() if err != nil { log.Warn("Couldn't stage fixed files:", err) - return + return result } r.addStagedFiles(files) } + + return result } -func (r *Runner) runCommands(ctx context.Context) { +func (r *Runner) runCommands(ctx context.Context) []Result { commands := make([]string, 0, len(r.Hook.Commands)) for name := range r.Hook.Commands { if len(r.RunOnlyCommands) == 0 || slices.Contains(r.RunOnlyCommands, name) { @@ -354,6 +374,8 @@ func (r *Runner) runCommands(ctx context.Context) { interactiveCommands := make([]string, 0) var wg sync.WaitGroup + results := make([]Result, 0, len(r.Hook.Commands)) + resChan := make(chan Result, len(r.Hook.Commands)) for _, name := range commands { if r.failed.Load() && r.Hook.Piped { @@ -368,16 +390,22 @@ func (r *Runner) runCommands(ctx context.Context) { if r.Hook.Parallel { wg.Add(1) - go func(name string, command *config.Command) { + go func(name string, command *config.Command, resChan chan Result) { defer wg.Done() - r.runCommand(ctx, name, command) - }(name, r.Hook.Commands[name]) + result := r.runCommand(ctx, name, command) + resChan <- result + }(name, r.Hook.Commands[name], resChan) } else { - r.runCommand(ctx, name, r.Hook.Commands[name]) + result := r.runCommand(ctx, name, r.Hook.Commands[name]) + results = append(results, result) } } wg.Wait() + close(resChan) + for result := range resChan { + results = append(results, result) + } for _, name := range interactiveCommands { if r.failed.Load() { @@ -385,15 +413,24 @@ func (r *Runner) runCommands(ctx context.Context) { continue } - r.runCommand(ctx, name, r.Hook.Commands[name]) + results = append(results, r.runCommand(ctx, name, r.Hook.Commands[name])) } + + return results } -func (r *Runner) runCommand(ctx context.Context, name string, command *config.Command) { +func (r *Runner) runCommand(ctx context.Context, name string, command *config.Command) Result { run, err := r.prepareCommand(name, command) if err != nil { r.logSkip(name, err.Error()) - return + + var skipErr *skipError + if errors.As(err, &skipErr) { + return skipped(name) + } + + r.failed.Store(true) + return failed(name, err.Error()) } if command.Interactive && !r.DisableTTY && !r.Hook.Follow { @@ -401,17 +438,23 @@ func (r *Runner) runCommand(ctx context.Context, name string, command *config.Co defer log.StartSpinner() } - finished := r.run(ctx, exec.Options{ + ok := r.run(ctx, exec.Options{ Name: name, Root: filepath.Join(r.Repo.RootPath, command.Root), Commands: run.commands, - FailText: command.FailText, Interactive: command.Interactive && !r.DisableTTY, UseStdin: command.UseStdin, Env: command.Env, }, r.Hook.Follow) - if finished && config.HookUsesStagedFiles(r.HookName) && command.StageFixed { + if !ok { + r.failed.Store(true) + return failed(name, command.FailText) + } + + result := succeeded(name) + + if config.HookUsesStagedFiles(r.HookName) && command.StageFixed { files := run.files if len(files) == 0 { @@ -419,7 +462,7 @@ func (r *Runner) runCommand(ctx context.Context, name string, command *config.Co files, err = r.Repo.StagedFiles() if err != nil { log.Warn("Couldn't stage fixed files:", err) - return + return result } files = filter.Apply(command, files) @@ -433,6 +476,8 @@ func (r *Runner) runCommand(ctx context.Context, name string, command *config.Co r.addStagedFiles(files) } + + return result } func (r *Runner) addStagedFiles(files []string) { @@ -456,24 +501,12 @@ func (r *Runner) run(ctx context.Context, opts exec.Options, follow bool) bool { } err := r.executor.Execute(ctx, opts, out) - if err != nil { - r.fail(opts.Name, errors.New(opts.FailText)) - } else { - r.success(opts.Name) - } - return err == nil } out := bytes.NewBuffer(make([]byte, 0)) err := r.executor.Execute(ctx, opts, out) - if err != nil { - r.fail(opts.Name, errors.New(opts.FailText)) - } else { - r.success(opts.Name) - } - r.logExecute(opts.Name, err, out) return err == nil diff --git a/internal/lefthook/runner/runner_test.go b/internal/lefthook/runner/runner_test.go index 29711bae..0c427754 100644 --- a/internal/lefthook/runner/runner_test.go +++ b/internal/lefthook/runner/runner_test.go @@ -120,7 +120,7 @@ func TestRunAll(t *testing.T) { }, Scripts: map[string]*config.Script{}, }, - success: []Result{{Name: "test"}}, + success: []Result{succeeded("test")}, }, { name: "with simple command in follow mode", @@ -134,7 +134,7 @@ func TestRunAll(t *testing.T) { }, Scripts: map[string]*config.Script{}, }, - success: []Result{{Name: "test"}}, + success: []Result{succeeded("test")}, }, { name: "with multiple commands ran in parallel", @@ -155,10 +155,10 @@ func TestRunAll(t *testing.T) { Scripts: map[string]*config.Script{}, }, success: []Result{ - {Name: "test"}, - {Name: "lint"}, + succeeded("test"), + succeeded("lint"), }, - fail: []Result{{Name: "type-check", Err: errors.New("")}}, + fail: []Result{failed("type-check", "")}, }, { name: "with exclude tags", @@ -180,7 +180,7 @@ func TestRunAll(t *testing.T) { }, Scripts: map[string]*config.Script{}, }, - success: []Result{{Name: "lint"}}, + success: []Result{succeeded("lint")}, }, { name: "with skip boolean option", @@ -197,7 +197,7 @@ func TestRunAll(t *testing.T) { }, Scripts: map[string]*config.Script{}, }, - success: []Result{{Name: "lint"}}, + success: []Result{succeeded("lint")}, }, { name: "with skip merge", @@ -217,7 +217,7 @@ func TestRunAll(t *testing.T) { }, Scripts: map[string]*config.Script{}, }, - success: []Result{{Name: "lint"}}, + success: []Result{succeeded("lint")}, }, { name: "with only on merge", @@ -238,8 +238,8 @@ func TestRunAll(t *testing.T) { Scripts: map[string]*config.Script{}, }, success: []Result{ - {Name: "lint"}, - {Name: "test"}, + succeeded("lint"), + succeeded("test"), }, }, { @@ -257,7 +257,7 @@ func TestRunAll(t *testing.T) { }, Scripts: map[string]*config.Script{}, }, - success: []Result{{Name: "lint"}}, + success: []Result{succeeded("lint")}, }, { name: "with global skip merge", @@ -315,8 +315,8 @@ func TestRunAll(t *testing.T) { Scripts: map[string]*config.Script{}, }, success: []Result{ - {Name: "lint"}, - {Name: "test"}, + succeeded("lint"), + succeeded("test"), }, }, { @@ -338,7 +338,7 @@ func TestRunAll(t *testing.T) { }, Scripts: map[string]*config.Script{}, }, - success: []Result{{Name: "lint"}}, + success: []Result{succeeded("lint")}, }, { name: "with global skip on ref", @@ -381,8 +381,8 @@ func TestRunAll(t *testing.T) { Scripts: map[string]*config.Script{}, }, success: []Result{ - {Name: "lint"}, - {Name: "test"}, + succeeded("lint"), + succeeded("test"), }, }, { @@ -426,8 +426,8 @@ func TestRunAll(t *testing.T) { Scripts: map[string]*config.Script{}, }, success: []Result{ - {Name: "test"}, - {Name: "lint"}, + succeeded("test"), + succeeded("lint"), }, }, { @@ -442,7 +442,7 @@ func TestRunAll(t *testing.T) { }, Scripts: map[string]*config.Script{}, }, - fail: []Result{{Name: "test", Err: errors.New("try 'success'")}}, + fail: []Result{failed("test", "try 'success'")}, }, { name: "with simple scripts", @@ -464,8 +464,8 @@ func TestRunAll(t *testing.T) { }, }, }, - success: []Result{{Name: "script.sh"}}, - fail: []Result{{Name: "failing.js", Err: errors.New("install node")}}, + success: []Result{succeeded("script.sh")}, + fail: []Result{failed("failing.js", "install node")}, }, { name: "with simple scripts and only option", @@ -490,8 +490,8 @@ func TestRunAll(t *testing.T) { }, }, }, - success: []Result{{Name: "script.sh"}}, - fail: []Result{{Name: "failing.js", Err: errors.New("install node")}}, + success: []Result{succeeded("script.sh")}, + fail: []Result{failed("failing.js", "install node")}, }, { name: "with simple scripts and only option", @@ -547,8 +547,8 @@ func TestRunAll(t *testing.T) { }, }, }, - success: []Result{}, // script.sh and ok are skipped - fail: []Result{{Name: "failing.js", Err: errors.New("")}, {Name: "fail", Err: errors.New("")}}, + success: []Result{}, // script.sh and ok are skipped because of non-interactive cmd failure + fail: []Result{failed("failing.js", ""), failed("fail", "")}, }, { name: "with stage_fixed in true", @@ -580,8 +580,8 @@ func TestRunAll(t *testing.T) { }, }, }, - success: []Result{{Name: "ok"}, {Name: "success.sh"}}, - fail: []Result{{Name: "fail", Err: errors.New("")}, {Name: "failing.js", Err: errors.New("")}}, + success: []Result{succeeded("ok"), succeeded("success.sh")}, + fail: []Result{failed("fail", ""), failed("failing.js", "")}, }, { name: "pre-commit hook simple", @@ -615,8 +615,8 @@ func TestRunAll(t *testing.T) { }, }, }, - success: []Result{{Name: "ok"}, {Name: "success.sh"}}, - fail: []Result{{Name: "fail", Err: errors.New("")}, {Name: "failing.js", Err: errors.New("")}}, + success: []Result{succeeded("ok"), succeeded("success.sh")}, + fail: []Result{failed("fail", ""), failed("failing.js", "")}, gitCommands: []string{ "git status --short", "git diff --name-only --cached --diff-filter=ACMR", @@ -649,7 +649,7 @@ func TestRunAll(t *testing.T) { }, }, }, - success: []Result{{Name: "ok"}}, + success: []Result{succeeded("ok")}, gitCommands: []string{ "git status --short", "git diff --name-only --cached --diff-filter=ACMR", @@ -681,8 +681,8 @@ func TestRunAll(t *testing.T) { }, }, force: true, - success: []Result{{Name: "ok"}}, - fail: []Result{{Name: "fail", Err: errors.New("")}}, + success: []Result{succeeded("ok")}, + fail: []Result{failed("fail", "")}, gitCommands: []string{ "git status --short", "git diff --name-only --cached --diff-filter=ACMR", @@ -707,7 +707,7 @@ func TestRunAll(t *testing.T) { }, }, }, - success: []Result{{Name: "ok"}}, + success: []Result{succeeded("ok")}, gitCommands: []string{ "git status --short", "git diff --name-only --cached --diff-filter=ACMR", @@ -737,7 +737,7 @@ func TestRunAll(t *testing.T) { }, }, }, - success: []Result{{Name: "ok"}}, + success: []Result{succeeded("ok")}, gitCommands: []string{ "git diff --name-only HEAD @{push}", "git diff --name-only HEAD @{push}", @@ -746,7 +746,6 @@ func TestRunAll(t *testing.T) { } { fs := afero.NewMemMapFs() repo.Fs = fs - resultChan := make(chan Result, len(tt.hook.Commands)+len(tt.hook.Scripts)) executor := TestExecutor{} runner := &Runner{ Options: Options{ @@ -755,7 +754,6 @@ func TestRunAll(t *testing.T) { HookName: tt.hookName, LogSettings: log.NewSettings(), GitArgs: tt.args, - ResultChan: resultChan, Force: tt.force, }, executor: executor, @@ -778,15 +776,14 @@ func TestRunAll(t *testing.T) { } t.Run(fmt.Sprintf("%d: %s", i, tt.name), func(t *testing.T) { - runner.RunAll(context.Background(), tt.sourceDirs) - close(resultChan) + results := runner.RunAll(context.Background(), tt.sourceDirs) var success, fail []Result - for res := range resultChan { - if res.Err == nil { - success = append(success, res) - } else { - fail = append(fail, res) + for _, result := range results { + if result.Success() { + success = append(success, result) + } else if result.Failure() { + fail = append(fail, result) } } @@ -820,12 +817,12 @@ func resultsMatch(a, b []Result) bool { matches := make(map[string]struct{}) for _, item := range a { - str := fmt.Sprintf("%s_%v", item.Name, item.Err) + str := fmt.Sprintf("%v", item) matches[str] = struct{}{} } for _, item := range b { - str := fmt.Sprintf("%s_%v", item.Name, item.Err) + str := fmt.Sprintf("%v", item) if _, ok := matches[str]; !ok { return false }