diff --git a/help.go b/help.go index 87d38098..381897ee 100644 --- a/help.go +++ b/help.go @@ -18,17 +18,18 @@ func (e *Executor) PrintTasksHelp() { // Format in tab-separated columns with a tab stop of 8. w := tabwriter.NewWriter(e.Stdout, 0, 8, 0, '\t', 0) for _, task := range tasks { - fmt.Fprintln(w, fmt.Sprintf("* %s: \t%s", task, e.Tasks[task].Desc)) + fmt.Fprintf(w, "* %s: \t%s\n", task.Task, task.Desc) } w.Flush() } -func (e *Executor) tasksWithDesc() (tasks []string) { - for name, task := range e.Tasks { +func (e *Executor) tasksWithDesc() (tasks []*Task) { + tasks = make([]*Task, 0, len(e.Taskfile.Tasks)) + for _, task := range e.Taskfile.Tasks { if task.Desc != "" { - tasks = append(tasks, name) + tasks = append(tasks, task) } } - sort.Strings(tasks) + sort.Slice(tasks, func(i, j int) bool { return tasks[i].Task < tasks[j].Task }) return } diff --git a/status.go b/status.go index 4742bb38..d231fb3f 100644 --- a/status.go +++ b/status.go @@ -11,7 +11,7 @@ import ( // Status returns an error if any the of given tasks is not up-to-date func (e *Executor) Status(calls ...Call) error { for _, call := range calls { - t, ok := e.Tasks[call.Task] + t, ok := e.Taskfile.Tasks[call.Task] if !ok { return &taskNotFoundError{taskName: call.Task} } diff --git a/task.go b/task.go index b13fc714..c4abe999 100644 --- a/task.go +++ b/task.go @@ -23,12 +23,12 @@ const ( // Executor executes a Taskfile type Executor struct { - Tasks Tasks - Dir string - Force bool - Watch bool - Verbose bool - Silent bool + Taskfile *Taskfile + Dir string + Force bool + Watch bool + Verbose bool + Silent bool Context context.Context @@ -78,8 +78,8 @@ func (e *Executor) Run(calls ...Call) error { e.Stderr = os.Stderr } - e.taskCallCount = make(map[string]*int32, len(e.Tasks)) - for k := range e.Tasks { + e.taskCallCount = make(map[string]*int32, len(e.Taskfile.Tasks)) + for k := range e.Taskfile.Tasks { e.taskCallCount[k] = new(int32) } @@ -89,7 +89,7 @@ func (e *Executor) Run(calls ...Call) error { // check if given tasks exist for _, c := range calls { - if _, ok := e.Tasks[c.Task]; !ok { + if _, ok := e.Taskfile.Tasks[c.Task]; !ok { // FIXME: move to the main package e.PrintTasksHelp() return &taskNotFoundError{taskName: c.Task} diff --git a/task_test.go b/task_test.go index 354c6d03..b93de4db 100644 --- a/task_test.go +++ b/task_test.go @@ -347,3 +347,26 @@ func TestCyclicDep(t *testing.T) { assert.NoError(t, e.ReadTaskfile()) assert.IsType(t, &task.MaximumTaskCallExceededError{}, e.Run(task.Call{Task: "task-1"})) } + +func TestTaskVersion(t *testing.T) { + tests := []struct { + Dir string + Version int + }{ + {"testdata/version/v1", 1}, + {"testdata/version/v2", 2}, + } + + for _, test := range tests { + t.Run(test.Dir, func(t *testing.T) { + e := task.Executor{ + Dir: test.Dir, + Stdout: ioutil.Discard, + Stderr: ioutil.Discard, + } + assert.NoError(t, e.ReadTaskfile()) + assert.Equal(t, test.Version, e.Taskfile.Version) + assert.Equal(t, 2, len(e.Taskfile.Tasks)) + }) + } +} diff --git a/taskfile.go b/taskfile.go index 01c553ba..6b8fc3d1 100644 --- a/taskfile.go +++ b/taskfile.go @@ -10,12 +10,37 @@ import ( "gopkg.in/yaml.v2" ) +// Taskfile represents a Taskfile.yml +type Taskfile struct { + // TODO: version is still not used + Version int + Tasks Tasks +} + +// UnmarshalYAML implements yaml.Unmarshaler interface +func (tf *Taskfile) UnmarshalYAML(unmarshal func(interface{}) error) error { + if err := unmarshal(&tf.Tasks); err == nil { + return nil + } + + var taskfile struct { + Version int + Tasks Tasks + } + if err := unmarshal(&taskfile); err != nil { + return err + } + tf.Version = taskfile.Version + tf.Tasks = taskfile.Tasks + return nil +} + // 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(path) + e.Taskfile, err = e.readTaskfileData(path) if err != nil { return err } @@ -27,20 +52,22 @@ func (e *Executor) ReadTaskfile() error { default: return err } + } else { + if err := mergo.MapWithOverwrite(&e.Taskfile.Tasks, osTasks.Tasks); err != nil { + return err + } } - if err := mergo.MapWithOverwrite(&e.Tasks, osTasks); err != nil { - return err - } - for name, task := range e.Tasks { + for name, task := range e.Taskfile.Tasks { task.Task = name } return e.readTaskvars() } -func (e *Executor) readTaskfileData(path string) (tasks map[string]*Task, err error) { +func (e *Executor) readTaskfileData(path string) (*Taskfile, error) { if b, err := ioutil.ReadFile(path + ".yml"); err == nil { - return tasks, yaml.UnmarshalStrict(b, &tasks) + var taskfile Taskfile + return &taskfile, yaml.UnmarshalStrict(b, &taskfile) } return nil, taskFileNotFound{path} } diff --git a/testdata/version/v1/Taskfile.yml b/testdata/version/v1/Taskfile.yml new file mode 100644 index 00000000..5eff2c71 --- /dev/null +++ b/testdata/version/v1/Taskfile.yml @@ -0,0 +1,9 @@ +version: 1 +tasks: + foo: + cmds: + - echo "Foo" + + bar: + cmds: + - echo "Bar" diff --git a/testdata/version/v2/Taskfile.yml b/testdata/version/v2/Taskfile.yml new file mode 100644 index 00000000..8dbdd900 --- /dev/null +++ b/testdata/version/v2/Taskfile.yml @@ -0,0 +1,9 @@ +version: 2 +tasks: + foo: + cmds: + - echo "Foo" + + bar: + cmds: + - echo "Bar" diff --git a/variables.go b/variables.go index d3708678..526a2ed6 100644 --- a/variables.go +++ b/variables.go @@ -92,7 +92,7 @@ func (v *Var) UnmarshalYAML(unmarshal func(interface{}) error) error { // 4. Taskvars variables, resolved with access to: // - environment variables func (e *Executor) getVariables(call Call) (Vars, error) { - t, ok := e.Tasks[call.Task] + t, ok := e.Taskfile.Tasks[call.Task] if !ok { return nil, &taskNotFoundError{call.Task} } @@ -198,7 +198,7 @@ func (e *Executor) handleShVar(v Var) (string, error) { // CompiledTask returns a copy of a task, but replacing variables in almost all // properties using the Go template package. func (e *Executor) CompiledTask(call Call) (*Task, error) { - origTask, ok := e.Tasks[call.Task] + origTask, ok := e.Taskfile.Tasks[call.Task] if !ok { return nil, &taskNotFoundError{call.Task} }