diff --git a/internal/output/group.go b/internal/output/group.go new file mode 100644 index 00000000..1e5a434a --- /dev/null +++ b/internal/output/group.go @@ -0,0 +1,26 @@ +package output + +import ( + "bytes" + "io" +) + +type Group struct{} + +func (Group) WrapWriter(w io.Writer) io.WriteCloser { + return &groupWriter{writer: w} +} + +type groupWriter struct { + writer io.Writer + buff bytes.Buffer +} + +func (gw *groupWriter) Write(p []byte) (int, error) { + return gw.buff.Write(p) +} + +func (gw *groupWriter) Close() error { + _, err := io.Copy(gw.writer, &gw.buff) + return err +} diff --git a/internal/output/interleaved.go b/internal/output/interleaved.go new file mode 100644 index 00000000..9d37f485 --- /dev/null +++ b/internal/output/interleaved.go @@ -0,0 +1,23 @@ +package output + +import ( + "io" +) + +type Interleaved struct{} + +func (Interleaved) WrapWriter(w io.Writer) io.WriteCloser { + return nopWriterCloser{w: w} +} + +type nopWriterCloser struct { + w io.Writer +} + +func (wc nopWriterCloser) Write(p []byte) (int, error) { + return wc.w.Write(p) +} + +func (wc nopWriterCloser) Close() error { + return nil +} diff --git a/internal/output/output.go b/internal/output/output.go new file mode 100644 index 00000000..fd19bb4a --- /dev/null +++ b/internal/output/output.go @@ -0,0 +1,9 @@ +package output + +import ( + "io" +) + +type Output interface { + WrapWriter(io.Writer) io.WriteCloser +} diff --git a/task.go b/task.go index 60f7bb39..96f51bf6 100644 --- a/task.go +++ b/task.go @@ -12,6 +12,7 @@ import ( compilerv2 "github.com/go-task/task/internal/compiler/v2" "github.com/go-task/task/internal/execext" "github.com/go-task/task/internal/logger" + "github.com/go-task/task/internal/output" "github.com/go-task/task/internal/taskfile" "github.com/go-task/task/internal/taskfile/version" @@ -44,6 +45,7 @@ type Executor struct { Logger *logger.Logger Compiler compiler.Compiler + Output output.Output taskvars taskfile.Vars @@ -119,9 +121,18 @@ func (e *Executor) Setup() error { case version.IsV22(v): return fmt.Errorf(`task: Taskfile versions greater than v2.1 not implemented in the version of Task`) } + if !version.IsV21(v) && e.Taskfile.Output != "" { return fmt.Errorf(`task: Taskfile option "output" is only available starting on Taskfile version v2.1`) } + switch e.Taskfile.Output { + case "", "interleaved": + e.Output = output.Interleaved{} + case "group": + e.Output = output.Group{} + default: + return fmt.Errorf(`task: output option "%s" not recognized`, e.Taskfile.Output) + } e.taskCallCount = make(map[string]*int32, len(e.Taskfile.Tasks)) for k := range e.Taskfile.Tasks { @@ -193,14 +204,19 @@ func (e *Executor) runCommand(ctx context.Context, t *taskfile.Task, call taskfi e.Logger.Errf(cmd.Cmd) } + stdOut := e.Output.WrapWriter(e.Stdout) + stdErr := e.Output.WrapWriter(e.Stderr) + defer stdOut.Close() + defer stdErr.Close() + return execext.RunCommand(&execext.RunCommandOptions{ Context: ctx, Command: cmd.Cmd, Dir: t.Dir, Env: getEnviron(t), Stdin: e.Stdin, - Stdout: e.Stdout, - Stderr: e.Stderr, + Stdout: stdOut, + Stderr: stdErr, }) }