From 240589978dda4b9bfb3cf09288840aadeeda4ee3 Mon Sep 17 00:00:00 2001 From: Sascha Andres Date: Thu, 2 Mar 2017 10:46:20 +0100 Subject: [PATCH 1/7] 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 2/7] 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 3/7] 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 4/7] 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 5/7] 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 6/7] 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 7/7] 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