diff --git a/internal/experiments/experiments.go b/internal/experiments/experiments.go
index c185052e..bcd37236 100644
--- a/internal/experiments/experiments.go
+++ b/internal/experiments/experiments.go
@@ -27,14 +27,14 @@ type Experiment struct {
var (
GentleForce Experiment
RemoteTaskfiles Experiment
- AnyVariables Experiment
+ MapVariables Experiment
)
func init() {
readDotEnv()
GentleForce = New("GENTLE_FORCE")
RemoteTaskfiles = New("REMOTE_TASKFILES")
- AnyVariables = New("ANY_VARIABLES", "1", "2")
+ MapVariables = New("MAP_VARIABLES", "1", "2")
}
func New(xName string, enabledValues ...string) Experiment {
@@ -101,6 +101,6 @@ func List(l *logger.Logger) error {
w := tabwriter.NewWriter(os.Stdout, 0, 8, 0, ' ', 0)
printExperiment(w, l, GentleForce)
printExperiment(w, l, RemoteTaskfiles)
- printExperiment(w, l, AnyVariables)
+ printExperiment(w, l, MapVariables)
return w.Flush()
}
diff --git a/taskfile/ast/var.go b/taskfile/ast/var.go
index 5ce8fd6d..7ec0a44b 100644
--- a/taskfile/ast/var.go
+++ b/taskfile/ast/var.go
@@ -83,10 +83,10 @@ type Var struct {
}
func (v *Var) UnmarshalYAML(node *yaml.Node) error {
- if experiments.AnyVariables.Enabled {
+ if experiments.MapVariables.Enabled {
// This implementation is not backwards-compatible and replaces the 'sh' key with map variables
- if experiments.AnyVariables.Value == "1" {
+ if experiments.MapVariables.Value == "1" {
var value any
if err := node.Decode(&value); err != nil {
return err
@@ -103,7 +103,7 @@ func (v *Var) UnmarshalYAML(node *yaml.Node) error {
}
// This implementation IS backwards-compatible and keeps the 'sh' key and allows map variables to be added under the `map` key
- if experiments.AnyVariables.Value == "2" {
+ if experiments.MapVariables.Value == "2" {
switch node.Kind {
case yaml.MappingNode:
key := node.Content[0].Value
@@ -141,15 +141,10 @@ func (v *Var) UnmarshalYAML(node *yaml.Node) error {
switch node.Kind {
- case yaml.ScalarNode:
- var str string
- if err := node.Decode(&str); err != nil {
- return err
- }
- v.Value = str
- return nil
-
case yaml.MappingNode:
+ if len(node.Content) > 2 || node.Content[0].Value != "sh" {
+ return fmt.Errorf(`task: line %d: maps cannot be assigned to variables`, node.Line)
+ }
var sh struct {
Sh string
}
@@ -158,7 +153,13 @@ func (v *Var) UnmarshalYAML(node *yaml.Node) error {
}
v.Sh = sh.Sh
return nil
- }
- return fmt.Errorf("yaml: line %d: cannot unmarshal %s into variable", node.Line, node.ShortTag())
+ default:
+ var value any
+ if err := node.Decode(&value); err != nil {
+ return err
+ }
+ v.Value = value
+ return nil
+ }
}
diff --git a/website/blog/2023-09-02-introducing-experiments.md b/website/blog/2023-09-02-introducing-experiments.mdx
similarity index 98%
rename from website/blog/2023-09-02-introducing-experiments.md
rename to website/blog/2023-09-02-introducing-experiments.mdx
index b7bccdf5..bcd7279e 100644
--- a/website/blog/2023-09-02-introducing-experiments.md
+++ b/website/blog/2023-09-02-introducing-experiments.mdx
@@ -16,7 +16,7 @@ communicate these kinds of thoughts to the community. So, with that in mind,
this is the first (hopefully of many) blog posts talking about Task and what
we're up to.
-
+{/* truncate */}
## :calendar: So, what have we been up to?
@@ -122,7 +122,7 @@ I plan to write more of these blog posts in the future on a variety of
Task-related topics, so make sure to check in occasionally and see what we're up
to!
-
+{/* prettier-ignore-start */}
[vscode-task]: https://github.com/go-task/vscode-task
[crowdin]: https://crowdin.com
[contributors]: https://github.com/go-task/task/graphs/contributors
@@ -139,4 +139,4 @@ to!
[experiments-project]: https://github.com/orgs/go-task/projects/1
[gentle-force-experiment]: https://github.com/go-task/task/issues/1200
[remote-taskfiles-experiment]: https://github.com/go-task/task/issues/1317
-
+{/* prettier-ignore-end */}
diff --git a/website/blog/2024-04-09-variables.mdx b/website/blog/2024-04-09-variables.mdx
new file mode 100644
index 00000000..b2a829e7
--- /dev/null
+++ b/website/blog/2024-04-09-variables.mdx
@@ -0,0 +1,182 @@
+---
+title: Any Variables
+description: Task variables are no longer limited to strings!
+slug: any-variables
+authors: [pd93]
+tags: [experiments, variables]
+image: https://i.imgur.com/mErPwqL.png
+hide_table_of_contents: false
+draft: true
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+Task has always had variables, but even though you were able to define them
+using different YAML types, they would always be converted to strings by Task.
+This limited users to string manipulation and encouraged messy workarounds for
+simple problems. Starting from [v3.36.0][v3.36.0], this is no longer the case!
+Task now supports most variable types, including **booleans**, **integers**,
+**floats** and **arrays**!
+
+{/* truncate */}
+
+## What's the big deal?
+
+These changes allow you to use variables in a much more natural way and opens up
+a wide variety of sprig functions that were previously useless. Take a look at
+some of the examples below for some inspiration.
+
+### Evaluating booleans
+
+No more comparing strings to "true" or "false". Now you can use actual boolean
+values in your templates:
+
+
+
+
+
+```yaml
+version: 3
+
+tasks:
+ foo:
+ vars:
+ BOOL: true # <-- Parsed as a string even though its a YAML boolean
+ cmds:
+ - '{{if eq .BOOL "true"}}echo foo{{end}}'
+```
+
+
+
+
+```yaml
+version: 3
+
+tasks:
+ foo:
+ vars:
+ BOOL: true # <-- Parsed as a boolean
+ cmds:
+ - '{{if .BOOL}}echo foo{{end}}' # <-- No need to compare to "true"
+```
+
+
+
+### Arithmetic
+
+You can now perform basic arithmetic operations on integer and float variables:
+
+```yaml
+version: 3
+
+tasks:
+ foo:
+ vars:
+ INT: 10
+ FLOAT: 3.14159
+ cmds:
+ - 'echo {{add .INT .FLOAT}}'
+```
+
+You can use any of the following arithmetic functions: `add`, `sub`, `mul`,
+`div`, `mod`, `max`, `min`, `floor`, `ceil`, `round` and `randInt`. Check out
+the [slim-sprig math documentation][slim-sprig-math] for more information.
+
+### Arrays
+
+You can now range over arrays inside templates and use list-based functions:
+
+```yaml
+version: 3
+
+tasks:
+ foo:
+ vars:
+ ARRAY: [1, 2, 3]
+ cmds:
+ - 'echo {{range .ARRAY}}{{.}}{{end}}'
+```
+
+You can use any of the following list-based functions: `first`, `rest`, `last`,
+`initial`, `append`, `prepend`, `concat`, `reverse`, `uniq`, `without`, `has`,
+`compact`, `slice` and `chunk`. Check out the [slim-sprg lists
+documentation][slim-sprig-list] for more information.
+
+### Looping over variables using `for`
+
+Previously, you would have to use a delimiter separated string to loop over an
+arbitrary list of items in a variable and split them by using the `split` subkey
+to specify the delimiter. However, we have now added support for looping over
+"collection-type" variables using the `for` keyword, so now you are able to loop
+over list variables directly:
+
+
+
+
+
+```yaml
+version: 3
+
+tasks:
+ foo:
+ vars:
+ LIST: 'foo,bar,baz'
+ cmds:
+ - for:
+ var: LIST
+ split: ','
+ cmd: echo {{.ITEM}}
+```
+
+
+
+
+```yaml
+version: 3
+
+tasks:
+ foo:
+ vars:
+ LIST: ['foo', 'bar', 'baz']
+ cmds:
+ - for:
+ var: LIST
+ cmd: echo {{.ITEM}}
+```
+
+
+
+## What about maps?
+
+Maps were originally included in the Any Variables experiment. However, they
+weren't quite ready yet. Instead of making you wait for everything to be ready
+at once, we have released support for all other variable types and we will
+continue working on map support in the new "[Map Variables][map-variables]"
+experiment.
+
+:::note
+
+If you were previously using maps with the Any Variables experiment and wish to
+continue using them, you will need to enable the new [Map Variables
+experiment][map-variables] instead.
+
+:::
+
+We're looking for feedback on a couple of different proposals, so please give
+them a go and let us know what you think. :pray:
+
+{/* prettier-ignore-start */}
+[v3.36.0]: https://github.com/go-task/task/releases/tag/v3.36.0
+[slim-sprig-math]: https://go-task.github.io/slim-sprig/math.html
+[slim-sprig-list]: https://go-task.github.io/slim-sprig/lists.html
+[map-variables]: /experiments/map-variables
+{/* prettier-ignore-end */}
diff --git a/website/docs/experiments/gentle_force.mdx b/website/docs/experiments/gentle_force.mdx
index ab3d52d3..5521303b 100644
--- a/website/docs/experiments/gentle_force.mdx
+++ b/website/docs/experiments/gentle_force.mdx
@@ -45,5 +45,5 @@ if you want to adopt the new behavior, you can continue to use the `--force`
flag as you do now!
{/* prettier-ignore-start */}
-[enabling-experiments]: /experiments/#enabling-experiments
+[enabling-experiments]: ./experiments.mdx#enabling-experiments
{/* prettier-ignore-end */}
diff --git a/website/docs/experiments/any_variables.mdx b/website/docs/experiments/map_variables.mdx
similarity index 68%
rename from website/docs/experiments/any_variables.mdx
rename to website/docs/experiments/map_variables.mdx
index be768d10..f607519b 100644
--- a/website/docs/experiments/any_variables.mdx
+++ b/website/docs/experiments/map_variables.mdx
@@ -1,11 +1,11 @@
---
-slug: /experiments/any-variables/
+slug: /experiments/map-variables/
---
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
-# Any Variables (#1415)
+# Map Variables (#1585)
:::caution
@@ -15,19 +15,9 @@ environment. They are intended for testing and feedback only.
:::
-Currently, Task only supports string variables. This experiment allows you to
-specify and use the following variable types:
-
-- `string`
-- `bool`
-- `int`
-- `float`
-- `array`
-- `map`
-
-This allows you to have a lot more flexibility in how you use variables in
-Task's templating engine. There are two active proposals for this experiment.
-Click on the tabs below to switch between them.
+Currently, Task supports all variable types except for maps. This experiment
+adds two different proposals for map variables. Click on the tabs below to
+switch between them.
-
:::info
To enable this experiment, set the environment variable:
-`TASK_X_ANY_VARIABLES=2`. Check out [our guide to enabling experiments
+`TASK_X_MAP_VARIABLES=2`. Check out [our guide to enabling experiments
][enabling-experiments] for more information.
:::
-## Maps
-
This proposal maintains backwards-compatibility and the `sh` subkey and adds
another new `map` subkey for defining map variables:
@@ -150,7 +135,13 @@ objects/arrays. This is similar to the `fromJSON` template function, but means
that you only have to parse the JSON/YAML once when you declare the variable,
instead of every time you want to access a value.
-Before:
+
+
+
```yaml
version: 3
@@ -164,7 +155,8 @@ tasks:
- 'echo {{(fromJSON .FOO).b}}'
```
-After:
+
+
```yaml
version: 3
@@ -179,12 +171,26 @@ tasks:
- 'echo {{.FOO.b}}'
```
+
+
## Variables by reference
Lastly, this proposal adds support for defining and passing variables by
reference. This is really important now that variables can be types other than a
-string. Previously, to send a variable from one task to another, you would have
-to use the templating system to pass it:
+string.
+
+Previously, to send a variable from one task to another, you would have to use
+the templating system. Unfortunately, the templater _always_ outputs a string
+and operations on the passed variable may not have behaved as expected. With
+this proposal, you can now pass variables by reference using the `ref` subkey:
+
+
+
+
```yaml
version: 3
@@ -202,10 +208,8 @@ tasks:
- 'echo {{index .FOO 0}}' # <-- FOO is a string so the task outputs '91' which is the ASCII code for '[' instead of the expected 'A'
```
-Unfortunately, this results in the value always being passed as a string as this
-is the output type of the templater and operations on the passed variable may
-not behave as expected. With this proposal, you can now pass variables by
-reference using the `ref` subkey:
+
+
```yaml
version: 3
@@ -224,6 +228,8 @@ tasks:
- 'echo {{index .FOO 0}}' # <-- FOO is still a map so the task outputs 'A' as expected
```
+
+
This means that the type of the variable is maintained when it is passed to
another Task. This also works the same way when calling `deps` and when defining
a variable and can be used in any combination:
@@ -249,14 +255,20 @@ tasks:
----
+## Looping over maps
-## Common to both proposals
+This experiment also adds support for looping over maps using the `for` keyword,
+just like arrays. In addition to the `{{.ITEM}}` variable being populated when
+looping over a map, we also make an additional `{{.KEY}}` variable available
+that holds the string value of the map key.
-Both proposals add support for all other variable types by directly defining
-them in the Taskfile. For example:
+
-### Evaluating booleans
+
```yaml
version: 3
@@ -264,64 +276,15 @@ version: 3
tasks:
foo:
vars:
- BOOL: false
- cmds:
- - '{{if .BOOL}}echo foo{{end}}'
-```
-
-### Arithmetic
-
-```yaml
-version: 3
-
-tasks:
- foo:
- vars:
- INT: 10
- FLOAT: 3.14159
- cmds:
- - 'echo {{add .INT .FLOAT}}'
-```
-
-### Ranging
-
-```yaml
-version: 3
-
-tasks:
- foo:
- vars:
- ARRAY: [1, 2, 3]
- cmds:
- - 'echo {{range .ARRAY}}{{.}}{{end}}'
-```
-
-There are many more templating functions which can be used with the new types of
-variables. For a full list, see the [slim-sprig][slim-sprig] documentation.
-
-## Looping over variables
-
-Previously, you would have to use a delimiter separated string to loop over an
-arbitrary list of items in a variable and split them by using the `split` subkey
-to specify the delimiter:
-
-```yaml
-version: 3
-
-tasks:
- foo:
- vars:
- LIST: 'foo,bar,baz'
+ MAP: {a: 1, b: 2, c: 3}
cmds:
- for:
- var: LIST
- split: ','
- cmd: echo {{.ITEM}}
+ var: MAP
+ cmd: 'echo "{{.KEY}}: {{.ITEM}}"'
```
-Both of these proposals add support for looping over "collection-type" variables
-using the `for` keyword, so now you are able to loop over a map/array variable
-directly:
+
+
```yaml
version: 3
@@ -329,18 +292,23 @@ version: 3
tasks:
foo:
vars:
- LIST: [foo, bar, baz]
+ map:
+ MAP: {a: 1, b: 2, c: 3}
cmds:
- for:
- var: LIST
- cmd: echo {{.ITEM}}
+ var: MAP
+ cmd: 'echo "{{.KEY}}: {{.ITEM}}"'
```
-When looping over a map we also make an additional `{{.KEY}}` variable availabe
-that holds the string value of the map key. Remember that maps are unordered, so
+:::note
+
+Remember that maps are unordered, so
the order in which the items are looped over is random.
+:::
+
+
+
{/* prettier-ignore-start */}
-[enabling-experiments]: /experiments/#enabling-experiments
-[slim-sprig]: https://go-task.github.io/slim-sprig/
+[enabling-experiments]: ./experiments.mdx#enabling-experiments
{/* prettier-ignore-end */}
diff --git a/website/docs/experiments/remote_taskfiles.mdx b/website/docs/experiments/remote_taskfiles.mdx
index 742b451c..9c83ab1c 100644
--- a/website/docs/experiments/remote_taskfiles.mdx
+++ b/website/docs/experiments/remote_taskfiles.mdx
@@ -99,6 +99,6 @@ the `--timeout` flag and specifying a duration. For example, `--timeout 5s` will
set the timeout to 5 seconds.
{/* prettier-ignore-start */}
-[enabling-experiments]: /experiments/#enabling-experiments
+[enabling-experiments]: ./experiments.mdx#enabling-experiments
[man-in-the-middle-attacks]: https://en.wikipedia.org/wiki/Man-in-the-middle_attack
{/* prettier-ignore-end */}
diff --git a/website/docs/experiments/template.mdx b/website/docs/experiments/template.mdx
index fca9a527..f1bb9887 100644
--- a/website/docs/experiments/template.mdx
+++ b/website/docs/experiments/template.mdx
@@ -38,5 +38,5 @@ information.
\{Short explanation of how users should migrate to the new behavior\}
{/* prettier-ignore-start */}
-[enabling-experiments]: /experiments/#enabling-experiments
+[enabling-experiments]: ./experiments.mdx#enabling-experiments
{/* prettier-ignore-end */}
diff --git a/website/docs/installation.mdx b/website/docs/installation.mdx
index ac5e063d..94dd42ca 100644
--- a/website/docs/installation.mdx
+++ b/website/docs/installation.mdx
@@ -31,7 +31,7 @@ brew install go-task
### pkgx
-If you're on macOS or Linux and have [pkgx](https://pkgx.sh/) installed, getting Task is as
+If you're on macOS or Linux and have [pkgx][pkgx] installed, getting Task is as
simple as running:
```shell
@@ -299,5 +299,5 @@ Invoke-Expression -Command path/to/task.ps1
[godownloader]: https://github.com/goreleaser/godownloader
[choco]: https://chocolatey.org/
[scoop]: https://scoop.sh/
-[tea]: https://tea.xyz/
+[pkgx]: https://pkgx.sh/
{/* prettier-ignore-end */}
diff --git a/website/docs/taskfile_versions.mdx b/website/docs/taskfile_versions.mdx
index 0b080726..b85a6f7a 100644
--- a/website/docs/taskfile_versions.mdx
+++ b/website/docs/taskfile_versions.mdx
@@ -256,8 +256,8 @@ The variable priority order was also different:
4. `Taskvars.yml` variables
{/* prettier-ignore-start */}
-[deprecate-version-2-schema]: /deprecations/version-2-schema/
-[output]: /usage#output-syntax
-[ignore_errors]: /usage#ignore-errors
-[includes]: /usage#including-other-taskfiles
+[deprecate-version-2-schema]: ./deprecations/version_2_schema.mdx
+[output]: ./usage.mdx#output-syntax
+[ignore_errors]: ./usage.mdx#ignore-errors
+[includes]: ./usage.mdx#including-other-taskfiles
{/* prettier-ignore-end */}
diff --git a/website/docs/usage.mdx b/website/docs/usage.mdx
index cea5a950..aab75174 100644
--- a/website/docs/usage.mdx
+++ b/website/docs/usage.mdx
@@ -947,8 +947,26 @@ tasks:
## Variables
-When doing interpolation of variables, Task will look for the below. They are
-listed below in order of importance (i.e. most important first):
+Task allows you to set variables using the `vars` keyword. The following
+variable types are supported:
+
+- `string`
+- `bool`
+- `int`
+- `float`
+- `array`
+
+:::note
+
+Maps are not supported by default, but there is an
+[experiment][map-variables] that can be enabled to add support. If
+you're interested in this functionality, we would appreciate your feedback.
+:pray:
+
+:::
+
+Variables can be set in many places in a Taskfile. When executing templates,
+Task will look for variables in the order listed below (most important first):
- Variables declared in the task definition
- Variables given while calling a task from another (See
@@ -1093,8 +1111,8 @@ tasks:
### Looping over variables
To loop over the contents of a variable, you simply need to specify the variable
-you want to loop over. By default, variables will be split on any whitespace
-characters.
+you want to loop over. By default, string variables will be split on any
+whitespace characters.
```yaml
version: '3'
@@ -1108,8 +1126,8 @@ tasks:
cmd: cat {{.ITEM}}
```
-If you need to split on a different character, you can do this by specifying the
-`split` property:
+If you need to split a string on a different character, you can do this by
+specifying the `split` property:
```yaml
version: '3'
@@ -1123,6 +1141,26 @@ tasks:
cmd: cat {{.ITEM}}
```
+You can also loop over arrays directly (and maps if you have the
+[maps experiment](/experiments/map-variables) enabled):
+
+```yaml
+version: 3
+
+tasks:
+ foo:
+ vars:
+ LIST: [foo, bar, baz]
+ cmds:
+ - for:
+ var: LIST
+ cmd: echo {{.ITEM}}
+```
+
+When looping over a map we also make an additional `{{.KEY}}` variable available
+that holds the string value of the map key. Remember that maps are unordered, so
+the order in which the items are looped over is random.
+
All of this also works with dynamic variables!
```yaml
@@ -1956,4 +1994,5 @@ if called by another task, either directly or as a dependency.
{/* prettier-ignore-start */}
[gotemplate]: https://golang.org/pkg/text/template/
+[map-variables]: ./experiments/map_variables.mdx
{/* prettier-ignore-end */}
diff --git a/website/docusaurus.config.ts b/website/docusaurus.config.ts
index 67d0cfb7..ea0d011d 100644
--- a/website/docusaurus.config.ts
+++ b/website/docusaurus.config.ts
@@ -21,8 +21,8 @@ const config: Config = {
tagline: 'A task runner / simpler Make alternative written in Go ',
url: 'https://taskfile.dev',
baseUrl: '/',
- onBrokenLinks: 'throw',
- onBrokenMarkdownLinks: 'throw',
+ onBrokenLinks: 'warn',
+ onBrokenMarkdownLinks: 'warn',
favicon: 'img/favicon.ico',
organizationName: 'go-task',