diff --git a/internal/experiments/experiments.go b/internal/experiments/experiments.go
index 6235a039..ab676022 100644
--- a/internal/experiments/experiments.go
+++ b/internal/experiments/experiments.go
@@ -44,7 +44,7 @@ func init() {
GentleForce = New("GENTLE_FORCE", 1)
RemoteTaskfiles = New("REMOTE_TASKFILES", 1)
AnyVariables = New("ANY_VARIABLES")
- MapVariables = New("MAP_VARIABLES", 1, 2)
+ MapVariables = New("MAP_VARIABLES")
EnvPrecedence = New("ENV_PRECEDENCE", 1)
}
diff --git a/taskfile/ast/var.go b/taskfile/ast/var.go
index abe5157d..0806e423 100644
--- a/taskfile/ast/var.go
+++ b/taskfile/ast/var.go
@@ -1,12 +1,9 @@
package ast
import (
- "strings"
-
"gopkg.in/yaml.v3"
"github.com/go-task/task/v3/errors"
- "github.com/go-task/task/v3/internal/experiments"
)
// Var represents either a static or dynamic variable.
@@ -19,82 +16,26 @@ type Var struct {
}
func (v *Var) UnmarshalYAML(node *yaml.Node) error {
- if experiments.MapVariables.Enabled() {
-
- // This implementation is not backwards-compatible and replaces the 'sh' key with map variables
- if experiments.MapVariables.Value == 1 {
- var value any
- if err := node.Decode(&value); err != nil {
- return errors.NewTaskfileDecodeError(err, node)
- }
- // If the value is a string and it starts with $, then it's a shell command
- if str, ok := value.(string); ok {
- if str, ok = strings.CutPrefix(str, "$"); ok {
- v.Sh = &str
- return nil
- }
- if str, ok = strings.CutPrefix(str, "#"); ok {
- v.Ref = str
- return nil
- }
- }
- v.Value = value
- return nil
- }
-
- // This implementation IS backwards-compatible and keeps the 'sh' key and allows map variables to be added under the `map` key
- if experiments.MapVariables.Value == 2 {
- switch node.Kind {
- case yaml.MappingNode:
- key := node.Content[0].Value
- switch key {
- case "sh", "ref", "map":
- var m struct {
- Sh *string
- Ref string
- Map any
- }
- if err := node.Decode(&m); err != nil {
- return errors.NewTaskfileDecodeError(err, node)
- }
- v.Sh = m.Sh
- v.Ref = m.Ref
- v.Value = m.Map
- return nil
- default:
- return errors.NewTaskfileDecodeError(nil, node).WithMessage(`%q is not a valid variable type. Try "sh", "ref", "map" or using a scalar value`, key)
- }
- default:
- var value any
- if err := node.Decode(&value); err != nil {
- return errors.NewTaskfileDecodeError(err, node)
- }
- v.Value = value
- return nil
- }
- }
- }
-
switch node.Kind {
-
case yaml.MappingNode:
key := node.Content[0].Value
switch key {
- case "sh", "ref":
+ case "sh", "ref", "map":
var m struct {
Sh *string
Ref string
+ Map any
}
if err := node.Decode(&m); err != nil {
return errors.NewTaskfileDecodeError(err, node)
}
v.Sh = m.Sh
v.Ref = m.Ref
+ v.Value = m.Map
return nil
default:
- return errors.NewTaskfileDecodeError(nil, node).WithMessage("maps cannot be assigned to variables")
+ return errors.NewTaskfileDecodeError(nil, node).WithMessage(`%q is not a valid variable type. Try "sh", "ref", "map" or using a scalar value`, key)
}
-
default:
var value any
if err := node.Decode(&value); err != nil {
diff --git a/website/docs/experiments/map_variables.mdx b/website/docs/experiments/map_variables.mdx
deleted file mode 100644
index 477714d0..00000000
--- a/website/docs/experiments/map_variables.mdx
+++ /dev/null
@@ -1,245 +0,0 @@
----
-slug: /experiments/map-variables/
----
-
-import Tabs from '@theme/Tabs';
-import TabItem from '@theme/TabItem';
-
-# Map Variables (#1585)
-
-:::caution
-
-All experimental features are subject to breaking changes and/or removal _at any
-time_. We strongly recommend that you do not use these features in a production
-environment. They are intended for testing and feedback only.
-
-:::
-
-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.
-
-
-
-
-
-:::warning
-
-This experiment proposal breaks the following functionality:
-
-- Dynamically defined variables (using the `sh` keyword)
-
-:::
-
-:::info
-
-To enable this experiment, set the environment variable:
-`TASK_X_MAP_VARIABLES=1`. Check out [our guide to enabling experiments
-][enabling-experiments] for more information.
-
-:::
-
-This proposal removes support for the `sh` and `ref` keywords in favour of a new
-syntax for dynamically defined variables and references. This allows you to
-define a map directly as you would for any other type:
-
-```yaml
-version: 3
-
-tasks:
- foo:
- vars:
- FOO: {a: 1, b: 2, c: 3} # <-- Directly defined map on the `FOO` key
- cmds:
- - 'echo {{.FOO.a}}'
-```
-
-## Migration
-
-Taskfiles with dynamically defined variables via the `sh` subkey or references
-defined with `ref` will no longer work with this experiment enabled. In order to
-keep using these features, you will need to migrate your Taskfile to use the new
-syntax.
-
-### Dynamic Variables
-
-Previously, you had to define dynamic variables using the `sh` subkey. With this
-experiment enabled, you will need to remove the `sh` subkey and define your
-command as a string that begins with a `$`. This will instruct Task to interpret
-the string as a command instead of a literal value and the variable will be
-populated with the output of the command. For example:
-
-
-
-
-
-```yaml
-version: 3
-
-tasks:
- foo:
- vars:
- CALCULATED_VAR:
- sh: 'echo hello'
- cmds:
- - 'echo {{.CALCULATED_VAR}}'
-```
-
-
-
-
-```yaml
-version: 3
-
-tasks:
- foo:
- vars:
- CALCULATED_VAR: '$echo hello' # <-- Prefix dynamic variable with a `$`
- cmds:
- - 'echo {{.CALCULATED_VAR}}'
-```
-
-
-
-### References
-
-
-
-
-
-```yaml
-version: 3
-
-tasks:
- foo:
- vars:
- VAR: 42
- VAR_REF:
- ref: '.FOO'
- cmds:
- - 'echo {{.VAR_REF}}'
-```
-
-
-
-
-```yaml
-version: 3
-
-tasks:
- foo:
- vars:
- VAR: 42
- VAR_REF: '#.FOO' # <-- Prefix reference with a `#`
- cmds:
- - 'echo {{.VAR_REF}}'
-```
-
-
-
-If your current Taskfile contains a string variable that begins with a `$` or a
-`#`, you will now need to escape it with a backslash (`\`) to stop Task from
-interpreting it as a command or reference.
-
-
-
-
-:::info
-
-To enable this experiment, set the environment variable:
-`TASK_X_MAP_VARIABLES=2`. Check out [our guide to enabling experiments
-][enabling-experiments] for more information.
-
-:::
-
-This proposal maintains backwards-compatibility and the `sh` subkey and adds
-another new `map` subkey for defining map variables:
-
-```yaml
-version: 3
-
-tasks:
- foo:
- vars:
- FOO:
- map: {a: 1, b: 2, c: 3} # <-- Defined using the `map' subkey instead of directly on 'FOO'
- BAR: true # <-- Other types of variables are still defined directly on the key
- BAZ:
- sh: 'echo Hello Task' # <-- The `sh` subkey is still supported
- QUX:
- ref: '.BAZ' # <-- The `ref` subkey is still supported
- cmds:
- - 'echo {{.FOO.a}}'
-```
-
-
-
-## Looping over maps
-
-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.
-
-
-
-
-
-```yaml
-version: 3
-
-tasks:
- foo:
- vars:
- MAP: {a: 1, b: 2, c: 3}
- cmds:
- - for:
- var: MAP
- cmd: 'echo "{{.KEY}}: {{.ITEM}}"'
-```
-
-
-
-
-```yaml
-version: 3
-
-tasks:
- foo:
- vars:
- map:
- MAP: {a: 1, b: 2, c: 3}
- cmds:
- - for:
- var: MAP
- cmd: 'echo "{{.KEY}}: {{.ITEM}}"'
-```
-
-:::note
-
-Remember that maps are unordered, so
-the order in which the items are looped over is random.
-
-:::
-
-
-
-{/* prettier-ignore-start */}
-[enabling-experiments]: ./experiments.mdx#enabling-experiments
-{/* prettier-ignore-end */}
diff --git a/website/docs/usage.mdx b/website/docs/usage.mdx
index f9738596..a894df42 100644
--- a/website/docs/usage.mdx
+++ b/website/docs/usage.mdx
@@ -1113,53 +1113,38 @@ variable types are supported:
- `int`
- `float`
- `array`
+- `map`
:::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:
-
-In the meantime, it is technically possible to define a map using a `ref` resolver and a templating function. For example:
-
-```yaml
-version: '3'
-
-tasks:
- task-with-map:
- vars:
- FOO:
- ref: dict "a" "1" "b" "2" "c" "3"
- cmds:
- - echo {{.FOO}}
-```
-
-```txt
-map[a:1 b:2 c:3]
-```
-
-OR by using the same technique with JSON:
-
-```yaml
-version: '3'
-
-tasks:
- task-with-map:
- vars:
- JSON: '{"a": 1, "b": 2, "c": 3}'
- FOO:
- ref: "fromJson .JSON"
- cmds:
- - echo {{.FOO}}
-```
-
-```txt
-map[a:1 b:2 c:3]
-```
+Defining a map requires that you use a special `map` subkey (see example below).
:::
+```yaml
+version: 3
+
+tasks:
+ foo:
+ vars:
+ STRING: 'Hello, World!'
+ BOOL: true
+ INT: 42
+ FLOAT: 3.14
+ ARRAY: [1, 2, 3]
+ MAP:
+ map: {A: 1, B: 2, C: 3}
+ cmds:
+ - 'echo {{.STRING}}' # Hello, World!
+ - 'echo {{.BOOL}}' # true
+ - 'echo {{.INT}}' # 42
+ - 'echo {{.FLOAT}}' # 3.14
+ - 'echo {{.ARRAY}}' # [1 2 3]
+ - 'echo {{.ARRAY.0}}' # 1
+ - 'echo {{.MAP}}' # map[A:1 B:2 C:3]
+ - 'echo {{.MAP.A}}' # 1
+```
+
Variables can be set in many places in a Taskfile. When executing
[templates][templating-reference], Task will look for variables in the order
listed below (most important first):
@@ -1360,6 +1345,29 @@ tasks:
- 'echo {{.FOO}}' # <-- FOO is just the letter 'A'
```
+### Parsing JSON/YAML into map variables
+
+If you have a raw JSON or YAML string that you want to process in Task, you can
+use a combination of the `ref` keyword and the `fromJson` or `fromYaml`
+templating functions to parse the string into a map variable. For example:
+
+```yaml
+version: '3'
+
+tasks:
+ task-with-map:
+ vars:
+ JSON: '{"a": 1, "b": 2, "c": 3}'
+ FOO:
+ ref: "fromJson .JSON"
+ cmds:
+ - echo {{.FOO}}
+```
+
+```txt
+map[a:1 b:2 c:3]
+```
+
## Looping over values
Task allows you to loop over certain values and execute a command for each.
@@ -1508,7 +1516,7 @@ tasks:
cmd: cat {{.ITEM}}
```
-You can also loop over arrays directly and maps:
+You can also loop over arrays and maps directly:
```yaml
version: 3
@@ -2319,6 +2327,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
[templating-reference]: ./reference/templating.mdx
{/* prettier-ignore-end */}
diff --git a/website/static/schema.json b/website/static/schema.json
index 49af497b..1aaeaa90 100644
--- a/website/static/schema.json
+++ b/website/static/schema.json
@@ -276,7 +276,7 @@
"^.*$": {
"anyOf": [
{
- "type": ["boolean", "integer", "null", "number", "string", "object", "array"]
+ "type": ["boolean", "integer", "null", "number", "string", "array"]
},
{
"$ref": "#/definitions/var_subkey"