-
-
Notifications
You must be signed in to change notification settings - Fork 747
Variable handling #6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
b325b50
2405899
0162c29
b9820c5
eb783d0
8619c8d
9abe71e
6733ef4
b5fff68
4ba13e4
a918125
e6bb0cf
d8f9b06
561c213
9bffad0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -42,6 +42,55 @@ 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. 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}}'" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn't this be a choice of the user instead of the tool? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is, user can call it the name they want. But I changed to uppercase as a documentation convention. |
||
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' | ||
``` | ||
|
||
### Running task in another dir | ||
|
||
By default, tasks will be executed in the directory where the Taskfile is | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,7 @@ package task | |
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
"io/ioutil" | ||
"log" | ||
"os" | ||
|
@@ -45,6 +46,8 @@ type Task struct { | |
Sources []string | ||
Generates []string | ||
Dir string | ||
Variables map[string]string | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I changed this to |
||
Set string | ||
} | ||
|
||
// Run runs Task | ||
|
@@ -81,8 +84,13 @@ func RunTask(name string) error { | |
return &taskNotFoundError{name} | ||
} | ||
|
||
vars, err := t.handleVariables() | ||
if err != nil { | ||
return &taskRunError{name, err} | ||
} | ||
|
||
for _, d := range t.Deps { | ||
if err := RunTask(d); err != nil { | ||
if err := RunTask(ReplaceVariables(d, vars)); err != nil { | ||
return err | ||
} | ||
} | ||
|
@@ -93,9 +101,23 @@ func RunTask(name string) error { | |
} | ||
|
||
for _, c := range t.Cmds { | ||
if err := runCommand(c, t.Dir); err != nil { | ||
// 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 | ||
) | ||
if output, err = runCommand(ReplaceVariables(c, vars), ReplaceVariables(t.Dir, vars)); err != nil { | ||
return &taskRunError{name, err} | ||
} | ||
if t.Set != "" { | ||
os.Setenv(t.Set, output) | ||
} else { | ||
fmt.Println(output) | ||
} | ||
} | ||
return nil | ||
} | ||
|
@@ -118,8 +140,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 { | ||
|
@@ -128,12 +154,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) { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
package task | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
"io/ioutil" | ||
"os" | ||
"strings" | ||
|
||
"github.com/BurntSushi/toml" | ||
"gopkg.in/yaml.v2" | ||
) | ||
|
||
var ( | ||
// VariableFilePath file containing additional variables | ||
VariableFilePath = "Taskvars" | ||
) | ||
|
||
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, nil | ||
} | ||
|
||
// 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 | ||
}) | ||
} | ||
|
||
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 | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
setvar
instead ofset
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
set
is the property with the variable name. I searched for a valid name of the most simplistic task to set a variable.