diff --git a/CHANGELOG.md b/CHANGELOG.md index 8606b341..f662e879 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,8 +2,12 @@ ## Unreleased +- Task now supports printing begin and end messages when using the `group` + output mode, useful for grouping tasks in CI systems. + [Check out the documentation](http://taskfile.dev/#/usage?id=output-syntax) for more information + ([#647](https://github.com/go-task/task/issues/647), [#651](https://github.com/go-task/task/pull/651)). - Add `Taskfile.dist.yml` and `Taskfile.dist.yaml` to the supported file - name list. [Check out the documentation for more information](https://taskfile.dev/#/usage?id=supported-file-names). + name list. [Check out the documentation](https://taskfile.dev/#/usage?id=supported-file-names) for more information ([#498](https://github.com/go-task/task/issues/498), [#666](https://github.com/go-task/task/pull/666)). ## v3.10.0 - 2022-01-04 diff --git a/cmd/task/task.go b/cmd/task/task.go index 4daf2e91..8eff5db5 100644 --- a/cmd/task/task.go +++ b/cmd/task/task.go @@ -11,7 +11,6 @@ import ( "strings" "syscall" - outputpkg "github.com/go-task/task/v3/internal/output" "github.com/spf13/pflag" "mvdan.cc/sh/v3/syntax" @@ -73,7 +72,7 @@ func main() { concurrency int dir string entrypoint string - output outputpkg.Style + output taskfile.Output color bool ) diff --git a/docs/usage.md b/docs/usage.md index 7521f47e..e59ea659 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -974,9 +974,10 @@ tasks: to run. When using the `group` output, you can optionally provide a templated message -to print at the start of the group. This can be useful for instructing CI -systems to group all of the output for a given task, such as with [GitHub -Actions' `::group::` command](https://docs.github.com/en/actions/learn-github-actions/workflow-commands-for-github-actions#grouping-log-lines). +to print at the start and end of the group. This can be useful for instructing +CI systems to group all of the output for a given task, such as with +[GitHub Actions' `::group::` command](https://docs.github.com/en/actions/learn-github-actions/workflow-commands-for-github-actions#grouping-log-lines) +or [Azure Pipelines](https://docs.microsoft.com/en-us/azure/devops/pipelines/scripts/logging-commands?expand=1&view=azure-devops&tabs=bash#formatting-commands). ```yaml version: '3' diff --git a/internal/output/output.go b/internal/output/output.go index ce651ad2..925c840a 100644 --- a/internal/output/output.go +++ b/internal/output/output.go @@ -1,9 +1,11 @@ package output import ( + "fmt" "io" -) + "github.com/go-task/task/v3/taskfile" +) // Templater executes a template engine. // It is provided by the templater.Templater package. @@ -15,3 +17,33 @@ type Templater interface { type Output interface { WrapWriter(w io.Writer, prefix string, tmpl Templater) io.Writer } + +// Build the Output for the requested taskfile.Output. +func BuildFor(o *taskfile.Output) (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, + }, nil + case "prefixed": + if err := checkOutputGroupUnset(o); err != nil { + return nil, err + } + return Prefixed{}, nil + default: + return nil, fmt.Errorf(`task: output style %q not recognized`, o.Name) + } +} + +func checkOutputGroupUnset(o *taskfile.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 +} diff --git a/internal/output/style.go b/internal/output/style.go deleted file mode 100644 index b670a688..00000000 --- a/internal/output/style.go +++ /dev/null @@ -1,85 +0,0 @@ -package output - -import ( - "fmt" -) - -// Style of the Task output -type Style struct { - // Name of the Style. - Name string `yaml:"-"` - // Group specific style - Group GroupStyle -} - -// Build the Output for the requested Style. -func (s *Style) Build() (Output, error) { - switch s.Name { - case "interleaved", "": - return Interleaved{}, s.ensureGroupStyleUnset() - case "group": - return Group{ - Begin: s.Group.Begin, - End: s.Group.End, - }, nil - case "prefixed": - return Prefixed{}, s.ensureGroupStyleUnset() - default: - return nil, fmt.Errorf(`task: output style %q not recognized`, s.Name) - } -} - -func (s *Style) ensureGroupStyleUnset() error { - if s.Group.IsSet() { - return fmt.Errorf("task: output style %q does not support the group begin/end parameter", s.Name) - } - return nil -} - -// IsSet returns true if and only if a custom output style is set. -func (s *Style) IsSet() bool { - return s.Name != "" -} - -// UnmarshalYAML implements yaml.Unmarshaler -// It accepts a scalar node representing the Style.Name or a mapping node representing the GroupStyle. -func (s *Style) UnmarshalYAML(unmarshal func(interface{}) error) error { - var name string - if err := unmarshal(&name); err == nil { - return s.UnmarshalText([]byte(name)) - } - var tmp struct { - Group *GroupStyle - } - if err := unmarshal(&tmp); err != nil { - return fmt.Errorf("task: output style must be a string or mapping with a \"group\" key: %w", err) - } - if tmp.Group == nil { - return fmt.Errorf("task: output style must have the \"group\" key when in mapping form") - } - *s = Style{ - Name: "group", - Group: *tmp.Group, - } - return nil -} - -// UnmarshalText implements encoding.TextUnmarshaler -// It accepts the Style.Node -func (s *Style) UnmarshalText(text []byte) error { - tmp := Style{Name: string(text)} - if _, err := tmp.Build(); err != nil { - return err - } - return nil -} - -// GroupStyle is the style options specific to the Group style. -type GroupStyle struct{ - Begin, End string -} - -// IsSet returns true if and only if a custom output style is set. -func (g *GroupStyle) IsSet() bool { - return g != nil && *g != GroupStyle{} -} diff --git a/task.go b/task.go index 4219ccb2..ecd3ed35 100644 --- a/task.go +++ b/task.go @@ -52,7 +52,7 @@ type Executor struct { Logger *logger.Logger Compiler compiler.Compiler Output output.Output - OutputStyle output.Style + OutputStyle taskfile.Output taskvars *taskfile.Vars @@ -211,10 +211,9 @@ func (e *Executor) Setup() error { if !e.OutputStyle.IsSet() { e.OutputStyle = e.Taskfile.Output } - if o, err := e.OutputStyle.Build(); err != nil { + e.Output, err = output.BuildFor(&e.OutputStyle) + if err != nil { return err - } else { - e.Output = o } if e.Taskfile.Method == "" { diff --git a/taskfile/output.go b/taskfile/output.go new file mode 100644 index 00000000..d3c7b54c --- /dev/null +++ b/taskfile/output.go @@ -0,0 +1,55 @@ +package taskfile + +import ( + "fmt" +) + +// Output of the Task output +type Output struct { + // Name of the Output. + Name string `yaml:"-"` + // Group specific style + Group OutputGroup +} + +// IsSet returns true if and only if a custom output style is set. +func (s *Output) IsSet() bool { + return s.Name != "" +} + +// UnmarshalYAML implements yaml.Unmarshaler +// It accepts a scalar node representing the Output.Name or a mapping node representing the OutputGroup. +func (s *Output) UnmarshalYAML(unmarshal func(interface{}) error) error { + var name string + if err := unmarshal(&name); err == nil { + s.Name = name + return nil + } + var tmp struct { + Group *OutputGroup + } + if err := unmarshal(&tmp); err != nil { + return fmt.Errorf("task: output style must be a string or mapping with a \"group\" key: %w", err) + } + if tmp.Group == nil { + return fmt.Errorf("task: output style must have the \"group\" key when in mapping form") + } + *s = Output{ + Name: "group", + Group: *tmp.Group, + } + return nil +} + +// OutputGroup is the style options specific to the Group style. +type OutputGroup struct { + Begin, End string +} + +// IsSet returns true if and only if a custom output style is set. +func (g *OutputGroup) IsSet() bool { + if g == nil { + return false + } + return g.Begin != "" || g.End != "" +} diff --git a/taskfile/taskfile.go b/taskfile/taskfile.go index d28534f1..ff7b1bbf 100644 --- a/taskfile/taskfile.go +++ b/taskfile/taskfile.go @@ -3,15 +3,13 @@ package taskfile import ( "fmt" "strconv" - - "github.com/go-task/task/v3/internal/output" ) // Taskfile represents a Taskfile.yml type Taskfile struct { Version string Expansions int - Output output.Style + Output Output Method string Includes *IncludedTaskfiles Vars *Vars @@ -27,7 +25,7 @@ func (tf *Taskfile) UnmarshalYAML(unmarshal func(interface{}) error) error { var taskfile struct { Version string Expansions int - Output output.Style + Output Output Method string Includes *IncludedTaskfiles Vars *Vars