From 87a200e42cdad095cf64af6a1a1ded8cef6f7e1a Mon Sep 17 00:00:00 2001 From: Andrey Nering Date: Sat, 17 Feb 2018 16:12:41 -0200 Subject: [PATCH] Extract some functionality to its own packages Like variable and template handling, and logging --- Taskvars.yml | 4 + errors.go | 9 - help.go | 4 +- internal/compiler/compiler.go | 12 ++ internal/compiler/env.go | 24 +++ internal/compiler/v1/compiler_v1.go | 145 +++++++++++++++ internal/logger/logger.go | 38 ++++ internal/templater/funcs.go | 52 ++++++ internal/templater/templater.go | 73 ++++++++ log.go | 31 ---- task.go | 35 ++-- task_test.go | 9 +- variables.go | 267 ++-------------------------- watch.go | 12 +- 14 files changed, 402 insertions(+), 313 deletions(-) create mode 100644 internal/compiler/compiler.go create mode 100644 internal/compiler/env.go create mode 100644 internal/compiler/v1/compiler_v1.go create mode 100644 internal/logger/logger.go create mode 100644 internal/templater/funcs.go create mode 100644 internal/templater/templater.go delete mode 100644 log.go diff --git a/Taskvars.yml b/Taskvars.yml index cc67ecde..0dda4d06 100644 --- a/Taskvars.yml +++ b/Taskvars.yml @@ -5,6 +5,10 @@ GO_PACKAGES: . ./cmd/task ./internal/args + ./internal/compiler + ./internal/compiler/v1 ./internal/execext + ./internal/logger ./internal/status ./internal/taskfile + ./internal/templater diff --git a/errors.go b/errors.go index 0ef9a7b5..e0bcab73 100644 --- a/errors.go +++ b/errors.go @@ -51,15 +51,6 @@ func (err *cantWatchNoSourcesError) Error() string { return fmt.Sprintf(`task: Can't watch task "%s" because it has no specified sources`, err.taskName) } -type dynamicVarError struct { - cause error - cmd string -} - -func (err *dynamicVarError) Error() string { - return fmt.Sprintf(`task: Command "%s" in taskvars file failed: %s`, err.cmd, err.cause) -} - // MaximumTaskCallExceededError is returned when a task is called too // many times. In this case you probably have a cyclic dependendy or // infinite loop diff --git a/help.go b/help.go index 07eb906f..50b31fae 100644 --- a/help.go +++ b/help.go @@ -12,10 +12,10 @@ import ( func (e *Executor) PrintTasksHelp() { tasks := e.tasksWithDesc() if len(tasks) == 0 { - e.outf("task: No tasks with description available") + e.Logger.Outf("task: No tasks with description available") return } - e.outf("task: Available tasks for this project:") + e.Logger.Outf("task: Available tasks for this project:") // Format in tab-separated columns with a tab stop of 8. w := tabwriter.NewWriter(e.Stdout, 0, 8, 0, '\t', 0) diff --git a/internal/compiler/compiler.go b/internal/compiler/compiler.go new file mode 100644 index 00000000..efe121cb --- /dev/null +++ b/internal/compiler/compiler.go @@ -0,0 +1,12 @@ +package compiler + +import ( + "github.com/go-task/task/internal/taskfile" +) + +// Compiler handles compilation of a task before its execution. +// E.g. variable merger, template processing, etc. +type Compiler interface { + GetVariables(t *taskfile.Task, call taskfile.Call) (taskfile.Vars, error) + HandleDynamicVar(v taskfile.Var) (string, error) +} diff --git a/internal/compiler/env.go b/internal/compiler/env.go new file mode 100644 index 00000000..aa39e72b --- /dev/null +++ b/internal/compiler/env.go @@ -0,0 +1,24 @@ +package compiler + +import ( + "os" + "strings" + + "github.com/go-task/task/internal/taskfile" +) + +// GetEnviron the all return all environment variables encapsulated on a +// taskfile.Vars +func GetEnviron() taskfile.Vars { + var ( + env = os.Environ() + m = make(taskfile.Vars, len(env)) + ) + + for _, e := range env { + keyVal := strings.SplitN(e, "=", 2) + key, val := keyVal[0], keyVal[1] + m[key] = taskfile.Var{Static: val} + } + return m +} diff --git a/internal/compiler/v1/compiler_v1.go b/internal/compiler/v1/compiler_v1.go new file mode 100644 index 00000000..5edd6a9b --- /dev/null +++ b/internal/compiler/v1/compiler_v1.go @@ -0,0 +1,145 @@ +package v1 + +import ( + "bytes" + "fmt" + "strings" + "sync" + + "github.com/go-task/task/internal/compiler" + "github.com/go-task/task/internal/execext" + "github.com/go-task/task/internal/logger" + "github.com/go-task/task/internal/taskfile" + "github.com/go-task/task/internal/templater" +) + +var _ compiler.Compiler = &CompilerV1{} + +type CompilerV1 struct { + Dir string + Vars taskfile.Vars + + Logger *logger.Logger + + dynamicCache map[string]string + muDynamicCache sync.Mutex +} + +// GetVariables returns fully resolved variables following the priority order: +// 1. Call variables (should already have been resolved) +// 2. Environment (should not need to be resolved) +// 3. Task variables, resolved with access to: +// - call, taskvars and environment variables +// 4. Taskvars variables, resolved with access to: +// - environment variables +func (c *CompilerV1) GetVariables(t *taskfile.Task, call taskfile.Call) (taskfile.Vars, error) { + merge := func(dest taskfile.Vars, srcs ...taskfile.Vars) { + for _, src := range srcs { + for k, v := range src { + dest[k] = v + } + } + } + varsKeys := func(srcs ...taskfile.Vars) []string { + m := make(map[string]struct{}) + for _, src := range srcs { + for k := range src { + m[k] = struct{}{} + } + } + lst := make([]string, 0, len(m)) + for k := range m { + lst = append(lst, k) + } + return lst + } + replaceVars := func(dest taskfile.Vars, keys []string) error { + r := templater.Templater{Vars: dest} + for _, k := range keys { + v := dest[k] + dest[k] = taskfile.Var{ + Static: r.Replace(v.Static), + Sh: r.Replace(v.Sh), + } + } + return r.Err() + } + resolveShell := func(dest taskfile.Vars, keys []string) error { + for _, k := range keys { + v := dest[k] + static, err := c.HandleDynamicVar(v) + if err != nil { + return err + } + dest[k] = taskfile.Var{Static: static} + } + return nil + } + update := func(dest taskfile.Vars, srcs ...taskfile.Vars) error { + merge(dest, srcs...) + // updatedKeys ensures template evaluation is run only once. + updatedKeys := varsKeys(srcs...) + if err := replaceVars(dest, updatedKeys); err != nil { + return err + } + return resolveShell(dest, updatedKeys) + } + + // Resolve taskvars variables to "result" with environment override variables. + override := compiler.GetEnviron() + result := make(taskfile.Vars, len(c.Vars)+len(t.Vars)+len(override)) + if err := update(result, c.Vars, override); err != nil { + return nil, err + } + // Resolve task variables to "result" with environment and call override variables. + merge(override, call.Vars) + if err := update(result, t.Vars, override); err != nil { + return nil, err + } + return result, nil +} + +func (c *CompilerV1) HandleDynamicVar(v taskfile.Var) (string, error) { + if v.Static != "" || v.Sh == "" { + return v.Static, nil + } + + c.muDynamicCache.Lock() + defer c.muDynamicCache.Unlock() + + if c.dynamicCache == nil { + c.dynamicCache = make(map[string]string, 30) + } + if result, ok := c.dynamicCache[v.Sh]; ok { + return result, nil + } + + var stdout bytes.Buffer + opts := &execext.RunCommandOptions{ + Command: v.Sh, + Dir: c.Dir, + Stdout: &stdout, + Stderr: c.Logger.Stderr, + } + if err := execext.RunCommand(opts); err != nil { + return "", &dynamicVarError{cause: err, cmd: opts.Command} + } + + // Trim a single trailing newline from the result to make most command + // output easier to use in shell commands. + result := strings.TrimSuffix(stdout.String(), "\n") + + c.dynamicCache[v.Sh] = result + c.Logger.VerboseErrf(`task: dynamic variable: '%s' result: '%s'`, v.Sh, result) + + return result, nil +} + +type dynamicVarError struct { + cause error + cmd string +} + +func (err *dynamicVarError) Error() string { + return fmt.Sprintf(`task: Command "%s" in taskvars file failed: %s`, err.cmd, err.cause) +} diff --git a/internal/logger/logger.go b/internal/logger/logger.go new file mode 100644 index 00000000..85c971d4 --- /dev/null +++ b/internal/logger/logger.go @@ -0,0 +1,38 @@ +package logger + +import ( + "fmt" + "io" +) + +type Logger struct { + Stdout io.Writer + Stderr io.Writer + Verbose bool +} + +func (l *Logger) Outf(s string, args ...interface{}) { + if len(args) == 0 { + s, args = "%s", []interface{}{s} + } + fmt.Fprintf(l.Stdout, s+"\n", args...) +} + +func (l *Logger) VerboseOutf(s string, args ...interface{}) { + if l.Verbose { + l.Outf(s, args...) + } +} + +func (l *Logger) Errf(s string, args ...interface{}) { + if len(args) == 0 { + s, args = "%s", []interface{}{s} + } + fmt.Fprintf(l.Stderr, s+"\n", args...) +} + +func (l *Logger) VerboseErrf(s string, args ...interface{}) { + if l.Verbose { + l.Errf(s, args...) + } +} diff --git a/internal/templater/funcs.go b/internal/templater/funcs.go new file mode 100644 index 00000000..96f6080d --- /dev/null +++ b/internal/templater/funcs.go @@ -0,0 +1,52 @@ +package templater + +import ( + "path/filepath" + "runtime" + "strings" + "text/template" + + "github.com/Masterminds/sprig" +) + +var ( + templateFuncs template.FuncMap +) + +func init() { + taskFuncs := template.FuncMap{ + "OS": func() string { return runtime.GOOS }, + "ARCH": func() string { return runtime.GOARCH }, + "catLines": func(s string) string { + s = strings.Replace(s, "\r\n", " ", -1) + return strings.Replace(s, "\n", " ", -1) + }, + "splitLines": func(s string) []string { + s = strings.Replace(s, "\r\n", "\n", -1) + return strings.Split(s, "\n") + }, + "fromSlash": func(path string) string { + return filepath.FromSlash(path) + }, + "toSlash": func(path string) string { + return filepath.ToSlash(path) + }, + "exeExt": func() string { + if runtime.GOOS == "windows" { + return ".exe" + } + return "" + }, + // IsSH is deprecated. + "IsSH": func() bool { return true }, + } + // Deprecated aliases for renamed functions. + taskFuncs["FromSlash"] = taskFuncs["fromSlash"] + taskFuncs["ToSlash"] = taskFuncs["toSlash"] + taskFuncs["ExeExt"] = taskFuncs["exeExt"] + + templateFuncs = sprig.TxtFuncMap() + for k, v := range taskFuncs { + templateFuncs[k] = v + } +} diff --git a/internal/templater/templater.go b/internal/templater/templater.go new file mode 100644 index 00000000..dfd6572b --- /dev/null +++ b/internal/templater/templater.go @@ -0,0 +1,73 @@ +package templater + +import ( + "bytes" + "text/template" + + "github.com/go-task/task/internal/taskfile" +) + +// Templater is a help struct that allow us to call "replaceX" funcs multiple +// times, without having to check for error each time. The first error that +// happen will be assigned to r.err, and consecutive calls to funcs will just +// return the zero value. +type Templater struct { + Vars taskfile.Vars + + strMap map[string]string + err error +} + +func (r *Templater) Replace(str string) string { + if r.err != nil || str == "" { + return "" + } + + templ, err := template.New("").Funcs(templateFuncs).Parse(str) + if err != nil { + r.err = err + return "" + } + + if r.strMap == nil { + r.strMap = r.Vars.ToStringMap() + } + + var b bytes.Buffer + if err = templ.Execute(&b, r.strMap); err != nil { + r.err = err + return "" + } + return b.String() +} + +func (r *Templater) ReplaceSlice(strs []string) []string { + if r.err != nil || len(strs) == 0 { + return nil + } + + new := make([]string, len(strs)) + for i, str := range strs { + new[i] = r.Replace(str) + } + return new +} + +func (r *Templater) ReplaceVars(vars taskfile.Vars) taskfile.Vars { + if r.err != nil || len(vars) == 0 { + return nil + } + + new := make(taskfile.Vars, len(vars)) + for k, v := range vars { + new[k] = taskfile.Var{ + Static: r.Replace(v.Static), + Sh: r.Replace(v.Sh), + } + } + return new +} + +func (r *Templater) Err() error { + return r.err +} diff --git a/log.go b/log.go deleted file mode 100644 index fc168130..00000000 --- a/log.go +++ /dev/null @@ -1,31 +0,0 @@ -package task - -import ( - "fmt" -) - -func (e *Executor) outf(s string, args ...interface{}) { - if len(args) == 0 { - s, args = "%s", []interface{}{s} - } - fmt.Fprintf(e.Stdout, s+"\n", args...) -} - -func (e *Executor) verboseOutf(s string, args ...interface{}) { - if e.Verbose { - e.outf(s, args...) - } -} - -func (e *Executor) errf(s string, args ...interface{}) { - if len(args) == 0 { - s, args = "%s", []interface{}{s} - } - fmt.Fprintf(e.Stderr, s+"\n", args...) -} - -func (e *Executor) verboseErrf(s string, args ...interface{}) { - if e.Verbose { - e.errf(s, args...) - } -} diff --git a/task.go b/task.go index 3ed284d5..ba374f3a 100644 --- a/task.go +++ b/task.go @@ -5,10 +5,12 @@ import ( "fmt" "io" "os" - "sync" "sync/atomic" + "github.com/go-task/task/internal/compiler" + compilerv1 "github.com/go-task/task/internal/compiler/v1" "github.com/go-task/task/internal/execext" + "github.com/go-task/task/internal/logger" "github.com/go-task/task/internal/taskfile" "golang.org/x/sync/errgroup" @@ -37,12 +39,12 @@ type Executor struct { Stdout io.Writer Stderr io.Writer + Logger *logger.Logger + Compiler compiler.Compiler + taskvars taskfile.Vars taskCallCount map[string]*int32 - - dynamicCache map[string]string - muDynamicCache sync.Mutex } // Run runs Task @@ -59,16 +61,27 @@ func (e *Executor) Run(calls ...taskfile.Call) error { if e.Stderr == nil { e.Stderr = os.Stderr } + if e.Logger == nil { + e.Logger = &logger.Logger{ + Stdout: e.Stdout, + Stderr: e.Stderr, + Verbose: e.Verbose, + } + } + // TODO: Add version 2 + if e.Compiler == nil { + e.Compiler = &compilerv1.CompilerV1{ + Dir: e.Dir, + Vars: e.taskvars, + Logger: e.Logger, + } + } e.taskCallCount = make(map[string]*int32, len(e.Taskfile.Tasks)) for k := range e.Taskfile.Tasks { e.taskCallCount[k] = new(int32) } - if e.dynamicCache == nil { - e.dynamicCache = make(map[string]string, 10) - } - // check if given tasks exist for _, c := range calls { if _, ok := e.Taskfile.Tasks[c.Task]; !ok { @@ -111,7 +124,7 @@ func (e *Executor) RunTask(ctx context.Context, call taskfile.Call) error { } if upToDate { if !e.Silent { - e.errf(`task: Task "%s" is up to date`, t.Task) + e.Logger.Errf(`task: Task "%s" is up to date`, t.Task) } return nil } @@ -120,7 +133,7 @@ func (e *Executor) RunTask(ctx context.Context, call taskfile.Call) error { for i := range t.Cmds { if err := e.runCommand(ctx, t, call, i); err != nil { if err2 := statusOnError(t); err2 != nil { - e.verboseErrf("task: error cleaning status on error: %v", err2) + e.Logger.VerboseErrf("task: error cleaning status on error: %v", err2) } return &taskRunError{t.Task, err} } @@ -150,7 +163,7 @@ func (e *Executor) runCommand(ctx context.Context, t *taskfile.Task, call taskfi } if e.Verbose || (!cmd.Silent && !t.Silent && !e.Silent) { - e.errf(cmd.Cmd) + e.Logger.Errf(cmd.Cmd) } return execext.RunCommand(&execext.RunCommandOptions{ diff --git a/task_test.go b/task_test.go index f8216530..f1931f68 100644 --- a/task_test.go +++ b/task_test.go @@ -209,10 +209,12 @@ func TestStatus(t *testing.T) { t.Errorf("File should not exists: %v", err) } + var buff bytes.Buffer e := &task.Executor{ Dir: dir, - Stdout: ioutil.Discard, - Stderr: ioutil.Discard, + Stdout: &buff, + Stderr: &buff, + Silent: true, } assert.NoError(t, e.ReadTaskfile()) assert.NoError(t, e.Run(taskfile.Call{Task: "gen-foo"})) @@ -221,8 +223,7 @@ func TestStatus(t *testing.T) { t.Errorf("File should exists: %v", err) } - buff := bytes.NewBuffer(nil) - e.Stdout, e.Stderr = buff, buff + e.Silent = false assert.NoError(t, e.Run(taskfile.Call{Task: "gen-foo"})) if buff.String() != `task: Task "gen-foo" is up to date`+"\n" { diff --git a/variables.go b/variables.go index c25463bd..ca0722bb 100644 --- a/variables.go +++ b/variables.go @@ -1,17 +1,11 @@ package task import ( - "bytes" - "os" "path/filepath" - "runtime" - "strings" - "text/template" - "github.com/go-task/task/internal/execext" "github.com/go-task/task/internal/taskfile" + "github.com/go-task/task/internal/templater" - "github.com/Masterminds/sprig" "github.com/mitchellh/go-homedir" ) @@ -20,131 +14,6 @@ var ( TaskvarsFilePath = "Taskvars" ) -func getEnvironmentVariables() taskfile.Vars { - var ( - env = os.Environ() - m = make(taskfile.Vars, len(env)) - ) - - for _, e := range env { - keyVal := strings.SplitN(e, "=", 2) - key, val := keyVal[0], keyVal[1] - m[key] = taskfile.Var{Static: val} - } - return m -} - -// getVariables returns fully resolved variables following the priority order: -// 1. Call variables (should already have been resolved) -// 2. Environment (should not need to be resolved) -// 3. Task variables, resolved with access to: -// - call, taskvars and environment variables -// 4. Taskvars variables, resolved with access to: -// - environment variables -func (e *Executor) getVariables(call taskfile.Call) (taskfile.Vars, error) { - t, ok := e.Taskfile.Tasks[call.Task] - if !ok { - return nil, &taskNotFoundError{call.Task} - } - - merge := func(dest taskfile.Vars, srcs ...taskfile.Vars) { - for _, src := range srcs { - for k, v := range src { - dest[k] = v - } - } - } - varsKeys := func(srcs ...taskfile.Vars) []string { - m := make(map[string]struct{}) - for _, src := range srcs { - for k := range src { - m[k] = struct{}{} - } - } - lst := make([]string, 0, len(m)) - for k := range m { - lst = append(lst, k) - } - return lst - } - replaceVars := func(dest taskfile.Vars, keys []string) error { - r := varReplacer{vars: dest} - for _, k := range keys { - v := dest[k] - dest[k] = taskfile.Var{ - Static: r.replace(v.Static), - Sh: r.replace(v.Sh), - } - } - return r.err - } - resolveShell := func(dest taskfile.Vars, keys []string) error { - for _, k := range keys { - v := dest[k] - static, err := e.handleShVar(v) - if err != nil { - return err - } - dest[k] = taskfile.Var{Static: static} - } - return nil - } - update := func(dest taskfile.Vars, srcs ...taskfile.Vars) error { - merge(dest, srcs...) - // updatedKeys ensures template evaluation is run only once. - updatedKeys := varsKeys(srcs...) - if err := replaceVars(dest, updatedKeys); err != nil { - return err - } - return resolveShell(dest, updatedKeys) - } - - // Resolve taskvars variables to "result" with environment override variables. - override := getEnvironmentVariables() - result := make(taskfile.Vars, len(e.taskvars)+len(t.Vars)+len(override)) - if err := update(result, e.taskvars, override); err != nil { - return nil, err - } - // Resolve task variables to "result" with environment and call override variables. - merge(override, call.Vars) - if err := update(result, t.Vars, override); err != nil { - return nil, err - } - return result, nil -} - -func (e *Executor) handleShVar(v taskfile.Var) (string, error) { - if v.Static != "" || v.Sh == "" { - return v.Static, nil - } - e.muDynamicCache.Lock() - defer e.muDynamicCache.Unlock() - - if result, ok := e.dynamicCache[v.Sh]; ok { - return result, nil - } - - var stdout bytes.Buffer - opts := &execext.RunCommandOptions{ - Command: v.Sh, - Dir: e.Dir, - Stdout: &stdout, - Stderr: e.Stderr, - } - if err := execext.RunCommand(opts); err != nil { - return "", &dynamicVarError{cause: err, cmd: opts.Command} - } - - // Trim a single trailing newline from the result to make most command - // output easier to use in shell commands. - result := strings.TrimSuffix(stdout.String(), "\n") - - e.dynamicCache[v.Sh] = result - e.verboseErrf(`task: dynamic variable: '%s' result: '%s'`, v.Sh, result) - - return result, nil -} - // CompiledTask returns a copy of a task, but replacing variables in almost all // properties using the Go template package. func (e *Executor) CompiledTask(call taskfile.Call) (*taskfile.Task, error) { @@ -153,23 +22,23 @@ func (e *Executor) CompiledTask(call taskfile.Call) (*taskfile.Task, error) { return nil, &taskNotFoundError{call.Task} } - vars, err := e.getVariables(call) + vars, err := e.Compiler.GetVariables(origTask, call) if err != nil { return nil, err } - r := varReplacer{vars: vars} + r := templater.Templater{Vars: vars} new := taskfile.Task{ Task: origTask.Task, - Desc: r.replace(origTask.Desc), - Sources: r.replaceSlice(origTask.Sources), - Generates: r.replaceSlice(origTask.Generates), - Status: r.replaceSlice(origTask.Status), - Dir: r.replace(origTask.Dir), + Desc: r.Replace(origTask.Desc), + Sources: r.ReplaceSlice(origTask.Sources), + Generates: r.ReplaceSlice(origTask.Generates), + Status: r.ReplaceSlice(origTask.Status), + Dir: r.Replace(origTask.Dir), Vars: nil, - Env: r.replaceVars(origTask.Env), + Env: r.ReplaceVars(origTask.Env), Silent: origTask.Silent, - Method: r.replace(origTask.Method), + Method: r.Replace(origTask.Method), } new.Dir, err = homedir.Expand(new.Dir) if err != nil { @@ -179,7 +48,7 @@ func (e *Executor) CompiledTask(call taskfile.Call) (*taskfile.Task, error) { new.Dir = filepath.Join(e.Dir, new.Dir) } for k, v := range new.Env { - static, err := e.handleShVar(v) + static, err := e.Compiler.HandleDynamicVar(v) if err != nil { return nil, err } @@ -190,10 +59,10 @@ func (e *Executor) CompiledTask(call taskfile.Call) (*taskfile.Task, error) { new.Cmds = make([]*taskfile.Cmd, len(origTask.Cmds)) for i, cmd := range origTask.Cmds { new.Cmds[i] = &taskfile.Cmd{ - Task: r.replace(cmd.Task), + Task: r.Replace(cmd.Task), Silent: cmd.Silent, - Cmd: r.replace(cmd.Cmd), - Vars: r.replaceVars(cmd.Vars), + Cmd: r.Replace(cmd.Cmd), + Vars: r.ReplaceVars(cmd.Vars), } } @@ -202,113 +71,11 @@ func (e *Executor) CompiledTask(call taskfile.Call) (*taskfile.Task, error) { new.Deps = make([]*taskfile.Dep, len(origTask.Deps)) for i, dep := range origTask.Deps { new.Deps[i] = &taskfile.Dep{ - Task: r.replace(dep.Task), - Vars: r.replaceVars(dep.Vars), + Task: r.Replace(dep.Task), + Vars: r.ReplaceVars(dep.Vars), } } } - return &new, r.err -} - -// varReplacer is a help struct that allow us to call "replaceX" funcs multiple -// times, without having to check for error each time. The first error that -// happen will be assigned to r.err, and consecutive calls to funcs will just -// return the zero value. -type varReplacer struct { - vars taskfile.Vars - strMap map[string]string - err error -} - -func (r *varReplacer) replace(str string) string { - if r.err != nil || str == "" { - return "" - } - - templ, err := template.New("").Funcs(templateFuncs).Parse(str) - if err != nil { - r.err = err - return "" - } - - if r.strMap == nil { - r.strMap = r.vars.ToStringMap() - } - - var b bytes.Buffer - if err = templ.Execute(&b, r.strMap); err != nil { - r.err = err - return "" - } - return b.String() -} - -func (r *varReplacer) replaceSlice(strs []string) []string { - if r.err != nil || len(strs) == 0 { - return nil - } - - new := make([]string, len(strs)) - for i, str := range strs { - new[i] = r.replace(str) - } - return new -} - -func (r *varReplacer) replaceVars(vars taskfile.Vars) taskfile.Vars { - if r.err != nil || len(vars) == 0 { - return nil - } - - new := make(taskfile.Vars, len(vars)) - for k, v := range vars { - new[k] = taskfile.Var{ - Static: r.replace(v.Static), - Sh: r.replace(v.Sh), - } - } - return new -} - -var ( - templateFuncs template.FuncMap -) - -func init() { - taskFuncs := template.FuncMap{ - "OS": func() string { return runtime.GOOS }, - "ARCH": func() string { return runtime.GOARCH }, - "catLines": func(s string) string { - s = strings.Replace(s, "\r\n", " ", -1) - return strings.Replace(s, "\n", " ", -1) - }, - "splitLines": func(s string) []string { - s = strings.Replace(s, "\r\n", "\n", -1) - return strings.Split(s, "\n") - }, - "fromSlash": func(path string) string { - return filepath.FromSlash(path) - }, - "toSlash": func(path string) string { - return filepath.ToSlash(path) - }, - "exeExt": func() string { - if runtime.GOOS == "windows" { - return ".exe" - } - return "" - }, - // IsSH is deprecated. - "IsSH": func() bool { return true }, - } - // Deprecated aliases for renamed functions. - taskFuncs["FromSlash"] = taskFuncs["fromSlash"] - taskFuncs["ToSlash"] = taskFuncs["toSlash"] - taskFuncs["ExeExt"] = taskFuncs["exeExt"] - - templateFuncs = sprig.TxtFuncMap() - for k, v := range taskFuncs { - templateFuncs[k] = v - } + return &new, r.Err() } diff --git a/watch.go b/watch.go index 64b18895..43d346b2 100644 --- a/watch.go +++ b/watch.go @@ -21,14 +21,14 @@ func (e *Executor) watchTasks(calls ...taskfile.Call) error { for i, c := range calls { tasks[i] = c.Task } - e.errf("task: Started watching for tasks: %s", strings.Join(tasks, ", ")) + e.Logger.Errf("task: Started watching for tasks: %s", strings.Join(tasks, ", ")) ctx, cancel := context.WithCancel(context.Background()) for _, c := range calls { c := c go func() { if err := e.RunTask(ctx, c); err != nil && !isContextError(err) { - e.errf("%v", err) + e.Logger.Errf("%v", err) } }() } @@ -44,7 +44,7 @@ func (e *Executor) watchTasks(calls ...taskfile.Call) error { for { select { case event := <-w.Event: - e.verboseErrf("task: received watch event: %v", event) + e.Logger.VerboseErrf("task: received watch event: %v", event) cancel() ctx, cancel = context.WithCancel(context.Background()) @@ -52,7 +52,7 @@ func (e *Executor) watchTasks(calls ...taskfile.Call) error { c := c go func() { if err := e.RunTask(ctx, c); err != nil && !isContextError(err) { - e.errf("%v", err) + e.Logger.Errf("%v", err) } }() } @@ -63,7 +63,7 @@ func (e *Executor) watchTasks(calls ...taskfile.Call) error { w.TriggerEvent(watcher.Remove, nil) }() default: - e.errf("%v", err) + e.Logger.Errf("%v", err) } case <-w.Closed: return @@ -75,7 +75,7 @@ func (e *Executor) watchTasks(calls ...taskfile.Call) error { // re-register each second because we can have new files for { if err := e.registerWatchedFiles(w, calls...); err != nil { - e.errf("%v", err) + e.Logger.Errf("%v", err) } time.Sleep(time.Second) }