diff --git a/file.go b/file.go index 60428ba3..46d86837 100644 --- a/file.go +++ b/file.go @@ -2,6 +2,7 @@ package task import ( "os" + "path/filepath" "time" "github.com/mattn/go-zglob" @@ -20,8 +21,9 @@ func maxTime(a, b time.Time) time.Time { return b } -func getPatternsMinTime(patterns []string) (m time.Time, err error) { +func getPatternsMinTime(dir string, patterns []string) (m time.Time, err error) { for _, p := range patterns { + p = filepath.Join(dir, p) mp, err := getPatternMinTime(p) if err != nil { return time.Time{}, err @@ -30,8 +32,9 @@ func getPatternsMinTime(patterns []string) (m time.Time, err error) { } return } -func getPatternsMaxTime(patterns []string) (m time.Time, err error) { +func getPatternsMaxTime(dir string, patterns []string) (m time.Time, err error) { for _, p := range patterns { + p = filepath.Join(dir, p) mp, err := getPatternMaxTime(p) if err != nil { return time.Time{}, err diff --git a/read_taskfile.go b/read_taskfile.go index f3328ac4..cc5da31d 100644 --- a/read_taskfile.go +++ b/read_taskfile.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "io/ioutil" + "path/filepath" "runtime" "github.com/BurntSushi/toml" @@ -13,13 +14,15 @@ import ( // ReadTaskfile parses Taskfile from the disk func (e *Executor) ReadTaskfile() error { + path := filepath.Join(e.Dir, TaskFilePath) + var err error - e.Tasks, err = e.readTaskfileData(TaskFilePath) + e.Tasks, err = e.readTaskfileData(path) if err != nil { return err } - osTasks, err := e.readTaskfileData(fmt.Sprintf("%s_%s", TaskFilePath, runtime.GOOS)) + osTasks, err := e.readTaskfileData(fmt.Sprintf("%s_%s", path, runtime.GOOS)) if err != nil { switch err.(type) { case taskFileNotFound: diff --git a/task.go b/task.go index d887e8cd..599d5968 100644 --- a/task.go +++ b/task.go @@ -6,6 +6,7 @@ import ( "fmt" "log" "os" + "path/filepath" "strings" "github.com/go-task/task/execext" @@ -21,6 +22,7 @@ const ( // Executor executes a Taskfile type Executor struct { Tasks Tasks + Dir string Force bool Watch bool @@ -86,7 +88,7 @@ func (e *Executor) RunTask(ctx context.Context, name string) error { } if !e.Force { - upToDate, err := t.isUpToDate(ctx) + upToDate, err := e.isTaskUpToDate(ctx, name) if err != nil { return err } @@ -112,7 +114,7 @@ func (e *Executor) runDeps(ctx context.Context, task string) error { dep := d g.Go(func() error { - dep, err := t.ReplaceVariables(dep) + dep, err := e.ReplaceVariables(task, dep) if err != nil { return err } @@ -130,15 +132,23 @@ func (e *Executor) runDeps(ctx context.Context, task string) error { return nil } -func (t *Task) isUpToDate(ctx context.Context) (bool, error) { +func (e *Executor) isTaskUpToDate(ctx context.Context, task string) (bool, error) { + t := e.Tasks[task] + if len(t.Status) > 0 { - return t.isUpToDateStatus(ctx) + return e.isUpToDateStatus(ctx, task) } - return t.isUpToDateTimestamp(ctx) + return e.isUpToDateTimestamp(ctx, task) } -func (t *Task) isUpToDateStatus(ctx context.Context) (bool, error) { - environ, err := t.getEnviron() +func (e *Executor) isUpToDateStatus(ctx context.Context, task string) (bool, error) { + t := e.Tasks[task] + + environ, err := e.getEnviron(task) + if err != nil { + return false, err + } + dir, err := e.getTaskDir(task) if err != nil { return false, err } @@ -147,7 +157,7 @@ func (t *Task) isUpToDateStatus(ctx context.Context) (bool, error) { err = execext.RunCommand(&execext.RunCommandOptions{ Context: ctx, Command: s, - Dir: t.Dir, + Dir: dir, Env: environ, }) if err != nil { @@ -157,26 +167,33 @@ func (t *Task) isUpToDateStatus(ctx context.Context) (bool, error) { return true, nil } -func (t *Task) isUpToDateTimestamp(ctx context.Context) (bool, error) { +func (e *Executor) isUpToDateTimestamp(ctx context.Context, task string) (bool, error) { + t := e.Tasks[task] + if len(t.Sources) == 0 || len(t.Generates) == 0 { return false, nil } - sources, err := t.ReplaceSliceVariables(t.Sources) - if err != nil { - return false, err - } - generates, err := t.ReplaceSliceVariables(t.Generates) + dir, err := e.getTaskDir(task) if err != nil { return false, err } - sourcesMaxTime, err := getPatternsMaxTime(sources) + sources, err := e.ReplaceSliceVariables(task, t.Sources) + if err != nil { + return false, err + } + generates, err := e.ReplaceSliceVariables(task, t.Generates) + if err != nil { + return false, err + } + + sourcesMaxTime, err := getPatternsMaxTime(dir, sources) if err != nil || sourcesMaxTime.IsZero() { return false, nil } - generatesMinTime, err := getPatternsMinTime(generates) + generatesMinTime, err := getPatternsMinTime(dir, generates) if err != nil || generatesMinTime.IsZero() { return false, nil } @@ -187,7 +204,7 @@ func (t *Task) isUpToDateTimestamp(ctx context.Context) (bool, error) { func (e *Executor) runCommand(ctx context.Context, task string, i int) error { t := e.Tasks[task] - c, err := t.ReplaceVariables(t.Cmds[i]) + c, err := e.ReplaceVariables(task, t.Cmds[i]) if err != nil { return err } @@ -200,12 +217,12 @@ func (e *Executor) runCommand(ctx context.Context, task string, i int) error { return nil } - dir, err := t.ReplaceVariables(t.Dir) + dir, err := e.getTaskDir(task) if err != nil { return err } - envs, err := t.getEnviron() + envs, err := e.getEnviron(task) if err != nil { return err } @@ -235,7 +252,24 @@ func (e *Executor) runCommand(ctx context.Context, task string, i int) error { return nil } -func (t *Task) getEnviron() ([]string, error) { +func (e *Executor) getTaskDir(name string) (string, error) { + t := e.Tasks[name] + + exeDir, err := e.ReplaceVariables(name, e.Dir) + if err != nil { + return "", err + } + taskDir, err := e.ReplaceVariables(name, t.Dir) + if err != nil { + return "", err + } + + return filepath.Join(exeDir, taskDir), nil +} + +func (e *Executor) getEnviron(task string) ([]string, error) { + t := e.Tasks[task] + if t.Env == nil { return nil, nil } @@ -243,7 +277,7 @@ func (t *Task) getEnviron() ([]string, error) { envs := os.Environ() for k, v := range t.Env { - env, err := t.ReplaceVariables(fmt.Sprintf("%s=%s", k, v)) + env, err := e.ReplaceVariables(task, fmt.Sprintf("%s=%s", k, v)) if err != nil { return nil, err } diff --git a/task_test.go b/task_test.go index 586893a1..e4dae54b 100644 --- a/task_test.go +++ b/task_test.go @@ -8,6 +8,10 @@ import ( "path/filepath" "strings" "testing" + + "github.com/go-task/task" + + "github.com/stretchr/testify/assert" ) func TestDeps(t *testing.T) { @@ -32,12 +36,11 @@ func TestDeps(t *testing.T) { _ = os.Remove(filepath.Join(dir, f)) } - c := exec.Command("task") - c.Dir = dir - if err := c.Run(); err != nil { - t.Error(err) - return + e := &task.Executor{ + Dir: dir, } + assert.NoError(t, e.ReadTaskfile()) + assert.NoError(t, e.Run("default")) for _, f := range files { f = filepath.Join(dir, f) @@ -65,13 +68,11 @@ func TestVars(t *testing.T) { _ = os.Remove(filepath.Join(dir, f.file)) } - c := exec.Command("task") - c.Dir = dir - - if err := c.Run(); err != nil { - t.Error(err) - return + e := &task.Executor{ + Dir: dir, } + assert.NoError(t, e.ReadTaskfile()) + assert.NoError(t, e.Run("default")) for _, f := range files { d, err := ioutil.ReadFile(filepath.Join(dir, f.file)) @@ -99,13 +100,11 @@ func TestTaskCall(t *testing.T) { _ = os.Remove(filepath.Join(dir, f)) } - c := exec.Command("task") - c.Dir = dir - - if err := c.Run(); err != nil { - t.Error(err) - return + e := &task.Executor{ + Dir: dir, } + assert.NoError(t, e.ReadTaskfile()) + assert.NoError(t, e.Run("default")) for _, f := range files { if _, err := os.Stat(filepath.Join(dir, f)); err != nil { diff --git a/variable_handling.go b/variable_handling.go index bd53a90e..26db4002 100644 --- a/variable_handling.go +++ b/variable_handling.go @@ -25,7 +25,7 @@ var ( ErrMultilineResultCmd = errors.New("Got multiline result from command") ) -func handleDynamicVariableContent(value string) (string, error) { +func (e *Executor) handleDynamicVariableContent(value string) (string, error) { if !strings.HasPrefix(value, "$") { return value, nil } @@ -34,6 +34,7 @@ func handleDynamicVariableContent(value string) (string, error) { opts := &execext.RunCommandOptions{ Command: strings.TrimPrefix(value, "$"), + Dir: e.Dir, Stdout: buff, Stderr: os.Stderr, } @@ -51,18 +52,20 @@ func handleDynamicVariableContent(value string) (string, error) { return result, nil } -func (t *Task) getVariables() (map[string]string, error) { +func (e *Executor) getVariables(task string) (map[string]string, error) { + t := e.Tasks[task] + localVariables := make(map[string]string) for key, value := range t.Vars { - val, err := handleDynamicVariableContent(value) + val, err := e.handleDynamicVariableContent(value) if err != nil { return nil, err } localVariables[key] = val } - if fileVariables, err := readTaskvarsFile(); err == nil { + if fileVariables, err := e.readTaskvarsFile(); err == nil { for key, value := range fileVariables { - val, err := handleDynamicVariableContent(value) + val, err := e.handleDynamicVariableContent(value) if err != nil { return nil, err } @@ -106,11 +109,11 @@ func init() { } // ReplaceSliceVariables writes vars into initial string slice -func (t *Task) ReplaceSliceVariables(initials []string) ([]string, error) { +func (e *Executor) ReplaceSliceVariables(task string, initials []string) ([]string, error) { result := make([]string, len(initials)) for i, s := range initials { var err error - result[i], err = t.ReplaceVariables(s) + result[i], err = e.ReplaceVariables(task, s) if err != nil { return nil, err } @@ -119,8 +122,8 @@ func (t *Task) ReplaceSliceVariables(initials []string) ([]string, error) { } // ReplaceVariables writes vars into initial string -func (t *Task) ReplaceVariables(initial string) (string, error) { - vars, err := t.getVariables() +func (e *Executor) ReplaceVariables(task, initial string) (string, error) { + vars, err := e.getVariables(task) if err != nil { return "", err } @@ -152,21 +155,23 @@ func getEnvironmentVariables() map[string]string { return m } -func readTaskvarsFile() (map[string]string, error) { +func (e *Executor) readTaskvarsFile() (map[string]string, error) { + file := filepath.Join(e.Dir, TaskvarsFilePath) + var variables map[string]string - if b, err := ioutil.ReadFile(TaskvarsFilePath + ".yml"); err == nil { + if b, err := ioutil.ReadFile(file + ".yml"); err == nil { if err := yaml.Unmarshal(b, &variables); err != nil { return nil, err } return variables, nil } - if b, err := ioutil.ReadFile(TaskvarsFilePath + ".json"); err == nil { + if b, err := ioutil.ReadFile(file + ".json"); err == nil { if err := json.Unmarshal(b, &variables); err != nil { return nil, err } return variables, nil } - if b, err := ioutil.ReadFile(TaskvarsFilePath + ".toml"); err == nil { + if b, err := ioutil.ReadFile(file + ".toml"); err == nil { if err := toml.Unmarshal(b, &variables); err != nil { return nil, err }