mirror of
https://github.com/go-task/task.git
synced 2026-08-29 01:58:56 +02:00
fix: accept []string and []int in matrix refs (#2956)
Co-authored-by: no-hup <shauryaj.finance@gmail.com>
This commit is contained in:
@@ -929,6 +929,7 @@ func TestForCmds(t *testing.T) {
|
|||||||
{name: "loop-explicit"},
|
{name: "loop-explicit"},
|
||||||
{name: "loop-matrix"},
|
{name: "loop-matrix"},
|
||||||
{name: "loop-matrix-ref"},
|
{name: "loop-matrix-ref"},
|
||||||
|
{name: "loop-matrix-ref-computed"},
|
||||||
{
|
{
|
||||||
name: "loop-matrix-ref-error",
|
name: "loop-matrix-ref-error",
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
|
|||||||
11
testdata/for/cmds/Taskfile.yml
vendored
11
testdata/for/cmds/Taskfile.yml
vendored
@@ -4,6 +4,7 @@ vars:
|
|||||||
OS_VAR: ["windows", "linux", "darwin"]
|
OS_VAR: ["windows", "linux", "darwin"]
|
||||||
ARCH_VAR: ["amd64", "arm64"]
|
ARCH_VAR: ["amd64", "arm64"]
|
||||||
NOT_A_LIST: "not a list"
|
NOT_A_LIST: "not a list"
|
||||||
|
OS_CSV: "windows,linux,darwin"
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
# Loop over a list of values
|
# Loop over a list of values
|
||||||
@@ -30,6 +31,16 @@ tasks:
|
|||||||
ref: .ARCH_VAR
|
ref: .ARCH_VAR
|
||||||
cmd: echo "{{.ITEM.OS}}/{{.ITEM.ARCH}}"
|
cmd: echo "{{.ITEM.OS}}/{{.ITEM.ARCH}}"
|
||||||
|
|
||||||
|
loop-matrix-ref-computed:
|
||||||
|
cmds:
|
||||||
|
- for:
|
||||||
|
matrix:
|
||||||
|
OS:
|
||||||
|
ref: 'splitList "," .OS_CSV'
|
||||||
|
ARCH:
|
||||||
|
ref: .ARCH_VAR
|
||||||
|
cmd: echo "{{.ITEM.OS}}/{{.ITEM.ARCH}}"
|
||||||
|
|
||||||
loop-matrix-ref-error:
|
loop-matrix-ref-error:
|
||||||
cmds:
|
cmds:
|
||||||
- for:
|
- for:
|
||||||
|
|||||||
6
testdata/for/cmds/testdata/TestForCmds-loop-matrix-ref-computed.golden
vendored
Normal file
6
testdata/for/cmds/testdata/TestForCmds-loop-matrix-ref-computed.golden
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
windows/amd64
|
||||||
|
windows/arm64
|
||||||
|
linux/amd64
|
||||||
|
linux/arm64
|
||||||
|
darwin/amd64
|
||||||
|
darwin/arm64
|
||||||
25
variables.go
25
variables.go
@@ -355,6 +355,22 @@ func asAnySlice[T any](slice []T) []any {
|
|||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// resolvedAsAnySlice converts a value resolved from a reference into a []any.
|
||||||
|
// A reference does not always resolve to a []any: lists declared in a Taskfile
|
||||||
|
// do, but template functions such as `keys` and `splitList` return a []string.
|
||||||
|
// The accepted types mirror the list types itemsFromFor already supports.
|
||||||
|
func resolvedAsAnySlice(v any) ([]any, bool) {
|
||||||
|
switch value := v.(type) {
|
||||||
|
case []any:
|
||||||
|
return value, true
|
||||||
|
case []string:
|
||||||
|
return asAnySlice(value), true
|
||||||
|
case []int:
|
||||||
|
return asAnySlice(value), true
|
||||||
|
}
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
func itemsFromFor(
|
func itemsFromFor(
|
||||||
f *ast.For,
|
f *ast.For,
|
||||||
dir string,
|
dir string,
|
||||||
@@ -476,12 +492,11 @@ func resolveMatrixRefs(matrix *ast.Matrix, cache *templater.Cache) (*ast.Matrix,
|
|||||||
if cache.Err() != nil {
|
if cache.Err() != nil {
|
||||||
return nil, cache.Err()
|
return nil, cache.Err()
|
||||||
}
|
}
|
||||||
switch value := v.(type) {
|
value, ok := resolvedAsAnySlice(v)
|
||||||
case []any:
|
if !ok {
|
||||||
row.Value = value
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("matrix reference %q must resolve to a list", row.Ref)
|
return nil, fmt.Errorf("matrix reference %q must resolve to a list", row.Ref)
|
||||||
}
|
}
|
||||||
|
row.Value = value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return resolved, nil
|
return resolved, nil
|
||||||
@@ -499,7 +514,7 @@ func resolveEnumRefs(requires *ast.Requires, cache *templater.Cache) error {
|
|||||||
if cache.Err() != nil {
|
if cache.Err() != nil {
|
||||||
return cache.Err()
|
return cache.Err()
|
||||||
}
|
}
|
||||||
arr, ok := resolved.([]any)
|
arr, ok := resolvedAsAnySlice(resolved)
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("enum reference %q must resolve to a list", v.Enum.Ref)
|
return fmt.Errorf("enum reference %q must resolve to a list", v.Enum.Ref)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,3 +43,52 @@ func TestResolveMatrixRefsDoesNotMutateInput(t *testing.T) {
|
|||||||
require.Nil(t, orig.Value, "input matrix was mutated: Ref rows must be resolved into a copy")
|
require.Nil(t, orig.Value, "input matrix was mutated: Ref rows must be resolved into a copy")
|
||||||
require.Equal(t, ".ARCH_VAR", orig.Ref, "input matrix Ref was altered")
|
require.Equal(t, ".ARCH_VAR", orig.Ref, "input matrix Ref was altered")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestResolveMatrixRefsListTypes is a regression test for #2544. A `ref:` is
|
||||||
|
// evaluated as a template expression, so it does not always resolve to a
|
||||||
|
// []any: a list declared in a Taskfile does, but template functions such as
|
||||||
|
// `keys` and `splitList` return a []string. Resolving a ref used to type
|
||||||
|
// assert []any, so those references failed with "must resolve to a list" even
|
||||||
|
// though the value was a list. The accepted types mirror the ones
|
||||||
|
// itemsFromFor supports for `for: var:`.
|
||||||
|
func TestResolveMatrixRefsListTypes(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
value any
|
||||||
|
want []any
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{name: "any slice", value: []any{"amd64", "arm64"}, want: []any{"amd64", "arm64"}},
|
||||||
|
{name: "string slice", value: []string{"amd64", "arm64"}, want: []any{"amd64", "arm64"}},
|
||||||
|
{name: "int slice", value: []int{1, 2}, want: []any{1, 2}},
|
||||||
|
{name: "string", value: "not a list", wantErr: true},
|
||||||
|
{name: "map", value: map[string]any{"key": "value"}, wantErr: true},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
matrix := ast.NewMatrix(
|
||||||
|
&ast.MatrixElement{Key: "ARCH", Value: &ast.MatrixRow{Ref: ".ARCH_VAR"}},
|
||||||
|
)
|
||||||
|
|
||||||
|
vars := ast.NewVars()
|
||||||
|
vars.Set("ARCH_VAR", ast.Var{Value: test.value})
|
||||||
|
cache := &templater.Cache{Vars: vars}
|
||||||
|
|
||||||
|
resolved, err := resolveMatrixRefs(matrix, cache)
|
||||||
|
if test.wantErr {
|
||||||
|
require.ErrorContains(t, err, `matrix reference ".ARCH_VAR" must resolve to a list`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
row, ok := resolved.Get("ARCH")
|
||||||
|
require.True(t, ok, "ARCH row missing from resolved matrix")
|
||||||
|
require.Equal(t, test.want, row.Value)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user