Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 14 additions & 15 deletions cmd/task/task.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,24 +22,24 @@ import (

func main() {
if err := run(); err != nil {
l := &logger.Logger{
l := logger.NewLogger(logger.LoggerOptions{
Stdout: os.Stdout,
Stderr: os.Stderr,
Verbose: flags.Verbose,
Color: flags.Color,
}
})
if err, ok := err.(*errors.TaskRunError); ok && flags.ExitCode {
emitCIErrorAnnotation(err)
l.Errf(logger.Red, "%v\n", err)
l.Errorf("%v\n", err)
os.Exit(err.TaskExitCode())
}
if err, ok := err.(errors.TaskError); ok {
emitCIErrorAnnotation(err)
l.Errf(logger.Red, "%v\n", err)
l.Errorf("%v\n", err)
os.Exit(err.Code())
}
emitCIErrorAnnotation(err)
l.Errf(logger.Red, "%v\n", err)
l.Errorf("%v\n", err)
os.Exit(errors.CodeUnknown)
}
os.Exit(errors.CodeOk)
Expand All @@ -58,12 +58,13 @@ func emitCIErrorAnnotation(err error) {
}

func run() error {
log := &logger.Logger{
Stdout: os.Stdout,
Stderr: os.Stderr,
Verbose: flags.Verbose,
Color: flags.Color,
}
log := logger.NewLogger(logger.LoggerOptions{
Stdout: os.Stdout,
Stderr: os.Stderr,
Verbose: flags.Verbose,
Color: flags.Color,
LogFormat: flags.LogFormat,
})

if err := flags.Validate(); err != nil {
return err
Expand Down Expand Up @@ -109,10 +110,8 @@ func run() error {
return err
}
if !flags.Silent {
if flags.Verbose {
log.Outf(logger.Default, "%s\n", task.DefaultTaskfile)
}
log.Outf(logger.Green, "Taskfile created: %s\n", filepathext.TryAbsToRel(finalPath))
log.VerboseOutf(logger.Default, "%s\n", task.DefaultTaskfile)
log.OutfDirect(logger.Green, "Taskfile created: %s\n", filepathext.TryAbsToRel(finalPath))
}
return nil
}
Expand Down
14 changes: 14 additions & 0 deletions executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ type (
Concurrency int
Interval time.Duration
Failfast bool
LogFormat string

// I/O
Stdin io.Reader
Expand Down Expand Up @@ -560,3 +561,16 @@ type failfastOption struct {
func (o *failfastOption) ApplyToExecutor(e *Executor) {
e.Failfast = o.failfast
}

// WithLogFormat sets the log format.
func WithLogFormat(logFormat string) ExecutorOption {
return &logFormatOption{logFormat: logFormat}
}

type logFormatOption struct {
logFormat string
}

func (o *logFormatOption) ApplyToExecutor(e *Executor) {
e.LogFormat = o.logFormat
}
6 changes: 3 additions & 3 deletions help.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,13 +80,13 @@ func (e *Executor) ListTasks(o ListOptions) (bool, error) {
}
if len(tasks) == 0 {
if o.ListOnlyTasksWithDescriptions {
e.Logger.Outf(logger.Yellow, "task: No tasks with description available. Try --list-all to list all tasks\n")
e.Logger.OutfDirect(logger.Yellow, "task: No tasks with description available. Try --list-all to list all tasks\n")
} else if o.ListAllTasks {
e.Logger.Outf(logger.Yellow, "task: No tasks available\n")
e.Logger.OutfDirect(logger.Yellow, "task: No tasks available\n")
}
return false, nil
}
e.Logger.Outf(logger.Default, "task: Available tasks for this project:\n")
e.Logger.OutfDirect(logger.Default, "task: Available tasks for this project:\n")

// Format in tab-separated columns with a tab stop of 8.
w := tabwriter.NewWriter(e.Stdout, 0, 8, 6, ' ', 0)
Expand Down
3 changes: 3 additions & 0 deletions internal/flags/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ var (
Timeout time.Duration
CacheExpiryDuration time.Duration
RemoteCacheDir string
LogFormat string
)

func init() {
Expand Down Expand Up @@ -148,6 +149,7 @@ func init() {
pflag.BoolVarP(&Failfast, "failfast", "F", getConfig(config, func() *bool { return &config.Failfast }, false), "When running tasks in parallel, stop all tasks if one fails.")
pflag.BoolVarP(&Global, "global", "g", false, "Runs global Taskfile, from $HOME/{T,t}askfile.{yml,yaml}.")
pflag.BoolVar(&Experiments, "experiments", false, "Lists all the available experiments and whether or not they are enabled.")
pflag.StringVar(&LogFormat, "log", "", `Log format ("json" or "text").`)

// Gentle force experiment will override the force flag and add a new force-all flag
if experiments.GentleForce.Enabled() {
Expand Down Expand Up @@ -291,6 +293,7 @@ func (o *flagsOption) ApplyToExecutor(e *task.Executor) {
task.WithTaskSorter(sorter),
task.WithVersionCheck(true),
task.WithFailfast(Failfast),
task.WithLogFormat(LogFormat),
)
}

Expand Down
112 changes: 112 additions & 0 deletions internal/logger/color.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
package logger

import (
"io"
"slices"
"strconv"
"strings"

"github.com/fatih/color"

"github.com/go-task/task/v3/internal/env"
)

var (
attrsReset = envColor("COLOR_RESET", color.Reset)
attrsFgBlue = envColor("COLOR_BLUE", color.FgBlue)
attrsFgGreen = envColor("COLOR_GREEN", color.FgGreen)
attrsFgCyan = envColor("COLOR_CYAN", color.FgCyan)
attrsFgYellow = envColor("COLOR_YELLOW", color.FgYellow)
attrsFgMagenta = envColor("COLOR_MAGENTA", color.FgMagenta)
attrsFgRed = envColor("COLOR_RED", color.FgRed)
attrsFgHiBlue = envColor("COLOR_BRIGHT_BLUE", color.FgHiBlue)
attrsFgHiGreen = envColor("COLOR_BRIGHT_GREEN", color.FgHiGreen)
attrsFgHiCyan = envColor("COLOR_BRIGHT_CYAN", color.FgHiCyan)
attrsFgHiYellow = envColor("COLOR_BRIGHT_YELLOW", color.FgHiYellow)
attrsFgHiMagenta = envColor("COLOR_BRIGHT_MAGENTA", color.FgHiMagenta)
attrsFgHiRed = envColor("COLOR_BRIGHT_RED", color.FgHiRed)
)

type (
Color func() PrintFunc
PrintFunc func(io.Writer, string, ...any)
)

func Default() PrintFunc {
return color.New(attrsReset...).FprintfFunc()
}

func Blue() PrintFunc {
return color.New(attrsFgBlue...).FprintfFunc()
}

func Green() PrintFunc {
return color.New(attrsFgGreen...).FprintfFunc()
}

func Cyan() PrintFunc {
return color.New(attrsFgCyan...).FprintfFunc()
}

func Yellow() PrintFunc {
return color.New(attrsFgYellow...).FprintfFunc()
}

func Magenta() PrintFunc {
return color.New(attrsFgMagenta...).FprintfFunc()
}

func Red() PrintFunc {
return color.New(attrsFgRed...).FprintfFunc()
}

func BrightBlue() PrintFunc {
return color.New(attrsFgHiBlue...).FprintfFunc()
}

func BrightGreen() PrintFunc {
return color.New(attrsFgHiGreen...).FprintfFunc()
}

func BrightCyan() PrintFunc {
return color.New(attrsFgHiCyan...).FprintfFunc()
}

func BrightYellow() PrintFunc {
return color.New(attrsFgHiYellow...).FprintfFunc()
}

func BrightMagenta() PrintFunc {
return color.New(attrsFgHiMagenta...).FprintfFunc()
}

func BrightRed() PrintFunc {
return color.New(attrsFgHiRed...).FprintfFunc()
}

func envColor(name string, defaultColor color.Attribute) []color.Attribute {
// Fetch the environment variable
override := env.GetTaskEnv(name)

// First, try splitting the string by commas (RGB shortcut syntax) and if it
// matches, then prepend the 256-color foreground escape sequence.
// Otherwise, split by semicolons (ANSI color codes) and use them as is.
attributeStrs := strings.Split(override, ",")
if len(attributeStrs) == 3 {
attributeStrs = slices.Concat([]string{"38", "2"}, attributeStrs)
} else {
attributeStrs = strings.Split(override, ";")
}

// Loop over the attributes and convert them to integers
attributes := make([]color.Attribute, len(attributeStrs))
for i, attributeStr := range attributeStrs {
attribute, err := strconv.Atoi(attributeStr)
if err != nil {
return []color.Attribute{defaultColor}
}
attributes[i] = color.Attribute(attribute)
}

return attributes
}
Loading
Loading