1
0
mirror of https://github.com/go-task/task.git synced 2025-06-04 23:38:05 +02:00

fix: matrix loops should be deterministic (#1784)

This commit is contained in:
Pete Davison 2024-09-02 22:43:54 +01:00 committed by GitHub
parent a3bdb6c40a
commit e4b4d04abd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 14 additions and 19 deletions

View File

@ -5,12 +5,13 @@ import (
"github.com/go-task/task/v3/errors" "github.com/go-task/task/v3/errors"
"github.com/go-task/task/v3/internal/deepcopy" "github.com/go-task/task/v3/internal/deepcopy"
"github.com/go-task/task/v3/internal/omap"
) )
type For struct { type For struct {
From string From string
List []any List []any
Matrix map[string][]any Matrix omap.OrderedMap[string, []any]
Var string Var string
Split string Split string
As string As string
@ -37,7 +38,7 @@ func (f *For) UnmarshalYAML(node *yaml.Node) error {
case yaml.MappingNode: case yaml.MappingNode:
var forStruct struct { var forStruct struct {
Matrix map[string][]any Matrix omap.OrderedMap[string, []any]
Var string Var string
Split string Split string
As string As string
@ -45,10 +46,10 @@ func (f *For) UnmarshalYAML(node *yaml.Node) error {
if err := node.Decode(&forStruct); err != nil { if err := node.Decode(&forStruct); err != nil {
return errors.NewTaskfileDecodeError(err, node) 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") 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") return errors.NewTaskfileDecodeError(nil, node).WithMessage("cannot use both var and matrix in for")
} }
f.Matrix = forStruct.Matrix f.Matrix = forStruct.Matrix
@ -68,7 +69,7 @@ func (f *For) DeepCopy() *For {
return &For{ return &For{
From: f.From, From: f.From,
List: deepcopy.Slice(f.List), List: deepcopy.Slice(f.List),
Matrix: deepcopy.Map(f.Matrix), Matrix: f.Matrix.DeepCopy(),
Var: f.Var, Var: f.Var,
Split: f.Split, Split: f.Split,
As: f.As, As: f.As,

View File

@ -11,6 +11,7 @@ import (
"github.com/go-task/task/v3/internal/execext" "github.com/go-task/task/v3/internal/execext"
"github.com/go-task/task/v3/internal/filepathext" "github.com/go-task/task/v3/internal/filepathext"
"github.com/go-task/task/v3/internal/fingerprint" "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/internal/templater"
"github.com/go-task/task/v3/taskfile/ast" "github.com/go-task/task/v3/taskfile/ast"
) )
@ -272,7 +273,7 @@ func itemsFromFor(
var keys []string // The list of keys to loop over (only if looping over a map) 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 var values []any // The list of values to loop over
// Get the list from a matrix // Get the list from a matrix
if f.Matrix != nil { if f.Matrix.Len() != 0 {
return asAnySlice(product(f.Matrix)), nil, nil return asAnySlice(product(f.Matrix)), nil, nil
} }
// Get the list from the explicit for list // Get the list from the explicit for list
@ -328,24 +329,16 @@ func itemsFromFor(
} }
// product generates the cartesian product of the input map of slices. // product generates the cartesian product of the input map of slices.
func product(inputMap map[string][]any) []map[string]any { func product(inputMap omap.OrderedMap[string, []any]) []map[string]any {
if len(inputMap) == 0 { if inputMap.Len() == 0 {
return nil 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 // Start with an empty product result
result := []map[string]any{{}} result := []map[string]any{{}}
// Iterate over each slice in the slices // 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 var newResult []map[string]any
// For each combination in the current result // For each combination in the current result
@ -358,14 +351,15 @@ func product(inputMap map[string][]any) []map[string]any {
newComb[k] = v newComb[k] = v
} }
// Add the current item with the corresponding key // Add the current item with the corresponding key
newComb[keys[i]] = item newComb[key] = item
newResult = append(newResult, newComb) newResult = append(newResult, newComb)
} }
} }
// Update result with the new combinations // Update result with the new combinations
result = newResult result = newResult
} return nil
})
return result return result
} }