mirror of
https://github.com/go-task/task.git
synced 2026-06-22 20:25:50 +00:00
Previously the gitlab output wrapped each command individually, causing two visible bugs in real GitLab pipelines: - every section displayed a duration of 00:00, because start and end markers were emitted microseconds apart for instant commands - the `task: [NAME] CMD` announcement lines were rendered outside the sections, because Logger.Errf bypassed the cmd-level wrapper Fix by wrapping output at the task level via a new optional [output.TaskWrapper] interface that GitLab implements. Task-scoped writers are threaded via ctx so nested `task:` invocations produce properly nested sections (GitLab supports this natively), and deps running in parallel each get their own buffer with mutex-protected flushes into the parent's buffer. - `internal/output/output.go`: add TaskWrapper interface - `internal/output/gitlab.go`: logic moved from WrapWriter to WrapTask; WrapWriter becomes passthrough; sync.Mutex around the buffer for concurrent flushes from parallel sub-task sections - `task_output.go` (new): ctx plumbing + helpers kept out of task.go - `task.go`: 7 lines of surgical edits — name the lambda's error return, wrap before the cmd loop, defer the closer with the final error, and swap the cmd announcement to `printCmdAnnouncement` which writes into the task-scoped stderr
64 lines
1.7 KiB
Go
64 lines
1.7 KiB
Go
package output
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
|
|
"github.com/go-task/task/v3/internal/logger"
|
|
"github.com/go-task/task/v3/internal/templater"
|
|
"github.com/go-task/task/v3/taskfile/ast"
|
|
)
|
|
|
|
type Output interface {
|
|
WrapWriter(stdOut, stdErr io.Writer, prefix string, cache *templater.Cache) (io.Writer, io.Writer, CloseFunc)
|
|
}
|
|
|
|
// TaskWrapper is an optional interface that Output implementations can satisfy
|
|
// to wrap an entire task's execution in a single enclosing block — including
|
|
// the task's command announcements and all its commands' output — instead of
|
|
// wrapping each command individually via WrapWriter.
|
|
type TaskWrapper interface {
|
|
WrapTask(stdOut, stdErr io.Writer, cache *templater.Cache) (io.Writer, io.Writer, CloseFunc)
|
|
}
|
|
|
|
type CloseFunc func(err error) error
|
|
|
|
// Build the Output for the requested ast.Output.
|
|
func BuildFor(o *ast.Output, logger *logger.Logger) (Output, error) {
|
|
switch o.Name {
|
|
case "interleaved", "":
|
|
if err := checkOutputGroupUnset(o); err != nil {
|
|
return nil, err
|
|
}
|
|
return Interleaved{}, nil
|
|
case "group":
|
|
return Group{
|
|
Begin: o.Group.Begin,
|
|
End: o.Group.End,
|
|
ErrorOnly: o.Group.ErrorOnly,
|
|
}, nil
|
|
case "prefixed":
|
|
if err := checkOutputGroupUnset(o); err != nil {
|
|
return nil, err
|
|
}
|
|
return NewPrefixed(logger), nil
|
|
case "gitlab":
|
|
if err := checkOutputGroupUnset(o); err != nil {
|
|
return nil, err
|
|
}
|
|
return GitLab{
|
|
Collapsed: o.GitLab.Collapsed,
|
|
ErrorOnly: o.GitLab.ErrorOnly,
|
|
}, nil
|
|
default:
|
|
return nil, fmt.Errorf(`task: output style %q not recognized`, o.Name)
|
|
}
|
|
}
|
|
|
|
func checkOutputGroupUnset(o *ast.Output) error {
|
|
if o.Group.IsSet() {
|
|
return fmt.Errorf("task: output style %q does not support the group begin/end parameter", o.Name)
|
|
}
|
|
return nil
|
|
}
|