Skip to content

Commit e4b4d04

Browse files
authored
fix: matrix loops should be deterministic (#1784)
1 parent a3bdb6c commit e4b4d04

File tree

2 files changed

+14
-19
lines changed

2 files changed

+14
-19
lines changed

taskfile/ast/for.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@ import (
55

66
"github.com/go-task/task/v3/errors"
77
"github.com/go-task/task/v3/internal/deepcopy"
8+
"github.com/go-task/task/v3/internal/omap"
89
)
910

1011
type For struct {
1112
From string
1213
List []any
13-
Matrix map[string][]any
14+
Matrix omap.OrderedMap[string, []any]
1415
Var string
1516
Split string
1617
As string
@@ -37,18 +38,18 @@ func (f *For) UnmarshalYAML(node *yaml.Node) error {
3738

3839
case yaml.MappingNode:
3940
var forStruct struct {
40-
Matrix map[string][]any
41+
Matrix omap.OrderedMap[string, []any]
4142
Var string
4243
Split string
4344
As string
4445
}
4546
if err := node.Decode(&forStruct); err != nil {
4647
return errors.NewTaskfileDecodeError(err, node)
4748
}
48-
if forStruct.Var == "" && forStruct.Matrix == nil {
49+
if forStruct.Var == "" && forStruct.Matrix.Len() == 0 {
4950
return errors.NewTaskfileDecodeError(nil, node).WithMessage("invalid keys in for")
5051
}
51-
if forStruct.Var != "" && forStruct.Matrix != nil {
52+
if forStruct.Var != "" && forStruct.Matrix.Len() != 0 {
5253
return errors.NewTaskfileDecodeError(nil, node).WithMessage("cannot use both var and matrix in for")
5354
}
5455
f.Matrix = forStruct.Matrix
@@ -68,7 +69,7 @@ func (f *For) DeepCopy() *For {
6869
return &For{
6970
From: f.From,
7071
List: deepcopy.Slice(f.List),
71-
Matrix: deepcopy.Map(f.Matrix),
72+
Matrix: f.Matrix.DeepCopy(),
7273
Var: f.Var,
7374
Split: f.Split,
7475
As: f.As,

variables.go

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"github.com/go-task/task/v3/internal/execext"
1212
"github.com/go-task/task/v3/internal/filepathext"
1313
"github.com/go-task/task/v3/internal/fingerprint"
14+
"github.com/go-task/task/v3/internal/omap"
1415
"github.com/go-task/task/v3/internal/templater"
1516
"github.com/go-task/task/v3/taskfile/ast"
1617
)
@@ -272,7 +273,7 @@ func itemsFromFor(
272273
var keys []string // The list of keys to loop over (only if looping over a map)
273274
var values []any // The list of values to loop over
274275
// Get the list from a matrix
275-
if f.Matrix != nil {
276+
if f.Matrix.Len() != 0 {
276277
return asAnySlice(product(f.Matrix)), nil, nil
277278
}
278279
// Get the list from the explicit for list
@@ -328,24 +329,16 @@ func itemsFromFor(
328329
}
329330

330331
// product generates the cartesian product of the input map of slices.
331-
func product(inputMap map[string][]any) []map[string]any {
332-
if len(inputMap) == 0 {
332+
func product(inputMap omap.OrderedMap[string, []any]) []map[string]any {
333+
if inputMap.Len() == 0 {
333334
return nil
334335
}
335336

336-
// Extract the keys and corresponding slices
337-
keys := make([]string, 0, len(inputMap))
338-
slices := make([][]any, 0, len(inputMap))
339-
for key, slice := range inputMap {
340-
keys = append(keys, key)
341-
slices = append(slices, slice)
342-
}
343-
344337
// Start with an empty product result
345338
result := []map[string]any{{}}
346339

347340
// Iterate over each slice in the slices
348-
for i, slice := range slices {
341+
_ = inputMap.Range(func(key string, slice []any) error {
349342
var newResult []map[string]any
350343

351344
// For each combination in the current result
@@ -358,14 +351,15 @@ func product(inputMap map[string][]any) []map[string]any {
358351
newComb[k] = v
359352
}
360353
// Add the current item with the corresponding key
361-
newComb[keys[i]] = item
354+
newComb[key] = item
362355
newResult = append(newResult, newComb)
363356
}
364357
}
365358

366359
// Update result with the new combinations
367360
result = newResult
368-
}
361+
return nil
362+
})
369363

370364
return result
371365
}

0 commit comments

Comments
 (0)