Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: matrix loops should be deterministic #1784

Merged
merged 1 commit into from
Sep 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions taskfile/ast/for.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@ import (

"github.com/go-task/task/v3/errors"
"github.com/go-task/task/v3/internal/deepcopy"
"github.com/go-task/task/v3/internal/omap"
)

type For struct {
From string
List []any
Matrix map[string][]any
Matrix omap.OrderedMap[string, []any]
Var string
Split string
As string
Expand All @@ -37,18 +38,18 @@ func (f *For) UnmarshalYAML(node *yaml.Node) error {

case yaml.MappingNode:
var forStruct struct {
Matrix map[string][]any
Matrix omap.OrderedMap[string, []any]
Var string
Split string
As string
}
if err := node.Decode(&forStruct); err != nil {
return errors.NewTaskfileDecodeError(err, node)
}
if forStruct.Var == "" && forStruct.Matrix == nil {
if forStruct.Var == "" && forStruct.Matrix.Len() == 0 {
return errors.NewTaskfileDecodeError(nil, node).WithMessage("invalid keys in for")
}
if forStruct.Var != "" && forStruct.Matrix != nil {
if forStruct.Var != "" && forStruct.Matrix.Len() != 0 {
return errors.NewTaskfileDecodeError(nil, node).WithMessage("cannot use both var and matrix in for")
}
f.Matrix = forStruct.Matrix
Expand All @@ -68,7 +69,7 @@ func (f *For) DeepCopy() *For {
return &For{
From: f.From,
List: deepcopy.Slice(f.List),
Matrix: deepcopy.Map(f.Matrix),
Matrix: f.Matrix.DeepCopy(),
Var: f.Var,
Split: f.Split,
As: f.As,
Expand Down
22 changes: 8 additions & 14 deletions variables.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/go-task/task/v3/internal/execext"
"github.com/go-task/task/v3/internal/filepathext"
"github.com/go-task/task/v3/internal/fingerprint"
"github.com/go-task/task/v3/internal/omap"
"github.com/go-task/task/v3/internal/templater"
"github.com/go-task/task/v3/taskfile/ast"
)
Expand Down Expand Up @@ -272,7 +273,7 @@ func itemsFromFor(
var keys []string // The list of keys to loop over (only if looping over a map)
var values []any // The list of values to loop over
// Get the list from a matrix
if f.Matrix != nil {
if f.Matrix.Len() != 0 {
return asAnySlice(product(f.Matrix)), nil, nil
}
// Get the list from the explicit for list
Expand Down Expand Up @@ -328,24 +329,16 @@ func itemsFromFor(
}

// product generates the cartesian product of the input map of slices.
func product(inputMap map[string][]any) []map[string]any {
if len(inputMap) == 0 {
func product(inputMap omap.OrderedMap[string, []any]) []map[string]any {
if inputMap.Len() == 0 {
return nil
}

// Extract the keys and corresponding slices
keys := make([]string, 0, len(inputMap))
slices := make([][]any, 0, len(inputMap))
for key, slice := range inputMap {
keys = append(keys, key)
slices = append(slices, slice)
}

// Start with an empty product result
result := []map[string]any{{}}

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

// For each combination in the current result
Expand All @@ -358,14 +351,15 @@ func product(inputMap map[string][]any) []map[string]any {
newComb[k] = v
}
// Add the current item with the corresponding key
newComb[keys[i]] = item
newComb[key] = item
newResult = append(newResult, newComb)
}
}

// Update result with the new combinations
result = newResult
}
return nil
})

return result
}
Loading