From 240589978dda4b9bfb3cf09288840aadeeda4ee3 Mon Sep 17 00:00:00 2001 From: Sascha Andres Date: Thu, 2 Mar 2017 10:46:20 +0100 Subject: [PATCH 01/12] Variable handling Relates to #2 --- task.go | 7 +++++-- variableHandling.go | 45 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 2 deletions(-) create mode 100644 variableHandling.go diff --git a/task.go b/task.go index e0eab8db..95e17b62 100644 --- a/task.go +++ b/task.go @@ -43,6 +43,7 @@ type Task struct { Sources []string Generates []string Dir string + Variables map[string]string } type taskNotFoundError struct { @@ -101,14 +102,16 @@ func RunTask(name string) error { return nil } + vars := t.handleVariables() + for _, d := range t.Deps { - if err := RunTask(d); err != nil { + if err := RunTask(ReplaceVariables(d, vars)); err != nil { return err } } for _, c := range t.Cmds { - if err := runCommand(c, t.Dir); err != nil { + if err := runCommand(ReplaceVariables(c, vars), ReplaceVariables(t.Dir, vars)); err != nil { return &taskRunError{name, err} } } diff --git a/variableHandling.go b/variableHandling.go new file mode 100644 index 00000000..e9d46dd9 --- /dev/null +++ b/variableHandling.go @@ -0,0 +1,45 @@ +package task + +import ( + "fmt" + "os" + "strings" +) + +func (t Task) handleVariables() map[string]string { + localVariables := make(map[string]string) + for key, value := range t.Variables { + localVariables[key] = value + } + for key, value := range getEnvironmentVariables() { + localVariables[key] = value + } + return localVariables +} + +// ReplaceVariables writes variables into initial string +func ReplaceVariables(initial string, variables map[string]string) string { + replaced := initial + for name, val := range variables { + replaced = strings.Replace(replaced, fmt.Sprintf("{{%s}}", name), val, -1) + } + return replaced +} + +// GetEnvironmentVariables returns environment variables as map +func getEnvironmentVariables() map[string]string { + getenvironment := func(data []string, getkeyval func(item string) (key, val string)) map[string]string { + items := make(map[string]string) + for _, item := range data { + key, val := getkeyval(item) + items[key] = val + } + return items + } + return getenvironment(os.Environ(), func(item string) (key, val string) { + splits := strings.Split(item, "=") + key = splits[0] + val = splits[1] + return + }) +} From 0162c2990e14b5fc2c1fcf4e3efc19cd9564ee93 Mon Sep 17 00:00:00 2001 From: Sascha Andres Date: Thu, 2 Mar 2017 11:28:34 +0100 Subject: [PATCH 02/12] Added external file support Relates to #2 --- variableHandling.go | 41 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/variableHandling.go b/variableHandling.go index e9d46dd9..f0ef7d74 100644 --- a/variableHandling.go +++ b/variableHandling.go @@ -1,20 +1,37 @@ package task import ( + "encoding/json" "fmt" + "io/ioutil" "os" "strings" + + "github.com/BurntSushi/toml" + yaml "gopkg.in/yaml.v2" ) -func (t Task) handleVariables() map[string]string { +var ( + // VariableFilePath file containing additional variables + VariableFilePath = "Variables" +) + +func (t Task) handleVariables() (map[string]string, error) { localVariables := make(map[string]string) for key, value := range t.Variables { localVariables[key] = value } + if fileVariables, err := readVariablefile(); err == nil { + for key, value := range fileVariables { + localVariables[key] = value + } + } else { + return nil, err + } for key, value := range getEnvironmentVariables() { localVariables[key] = value } - return localVariables + return localVariables, nil } // ReplaceVariables writes variables into initial string @@ -43,3 +60,23 @@ func getEnvironmentVariables() map[string]string { return }) } + +func readVariablefile() (map[string]string, error) { + var variables map[string]string + if b, err := ioutil.ReadFile(VariableFilePath + ".yml"); err == nil { + if err := yaml.Unmarshal(b, &variables); err != nil { + return nil, err + } + } + if b, err := ioutil.ReadFile(VariableFilePath + ".json"); err == nil { + if err := json.Unmarshal(b, &variables); err != nil { + return nil, err + } + } + if b, err := ioutil.ReadFile(VariableFilePath + ".toml"); err == nil { + if err := toml.Unmarshal(b, &variables); err != nil { + return nil, err + } + } + return variables, nil +} From b9820c5c7ddd09d77e4ed3a0138d3b0de8409a01 Mon Sep 17 00:00:00 2001 From: Sascha Andres Date: Thu, 2 Mar 2017 11:30:59 +0100 Subject: [PATCH 03/12] Handling errors on variable handling Relates to #2 --- task.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/task.go b/task.go index 95e17b62..13215588 100644 --- a/task.go +++ b/task.go @@ -102,7 +102,10 @@ func RunTask(name string) error { return nil } - vars := t.handleVariables() + vars, err := t.handleVariables() + if err != nil { + return &taskRunError{name, err} + } for _, d := range t.Deps { if err := RunTask(ReplaceVariables(d, vars)); err != nil { From eb783d04b89c2bff47398fad06e1bf0818139f83 Mon Sep 17 00:00:00 2001 From: Sascha Andres Date: Thu, 2 Mar 2017 20:19:25 +0100 Subject: [PATCH 04/12] Allow setting a variable --- task.go | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/task.go b/task.go index 13215588..62251524 100644 --- a/task.go +++ b/task.go @@ -44,6 +44,7 @@ type Task struct { Generates []string Dir string Variables map[string]string + Set string } type taskNotFoundError struct { @@ -101,22 +102,31 @@ func RunTask(name string) error { log.Printf(`Task "%s" is up to date`, name) return nil } - vars, err := t.handleVariables() if err != nil { return &taskRunError{name, err} } - for _, d := range t.Deps { if err := RunTask(ReplaceVariables(d, vars)); err != nil { return err } } - + vars, err = t.handleVariables() // read in a second time, as a dependency could have set a new env variable + if err != nil { + return &taskRunError{name, err} + } for _, c := range t.Cmds { - if err := runCommand(ReplaceVariables(c, vars), ReplaceVariables(t.Dir, vars)); err != nil { + var ( + output string + err error + ) + if output, err = runCommand(ReplaceVariables(c, vars), ReplaceVariables(t.Dir, vars)); err != nil { return &taskRunError{name, err} } + fmt.Println(output) + if t.Set != "" { + os.Setenv(t.Set, output) + } } return nil } @@ -139,8 +149,12 @@ func isTaskUpToDate(t *Task) bool { return generatesMinTime.After(sourcesMaxTime) } -func runCommand(c, path string) error { - var cmd *exec.Cmd +func runCommand(c, path string) (string, error) { + var ( + cmd *exec.Cmd + b []byte + err error + ) if ShExists { cmd = exec.Command(ShPath, "-c", c) } else { @@ -149,12 +163,11 @@ func runCommand(c, path string) error { if path != "" { cmd.Dir = path } - cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr - if err := cmd.Run(); err != nil { - return err + if b, err = cmd.Output(); err != nil { + return "", err } - return nil + return string(b), nil } func readTaskfile() (tasks map[string]*Task, err error) { From 8619c8d41701f61e366d8f5871c7bc59004f8287 Mon Sep 17 00:00:00 2001 From: Sascha Andres Date: Thu, 2 Mar 2017 20:27:26 +0100 Subject: [PATCH 05/12] Added documentation for variables Closes #2 --- README.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/README.md b/README.md index 9f78723e..50f18647 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,30 @@ task assets build If Bash is available (Linux and Windows while on Git Bash), the commands will run in Bash, otherwise will fallback to `cmd` (on Windows). +### Variables + +```yml +build: + deps: [setvar] + cmds: + - echo "{{prefix}} {{THEVAR}}" + variables: + prefix: "Path:" + +setvar: + cmds: + - echo "{{PATH}}" + set: THEVAR +``` + +The above sample saves the path into a new variable which is then again echoed. + +You can use environment variables, task level variables and a file called `Variables` as source of variables. + +They are evaluated in the following order: + +Task local variables are overwritten by variables found in `Variables`. Variables found in `Variables` are overwritten with variables from the environment. + ### Task dependencies You may have tasks that depends on others. Just pointing them on `deps` will From 9abe71e9670fe6cd3c22b3a8620ee3db4a2e7b61 Mon Sep 17 00:00:00 2001 From: Sascha Andres Date: Thu, 2 Mar 2017 20:34:09 +0100 Subject: [PATCH 06/12] Moved handleVariables() into command loop --- task.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/task.go b/task.go index 62251524..32ea2818 100644 --- a/task.go +++ b/task.go @@ -111,11 +111,12 @@ func RunTask(name string) error { return err } } - vars, err = t.handleVariables() // read in a second time, as a dependency could have set a new env variable - if err != nil { - return &taskRunError{name, err} - } for _, c := range t.Cmds { + // read in a each time, as a command could change a variable or it has been changed by a dependency + vars, err = t.handleVariables() + if err != nil { + return &taskRunError{name, err} + } var ( output string err error From 6733ef458f22d7db95b651b439551c2e8d4a8fda Mon Sep 17 00:00:00 2001 From: Sascha Andres Date: Thu, 2 Mar 2017 20:34:35 +0100 Subject: [PATCH 07/12] Explained usage of set with multiple commands --- README.md | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 50f18647..4c516a41 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,32 @@ You can use environment variables, task level variables and a file called `Varia They are evaluated in the following order: -Task local variables are overwritten by variables found in `Variables`. Variables found in `Variables` are overwritten with variables from the environment. +Task local variables are overwritten by variables found in `Variables`. Variables found in `Variables` are overwritten with variables from the environment. The output of the last command is stored in the environment. So you can do something like this: + +```yml +build: + deps: [setvar] + cmds: + - echo "{{prefix}} '{{THEVAR}}'" + variables: + prefix: "Result: " + +setvar: + cmds: + - echo -n "a" + - echo -n "{{THEVAR}}b" + - echo -n "{{THEVAR}}c" + set: THEVAR +``` + +The result of a run of build would be: + +``` +a +ab +abc +Result: 'abc' +``` ### Task dependencies From 4ba13e4e8ad146a464ea5c4e4ed68caea2997070 Mon Sep 17 00:00:00 2001 From: Sascha Andres Date: Fri, 3 Mar 2017 09:00:01 +0100 Subject: [PATCH 08/12] Updated README --- README.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/README.md b/README.md index 702e4285..0ad08969 100644 --- a/README.md +++ b/README.md @@ -88,6 +88,19 @@ abc Result: 'abc' ``` +### Running task in another dir + +By default, tasks will be executed in the directory where the Taskfile is +located. But you can easily make the task run in another folder informing +`dir`: + +```yml +js: + dir: www/public/js + cmds: + - gulp +``` + ### Task dependencies You may have tasks that depends on others. Just pointing them on `deps` will From e6bb0cfc6d2ce5770df2f4f1df6e8c431616da05 Mon Sep 17 00:00:00 2001 From: Sascha Andres Date: Sun, 5 Mar 2017 10:07:08 +0100 Subject: [PATCH 09/12] Changed to be quiet when a variable is set --- task.go | 3 ++- variableHandling.go => variable_handling.go | 0 2 files changed, 2 insertions(+), 1 deletion(-) rename variableHandling.go => variable_handling.go (100%) diff --git a/task.go b/task.go index 06c5bf18..7e2ade46 100644 --- a/task.go +++ b/task.go @@ -111,9 +111,10 @@ func RunTask(name string) error { if output, err = runCommand(ReplaceVariables(c, vars), ReplaceVariables(t.Dir, vars)); err != nil { return &taskRunError{name, err} } - fmt.Println(output) if t.Set != "" { os.Setenv(t.Set, output) + } else { + fmt.Println(output) } } return nil diff --git a/variableHandling.go b/variable_handling.go similarity index 100% rename from variableHandling.go rename to variable_handling.go From d8f9b0697d584f293d0ce052cf1f33a87aa90969 Mon Sep 17 00:00:00 2001 From: Sascha Andres Date: Sun, 5 Mar 2017 10:08:24 +0100 Subject: [PATCH 10/12] Removed aliasing --- variable_handling.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/variable_handling.go b/variable_handling.go index f0ef7d74..b504154a 100644 --- a/variable_handling.go +++ b/variable_handling.go @@ -8,7 +8,7 @@ import ( "strings" "github.com/BurntSushi/toml" - yaml "gopkg.in/yaml.v2" + "gopkg.in/yaml.v2" ) var ( From 561c213a92ce6fb510e6c31a9e20c49d40dabf37 Mon Sep 17 00:00:00 2001 From: Sascha Andres Date: Sun, 5 Mar 2017 10:15:49 +0100 Subject: [PATCH 11/12] Running deps first --- task.go | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/task.go b/task.go index 7e2ade46..09a15fbd 100644 --- a/task.go +++ b/task.go @@ -2,6 +2,7 @@ package task import ( "encoding/json" + "fmt" "io/ioutil" "log" "os" @@ -83,21 +84,22 @@ func RunTask(name string) error { return &taskNotFoundError{name} } - if !Force && isTaskUpToDate(t) { - log.Printf(`task: Task "%s" is up to date`, name) - return nil - } vars, err := t.handleVariables() if err != nil { return &taskRunError{name, err} } - + for _, d := range t.Deps { if err := RunTask(ReplaceVariables(d, vars)); err != nil { return err } } - + + if !Force && isTaskUpToDate(t) { + log.Printf(`task: Task "%s" is up to date`, name) + return nil + } + for _, c := range t.Cmds { // read in a each time, as a command could change a variable or it has been changed by a dependency vars, err = t.handleVariables() From 9bffad0f272c3c3ac6bd5927fbb335ec5e8bd408 Mon Sep 17 00:00:00 2001 From: Sascha Andres Date: Sun, 5 Mar 2017 10:20:16 +0100 Subject: [PATCH 12/12] Changed name of Variables to Taskvars --- variable_handling.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/variable_handling.go b/variable_handling.go index b504154a..651dd9ed 100644 --- a/variable_handling.go +++ b/variable_handling.go @@ -13,7 +13,7 @@ import ( var ( // VariableFilePath file containing additional variables - VariableFilePath = "Variables" + VariableFilePath = "Taskvars" ) func (t Task) handleVariables() (map[string]string, error) {