1
0
mirror of https://github.com/go-task/task.git synced 2024-12-04 10:24:45 +02:00

allow assigning variables to tasks at run time via CLI

using a similar syntax than setting env variables to command in bash,
but used right after the task:

```bash
task print MESSAGE=Hello!
```

closes #33
This commit is contained in:
Andrey Nering 2017-09-07 13:57:06 -03:00
parent 5e72de4ba2
commit f3097845b4
8 changed files with 159 additions and 35 deletions

View File

@ -279,6 +279,15 @@ Example of overriding with environment variables:
$ TASK_VARIABLE=a-value task do-something
```
Since some shells don't support above syntax to set environment variables
(Windows) tasks also accepts a similar style when not in the beginning of
the command. Variables given in this form are only visible to the task called
right before.
```bash
$ task write-file FILE=file.txt "CONTENT=Hello, World!" print "MESSAGE=All done!"
```
Example of locally declared vars:
```yml

View File

@ -34,6 +34,7 @@ test:
desc: Runs test suite
deps: [install]
cmds:
- go test ./args
- go test
# https://github.com/goreleaser/goreleaser

34
args/args.go Normal file
View File

@ -0,0 +1,34 @@
package args
import (
"errors"
"strings"
"github.com/go-task/task"
)
var (
ErrVariableWithoutTask = errors.New("task: variable given before any task")
)
func Parse(args ...string) ([]task.Call, error) {
var calls []task.Call
for _, arg := range args {
if !strings.Contains(arg, "=") {
calls = append(calls, task.Call{Task: arg})
continue
}
if len(calls) < 1 {
return nil, ErrVariableWithoutTask
}
if calls[len(calls)-1].Vars == nil {
calls[len(calls)-1].Vars = make(task.Vars)
}
pair := strings.SplitN(arg, "=", 2)
calls[len(calls)-1].Vars[pair[0]] = task.Var{Static: pair[1]}
}
return calls, nil
}

70
args/args_test.go Normal file
View File

@ -0,0 +1,70 @@
package args_test
import (
"fmt"
"testing"
"github.com/go-task/task"
"github.com/go-task/task/args"
"github.com/stretchr/testify/assert"
)
func TestArgs(t *testing.T) {
tests := []struct {
Args []string
Expected []task.Call
Err error
}{
{
Args: []string{"task-a", "task-b", "task-c"},
Expected: []task.Call{
{Task: "task-a"},
{Task: "task-b"},
{Task: "task-c"},
},
},
{
Args: []string{"task-a", "FOO=bar", "task-b", "task-c", "BAR=baz", "BAZ=foo"},
Expected: []task.Call{
{
Task: "task-a",
Vars: task.Vars{
"FOO": task.Var{Static: "bar"},
},
},
{Task: "task-b"},
{
Task: "task-c",
Vars: task.Vars{
"BAR": task.Var{Static: "baz"},
"BAZ": task.Var{Static: "foo"},
},
},
},
},
{
Args: []string{"task-a", "CONTENT=with some spaces"},
Expected: []task.Call{
{
Task: "task-a",
Vars: task.Vars{
"CONTENT": task.Var{Static: "with some spaces"},
},
},
},
},
{
Args: []string{"FOO=bar", "task-a"},
Err: args.ErrVariableWithoutTask,
},
}
for i, test := range tests {
t.Run(fmt.Sprintf("TestArgs%d", i+1), func(t *testing.T) {
calls, err := args.Parse(test.Args...)
assert.Equal(t, test.Err, err)
assert.Equal(t, test.Expected, calls)
})
}
}

View File

@ -6,6 +6,7 @@ import (
"os"
"github.com/go-task/task"
"github.com/go-task/task/args"
"github.com/spf13/pflag"
)
@ -99,13 +100,18 @@ func main() {
return
}
args := pflag.Args()
if len(args) == 0 {
arguments := pflag.Args()
if len(arguments) == 0 {
log.Println("task: No argument given, trying default task")
args = []string{"default"}
arguments = []string{"default"}
}
if err := e.Run(args...); err != nil {
calls, err := args.Parse(arguments...)
if err != nil {
log.Fatal(err)
}
if err := e.Run(calls...); err != nil {
log.Fatal(err)
}
}

14
task.go
View File

@ -63,7 +63,7 @@ type Task struct {
}
// Run runs Task
func (e *Executor) Run(args ...string) error {
func (e *Executor) Run(calls ...Call) error {
if e.Stdin == nil {
e.Stdin = os.Stdin
}
@ -84,23 +84,23 @@ func (e *Executor) Run(args ...string) error {
}
// check if given tasks exist
for _, a := range args {
if _, ok := e.Tasks[a]; !ok {
for _, c := range calls {
if _, ok := e.Tasks[c.Task]; !ok {
// FIXME: move to the main package
e.PrintTasksHelp()
return &taskNotFoundError{taskName: a}
return &taskNotFoundError{taskName: c.Task}
}
}
if e.Watch {
if err := e.watchTasks(args...); err != nil {
if err := e.watchTasks(calls...); err != nil {
return err
}
return nil
}
for _, a := range args {
if err := e.RunTask(context.Background(), Call{Task: a, Vars: nil}); err != nil {
for _, c := range calls {
if err := e.RunTask(context.TODO(), c); err != nil {
return err
}
}

View File

@ -38,7 +38,7 @@ func (fct fileContentTest) Run(t *testing.T) {
Stderr: ioutil.Discard,
}
assert.NoError(t, e.ReadTaskfile(), "e.ReadTaskfile()")
assert.NoError(t, e.Run(fct.Target), "e.Run(target)")
assert.NoError(t, e.Run(task.Call{Task: fct.Target}), "e.Run(target)")
for name, expectContent := range fct.Files {
t.Run(fct.name(name), func(t *testing.T) {
@ -137,7 +137,7 @@ func TestVarsInvalidTmpl(t *testing.T) {
Stderr: ioutil.Discard,
}
assert.NoError(t, e.ReadTaskfile(), "e.ReadTaskfile()")
assert.EqualError(t, e.Run(target), expectError, "e.Run(target)")
assert.EqualError(t, e.Run(task.Call{Task: target}), expectError, "e.Run(target)")
}
func TestParams(t *testing.T) {
@ -189,7 +189,7 @@ func TestDeps(t *testing.T) {
Stderr: ioutil.Discard,
}
assert.NoError(t, e.ReadTaskfile())
assert.NoError(t, e.Run("default"))
assert.NoError(t, e.Run(task.Call{Task: "default"}))
for _, f := range files {
f = filepath.Join(dir, f)
@ -217,7 +217,7 @@ func TestTaskCall(t *testing.T) {
Stderr: ioutil.Discard,
}
assert.NoError(t, e.ReadTaskfile())
assert.NoError(t, e.Run("default"))
assert.NoError(t, e.Run(task.Call{Task: "default"}))
for _, f := range files {
if _, err := os.Stat(filepath.Join(dir, f)); err != nil {
@ -242,7 +242,7 @@ func TestStatus(t *testing.T) {
Stderr: ioutil.Discard,
}
assert.NoError(t, e.ReadTaskfile())
assert.NoError(t, e.Run("gen-foo"))
assert.NoError(t, e.Run(task.Call{Task: "gen-foo"}))
if _, err := os.Stat(file); err != nil {
t.Errorf("File should exists: %v", err)
@ -250,7 +250,7 @@ func TestStatus(t *testing.T) {
buff := bytes.NewBuffer(nil)
e.Stdout, e.Stderr = buff, buff
assert.NoError(t, e.Run("gen-foo"))
assert.NoError(t, e.Run(task.Call{Task: "gen-foo"}))
if buff.String() != `task: Task "gen-foo" is up to date`+"\n" {
t.Errorf("Wrong output message: %s", buff.String())
@ -283,13 +283,13 @@ func TestGenerates(t *testing.T) {
}
assert.NoError(t, e.ReadTaskfile())
for _, task := range []string{relTask, absTask} {
var destFile = filepath.Join(dir, task)
for _, theTask := range []string{relTask, absTask} {
var destFile = filepath.Join(dir, theTask)
var upToDate = fmt.Sprintf("task: Task \"%s\" is up to date\n", srcTask) +
fmt.Sprintf("task: Task \"%s\" is up to date\n", task)
fmt.Sprintf("task: Task \"%s\" is up to date\n", theTask)
// Run task for the first time.
assert.NoError(t, e.Run(task))
assert.NoError(t, e.Run(task.Call{Task: theTask}))
if _, err := os.Stat(srcFile); err != nil {
t.Errorf("File should exists: %v", err)
@ -304,7 +304,7 @@ func TestGenerates(t *testing.T) {
buff.Reset()
// Re-run task to ensure it's now found to be up-to-date.
assert.NoError(t, e.Run(task))
assert.NoError(t, e.Run(task.Call{Task: theTask}))
if buff.String() != upToDate {
t.Errorf("Wrong output message: %s", buff.String())
}
@ -339,5 +339,5 @@ func TestCyclicDep(t *testing.T) {
Stderr: ioutil.Discard,
}
assert.NoError(t, e.ReadTaskfile())
assert.IsType(t, &task.MaximumTaskCallExceededError{}, e.Run("task-1"))
assert.IsType(t, &task.MaximumTaskCallExceededError{}, e.Run(task.Call{Task: "task-1"}))
}

View File

@ -15,14 +15,18 @@ var watchIgnoredDirs = []string{
}
// watchTasks start watching the given tasks
func (e *Executor) watchTasks(args ...string) error {
e.printfln("task: Started watching for tasks: %s", strings.Join(args, ", "))
func (e *Executor) watchTasks(calls ...Call) error {
tasks := make([]string, len(calls))
for i, c := range calls {
tasks[i] = c.Task
}
e.printfln("task: Started watching for tasks: %s", strings.Join(tasks, ", "))
ctx, cancel := context.WithCancel(context.Background())
for _, a := range args {
a := a
for _, c := range calls {
c := c
go func() {
if err := e.RunTask(ctx, Call{Task: a}); err != nil && !isContextError(err) {
if err := e.RunTask(ctx, c); err != nil && !isContextError(err) {
e.println(err)
}
}()
@ -43,10 +47,10 @@ func (e *Executor) watchTasks(args ...string) error {
cancel()
ctx, cancel = context.WithCancel(context.Background())
for _, a := range args {
a := a
for _, c := range calls {
c := c
go func() {
if err := e.RunTask(ctx, Call{Task: a}); err != nil && !isContextError(err) {
if err := e.RunTask(ctx, c); err != nil && !isContextError(err) {
e.println(err)
}
}()
@ -69,7 +73,7 @@ func (e *Executor) watchTasks(args ...string) error {
go func() {
// re-register each second because we can have new files
for {
if err := e.registerWatchedFiles(w, args); err != nil {
if err := e.registerWatchedFiles(w, tasks); err != nil {
e.println(err)
}
time.Sleep(time.Second)
@ -79,7 +83,7 @@ func (e *Executor) watchTasks(args ...string) error {
return w.Start(time.Second)
}
func (e *Executor) registerWatchedFiles(w *watcher.Watcher, args []string) error {
func (e *Executor) registerWatchedFiles(w *watcher.Watcher, tasks []string) error {
oldWatchedFiles := make(map[string]struct{})
for f := range w.WatchedFiles() {
oldWatchedFiles[f] = struct{}{}
@ -121,8 +125,8 @@ func (e *Executor) registerWatchedFiles(w *watcher.Watcher, args []string) error
return nil
}
for _, a := range args {
if err := registerTaskFiles(a); err != nil {
for _, t := range tasks {
if err := registerTaskFiles(t); err != nil {
return err
}
}