Skip to content

Commit c6f1b3a

Browse files
authoredMar 26, 2025··
feat: make map variables experiment (prop 2) generally available (#2081)
* feat: make map variables experiment (prop 2) generally available * docs: remove map variables experiment page and update usage to include map variable info
1 parent cb14a4f commit c6f1b3a

File tree

5 files changed

+51
-348
lines changed

5 files changed

+51
-348
lines changed
 

‎internal/experiments/experiments.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ func init() {
4444
GentleForce = New("GENTLE_FORCE", 1)
4545
RemoteTaskfiles = New("REMOTE_TASKFILES", 1)
4646
AnyVariables = New("ANY_VARIABLES")
47-
MapVariables = New("MAP_VARIABLES", 1, 2)
47+
MapVariables = New("MAP_VARIABLES")
4848
EnvPrecedence = New("ENV_PRECEDENCE", 1)
4949
}
5050

‎taskfile/ast/var.go

+4-63
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
11
package ast
22

33
import (
4-
"strings"
5-
64
"gopkg.in/yaml.v3"
75

86
"github.com/go-task/task/v3/errors"
9-
"github.com/go-task/task/v3/internal/experiments"
107
)
118

129
// Var represents either a static or dynamic variable.
@@ -19,82 +16,26 @@ type Var struct {
1916
}
2017

2118
func (v *Var) UnmarshalYAML(node *yaml.Node) error {
22-
if experiments.MapVariables.Enabled() {
23-
24-
// This implementation is not backwards-compatible and replaces the 'sh' key with map variables
25-
if experiments.MapVariables.Value == 1 {
26-
var value any
27-
if err := node.Decode(&value); err != nil {
28-
return errors.NewTaskfileDecodeError(err, node)
29-
}
30-
// If the value is a string and it starts with $, then it's a shell command
31-
if str, ok := value.(string); ok {
32-
if str, ok = strings.CutPrefix(str, "$"); ok {
33-
v.Sh = &str
34-
return nil
35-
}
36-
if str, ok = strings.CutPrefix(str, "#"); ok {
37-
v.Ref = str
38-
return nil
39-
}
40-
}
41-
v.Value = value
42-
return nil
43-
}
44-
45-
// This implementation IS backwards-compatible and keeps the 'sh' key and allows map variables to be added under the `map` key
46-
if experiments.MapVariables.Value == 2 {
47-
switch node.Kind {
48-
case yaml.MappingNode:
49-
key := node.Content[0].Value
50-
switch key {
51-
case "sh", "ref", "map":
52-
var m struct {
53-
Sh *string
54-
Ref string
55-
Map any
56-
}
57-
if err := node.Decode(&m); err != nil {
58-
return errors.NewTaskfileDecodeError(err, node)
59-
}
60-
v.Sh = m.Sh
61-
v.Ref = m.Ref
62-
v.Value = m.Map
63-
return nil
64-
default:
65-
return errors.NewTaskfileDecodeError(nil, node).WithMessage(`%q is not a valid variable type. Try "sh", "ref", "map" or using a scalar value`, key)
66-
}
67-
default:
68-
var value any
69-
if err := node.Decode(&value); err != nil {
70-
return errors.NewTaskfileDecodeError(err, node)
71-
}
72-
v.Value = value
73-
return nil
74-
}
75-
}
76-
}
77-
7819
switch node.Kind {
79-
8020
case yaml.MappingNode:
8121
key := node.Content[0].Value
8222
switch key {
83-
case "sh", "ref":
23+
case "sh", "ref", "map":
8424
var m struct {
8525
Sh *string
8626
Ref string
27+
Map any
8728
}
8829
if err := node.Decode(&m); err != nil {
8930
return errors.NewTaskfileDecodeError(err, node)
9031
}
9132
v.Sh = m.Sh
9233
v.Ref = m.Ref
34+
v.Value = m.Map
9335
return nil
9436
default:
95-
return errors.NewTaskfileDecodeError(nil, node).WithMessage("maps cannot be assigned to variables")
37+
return errors.NewTaskfileDecodeError(nil, node).WithMessage(`%q is not a valid variable type. Try "sh", "ref", "map" or using a scalar value`, key)
9638
}
97-
9839
default:
9940
var value any
10041
if err := node.Decode(&value); err != nil {

‎website/docs/experiments/map_variables.mdx

-245
This file was deleted.

‎website/docs/usage.mdx

+45-38
Original file line numberDiff line numberDiff line change
@@ -1113,53 +1113,38 @@ variable types are supported:
11131113
- `int`
11141114
- `float`
11151115
- `array`
1116+
- `map`
11161117

11171118
:::note
11181119

1119-
Maps are not supported by default, but there is an
1120-
[experiment][map-variables] that can be enabled to add support. If
1121-
you're interested in this functionality, we would appreciate your feedback.
1122-
:pray:
1120+
Defining a map requires that you use a special `map` subkey (see example below).
11231121

1124-
In the meantime, it is technically possible to define a map using a `ref` resolver and a templating function. For example:
1125-
1126-
```yaml
1127-
version: '3'
1128-
1129-
tasks:
1130-
task-with-map:
1131-
vars:
1132-
FOO:
1133-
ref: dict "a" "1" "b" "2" "c" "3"
1134-
cmds:
1135-
- echo {{.FOO}}
1136-
```
1137-
1138-
```txt
1139-
map[a:1 b:2 c:3]
1140-
```
1141-
1142-
OR by using the same technique with JSON:
1122+
:::
11431123

11441124
```yaml
1145-
version: '3'
1125+
version: 3
11461126
11471127
tasks:
1148-
task-with-map:
1128+
foo:
11491129
vars:
1150-
JSON: '{"a": 1, "b": 2, "c": 3}'
1151-
FOO:
1152-
ref: "fromJson .JSON"
1153-
cmds:
1154-
- echo {{.FOO}}
1130+
STRING: 'Hello, World!'
1131+
BOOL: true
1132+
INT: 42
1133+
FLOAT: 3.14
1134+
ARRAY: [1, 2, 3]
1135+
MAP:
1136+
map: {A: 1, B: 2, C: 3}
1137+
cmds:
1138+
- 'echo {{.STRING}}' # Hello, World!
1139+
- 'echo {{.BOOL}}' # true
1140+
- 'echo {{.INT}}' # 42
1141+
- 'echo {{.FLOAT}}' # 3.14
1142+
- 'echo {{.ARRAY}}' # [1 2 3]
1143+
- 'echo {{.ARRAY.0}}' # 1
1144+
- 'echo {{.MAP}}' # map[A:1 B:2 C:3]
1145+
- 'echo {{.MAP.A}}' # 1
11551146
```
11561147

1157-
```txt
1158-
map[a:1 b:2 c:3]
1159-
```
1160-
1161-
:::
1162-
11631148
Variables can be set in many places in a Taskfile. When executing
11641149
[templates][templating-reference], Task will look for variables in the order
11651150
listed below (most important first):
@@ -1360,6 +1345,29 @@ tasks:
13601345
- 'echo {{.FOO}}' # <-- FOO is just the letter 'A'
13611346
```
13621347

1348+
### Parsing JSON/YAML into map variables
1349+
1350+
If you have a raw JSON or YAML string that you want to process in Task, you can
1351+
use a combination of the `ref` keyword and the `fromJson` or `fromYaml`
1352+
templating functions to parse the string into a map variable. For example:
1353+
1354+
```yaml
1355+
version: '3'
1356+
1357+
tasks:
1358+
task-with-map:
1359+
vars:
1360+
JSON: '{"a": 1, "b": 2, "c": 3}'
1361+
FOO:
1362+
ref: "fromJson .JSON"
1363+
cmds:
1364+
- echo {{.FOO}}
1365+
```
1366+
1367+
```txt
1368+
map[a:1 b:2 c:3]
1369+
```
1370+
13631371
## Looping over values
13641372

13651373
Task allows you to loop over certain values and execute a command for each.
@@ -1508,7 +1516,7 @@ tasks:
15081516
cmd: cat {{.ITEM}}
15091517
```
15101518

1511-
You can also loop over arrays directly and maps:
1519+
You can also loop over arrays and maps directly:
15121520

15131521
```yaml
15141522
version: 3
@@ -2319,6 +2327,5 @@ if called by another task, either directly or as a dependency.
23192327

23202328
{/* prettier-ignore-start */}
23212329
[gotemplate]: https://golang.org/pkg/text/template/
2322-
[map-variables]: ./experiments/map_variables.mdx
23232330
[templating-reference]: ./reference/templating.mdx
23242331
{/* prettier-ignore-end */}

‎website/static/schema.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,7 @@
276276
"^.*$": {
277277
"anyOf": [
278278
{
279-
"type": ["boolean", "integer", "null", "number", "string", "object", "array"]
279+
"type": ["boolean", "integer", "null", "number", "string", "array"]
280280
},
281281
{
282282
"$ref": "#/definitions/var_subkey"

0 commit comments

Comments
 (0)
Please sign in to comment.