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:
parent
5e72de4ba2
commit
f3097845b4
@ -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
|
||||
|
@ -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
34
args/args.go
Normal 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
70
args/args_test.go
Normal 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)
|
||||
})
|
||||
}
|
||||
}
|
@ -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
14
task.go
@ -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
|
||||
}
|
||||
}
|
||||
|
24
task_test.go
24
task_test.go
@ -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"}))
|
||||
}
|
||||
|
28
watch.go
28
watch.go
@ -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
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user