When writing the Taskfile.yml many people probably write it like any other yaml, which means values like false is a boolean, as the yaml spec says: http://yaml.org/type/bool.html
But it turns out to be used as string and when using the template language it's actually "false". This is bad for {{if .SOME_VAR}} for example, which doesn't work, so you have to write {{if eq "true" .SOME_VAR}} for example, or even a much longer expression given the valid forms of a bool in the spec (e.g. "no", "NO", "No", n", ...).
Note: I found the same issue in a different Go repository: https://github.com/concourse/concourse/issues/294
Unfortunately they only link to a Pivotal tracker and the link is 404, so I don't know if and how they dealt with this issue.
If you explicitly don't want to adhere to the yaml spec (and of course there might be good reasons for it), this behaviour should at least be mentioned in the documentation. Currently the "Variables" section doesn't do this: https://taskfile.org/#/usage?id=variables
Example Taskfile, created with task --init and then customized:
# github.com/go-task/task
version: '2'
vars:
GREETING: Hello, World!
IS_LIB: false
tasks:
default:
cmds:
- echo "{{.GREETING}}"
- echo "{{.IS_LIB}}"
- echo "{{if .IS_LIB}} error {{else}} ok {{end}}"
- echo "{{if eq "true" .IS_LIB}} error {{else}} ok {{end}}"
silent: true
The output is:
task: No argument given, trying default task
Hello, World!
false
error
ok
task Version: Commit 309cfb14995ec511546726ab607d53c55c803d0f
OS: Ubuntu 14.04
I have met this limitaruon as well. With some effort, I believe not only Boolean, but also array and numeric values could be supported as static values.
Unfortunantly maps would probably be of the table because the syntax for dynamic variables is:
vars:
myvar:
sh: cat myfile.txt
For our own use cases, we ended up sticking with string values rather than fixing it (we wanted maps and Boolean).
Hi @philippgille,
As said by @smyrman, there's a syntax conflict with the sh: keyword, so implementation wise we need to find a workaround.
That said, I agree this would be very useful.
This is slightly related to #82
Well, there is no conflict for Booleans, numbers and arrays.
vars:
myvar: <string, bool, number or array>
We can expand that with an explicit value key that will be parse into an equivalent of Var.Static, and also allows maps and dollar ($) prefixes in strings:
vars:
myvar:
value: <any yaml value>
Implementation wise, at least these changes must be made:
To use interface{} over string for Static, plausibly rename Static for increased readability?
type Var struct {
Value interface{} `yaml:"value"`
Sh string `yaml:"sh"`
}
To return a map of interface{} values:
func (vs Vars) Resolve() (m map[string]interface{}) {
....
}
interface{} value, and then do a type-switch.$) when the parsed root value is a string.To highlight the last point:
vars:
myvar:
value: "$I am not a command"
vars:
myvar: "$echo 'I am a command'"
From there, there will be lots of follow up changes. If you are interested in doing a PR @philippgille, that would be nice. If not, I suspect it will be fixed eventually.
What's your views @andreynering?
PS! The same should off-course work the same way for the Taskvars.yml file, and for call variables passed to tasks.
@smyrman Yeah, I think that's the right way of doing this. Thanks for the suggestion!
@andreynering I just briefly evaluated this tool as an alternative to make. It looks very promising! Thank you for your effort. What would be a really appreciated feature is to handle variables of type map (or any other complex type). In our case we have a set of variables that should change values depending on if you are deploying to dev, stage, prod etc.
Looking up the values for the current deploy environment in a map would be very neat!
Meanwhile, is there another approach you can use to build up a map like structure as a workaround? Like using a pipe-sign to build up a multi-line string? Are there parsing utilities included that can fetch the values from a text-representation of a map? Perhaps not pretty. Any other suggestion?
Hi @pettervondolwitz,
Indeed I plan to support non-string variables. It's just a matter of finding free time to do so.
For now I don't have any workaround in mind, but maybe if you explain better your use case, with examples, I can help a bit.
@andreynering
A slightly simplified description would be the following. Let's say we want to orchestrate deployment to three different environments. Taskvars.yml could look something like below:
TARGETS:
DEV:
ACCOUNT: 11111111111
AWS_REGION: us-west-1
BASE_FQDN: dev.acme.com
STAGE:
ACCOUNT: 22222222222
AWS_REGION: us-west-2
BASE_FQDN: stage.acme.com
PROD:
ACCOUNT: 33333333333
AWS_REGION: eu-west-1
BASE_FQDN: acme.com
Then in the Taskfile.yml we would like to point out the current target environment somehow. By setting a variable perhaps if using the current capabilities of this project and then extracting the relevant account number by using a go template:
{{index .TARGETS "DEV".ACCOUNT}}
where "DEV" represents the current environment. Not sure the syntax is correct here but something like the above. Applying the same type of logic to different environments when deploying should be a fairly common use case. The yaml-file above needs to be more complex in our case, i.e. including additional maps/arrays at a deeper level but if a generic support is added for any complex yaml-variable then this would not present a problem I guess.
Here are two workarounds you could use (using only one variable in the examples to keep it short):
In Taskvars.yml:
TARGET__DEV__ACCOUNT: "11111111111"
TARGET__STAGE__ACCOUNT: "22222222222"
TARGET__PROD__ACCOUNT: "22222222222"
In Taskfile.yml:
version: "2"
tasks:
deploy:
desc: "deply all targets"
deps:
- deploy-dev
- deploy-stage
- deploy-prod
deploy-dev:
desc: "deply dev target"
cmds:
- task: _deploy
vars:
ACCOUNT: '{{.TARGET__DEV__ACCUOUNT}}'
deploy-stage:
desc: "deply stage target"
cmds:
- task: _deploy
vars:
ACCOUNT: '{{.TARGET__STAGE__ACCUOUNT}}'
deploy-prod:
desc: "deply prod target"
cmds:
- task: _deploy
vars:
ACCOUNT: '{{.TARGET__PROD__ACCUOUNT}}'
_deploy:
cmds:
- 'command {{.ACCOUNT}}'
Alternatively, you could use a folder structure:
deploy/stagedeploy/proddeploy/devLet each of them include the same _deloy task from a "lib" directory, and have their own Taskvars.yml:
ACCOUNT: "11111111111"
@smyrman Thank you for your suggestions! I think flatten out as you suggest in the first example would be to messy since the real use case is much more involved with maps in maps etc. I will see if we can use your second approach though. Also the first example was a good inspiration on how you can go about similar tasks.
I was planning a task that created a series of web site base files in a directory per site and was expecting to use a YAML dict object for each site.
From this discussion it seems the best way for now would be to have a sitename1/Taskvars.yml, sitename2/Taskvars.yml style folder structure and a generic 'site' task that reads those?
Yes, I belie for now, that's a feasible solution. Otherwise it's attempting a PR at the suggested solution above: https://github.com/go-task/task/issues/140#issuecomment-430524784
Most helpful comment
Well, there is no conflict for Booleans, numbers and arrays.
We can expand that with an explicit
valuekey that will be parse into an equivalent ofVar.Static, and also allows maps and dollar ($) prefixes in strings:Implementation wise, at least these changes must be made:
Change the Var type
https://github.com/go-task/task/blob/309cfb14995ec511546726ab607d53c55c803d0f/internal/taskfile/var.go#L32
To use
interface{}overstringforStatic, plausibly renameStaticfor increased readability?Replace ToStringMap
https://github.com/go-task/task/blob/309cfb14995ec511546726ab607d53c55c803d0f/internal/taskfile/var.go#L18
To return a map of
interface{}values:Change UnmarshalYAML
https://github.com/go-task/task/blob/309cfb14995ec511546726ab607d53c55c803d0f/internal/taskfile/var.go#L38
interface{}value, and then do a type-switch.$) when the parsed root value is a string.To highlight the last point:
From there, there will be lots of follow up changes. If you are interested in doing a PR @philippgille, that would be nice. If not, I suspect it will be fixed eventually.
What's your views @andreynering?