Skip to content

Commit

Permalink
feat: support looping over map variables (#1436)
Browse files Browse the repository at this point in the history
* feat: support looping over map variables

* feat: add .KEY variable
  • Loading branch information
pd93 authored Dec 21, 2023
1 parent 658b601 commit b592648
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 8 deletions.
31 changes: 27 additions & 4 deletions docs/docs/experiments/any_variables.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,7 @@ tasks:
```
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.
variables. For a full list, see the [slim-sprig][slim-sprig] documentation.
## Looping over variables
Expand All @@ -86,8 +85,8 @@ tasks:
cmd: echo {{.ITEM}}
```

Because this experiment adds support for array variables, the `for` keyword has
been updated to support looping over arrays directly:
Because this experiment adds support for "collection-type" variables, the `for`
keyword has been updated to support looping over arrays directly:

```yaml
version: 3
Expand All @@ -102,6 +101,30 @@ tasks:
cmd: echo {{.ITEM}}
```

This also works for maps. 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 the order in which the items are looped
over is random:

```yaml
version: 3
tasks:
foo:
vars:
MAP:
KEY_1:
SUBKEY: sub_value_1
KEY_2:
SUBKEY: sub_value_2
KEY_3:
SUBKEY: sub_value_3
cmds:
- for:
var: MAP
cmd: echo {{.KEY}} {{.ITEM.SUBKEY}}
```

String splitting is still supported and remember that for simple cases, you have
always been able to loop over an array without using variables at all:

Expand Down
26 changes: 26 additions & 0 deletions testdata/vars/any/Taskfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ tasks:
- task: string-array
- task: for-string
- task: for-int
- task: for-map
- task: for-multi-layer-map

dynamic:
vars:
Expand Down Expand Up @@ -78,3 +80,27 @@ tasks:
var: LIST
cmd: echo {{add .ITEM 100}}

for-map:
vars:
MAP:
KEY_1: value_1
KEY_2: value_2
KEY_3: value_3
cmds:
- for:
var: MAP
cmd: echo {{.KEY}} {{.ITEM}}

for-multi-layer-map:
vars:
MAP:
KEY_1:
SUBKEY: sub_value_1
KEY_2:
SUBKEY: sub_value_2
KEY_3:
SUBKEY: sub_value_3
cmds:
- for:
var: MAP
cmd: echo {{.KEY}} {{.ITEM.SUBKEY}}
12 changes: 8 additions & 4 deletions variables.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ func (e *Executor) compiledTask(call taskfile.Call, evaluateShVars bool) (*taskf
continue
}
if cmd.For != nil {
var keys []string
var list []any
// Get the list from the explicit for list
if cmd.For.List != nil && len(cmd.For.List) > 0 {
Expand Down Expand Up @@ -170,9 +171,9 @@ func (e *Executor) compiledTask(call taskfile.Call, evaluateShVars bool) (*taskf
case []any:
list = value
case map[string]any:
return &taskfile.Task{}, errors.TaskfileInvalidError{
URI: origTask.Location.Taskfile,
Err: errors.New("sh is not supported with the 'Any Variables' experiment enabled.\nSee https://taskfile.dev/experiments/any-variables for more information."),
for k, v := range value {
keys = append(keys, k)
list = append(list, v)
}
default:
return nil, errors.TaskfileInvalidError{
Expand All @@ -191,10 +192,13 @@ func (e *Executor) compiledTask(call taskfile.Call, evaluateShVars bool) (*taskf
as = "ITEM"
}
// Create a new command for each item in the list
for _, loopValue := range list {
for i, loopValue := range list {
extra := map[string]any{
as: loopValue,
}
if len(keys) > 0 {
extra["KEY"] = keys[i]
}
new.Cmds = append(new.Cmds, &taskfile.Cmd{
Cmd: r.ReplaceWithExtra(cmd.Cmd, extra),
Task: r.ReplaceWithExtra(cmd.Task, extra),
Expand Down

0 comments on commit b592648

Please sign in to comment.