mirror of
https://github.com/go-task/task.git
synced 2025-01-20 04:59:37 +02:00
feat: ability to resolve refs using templating syntax (#1612)
* feat: resolve references using templating syntax * refactor: moved when references are resolved to one place * fix: linter * docs: update map variables doc
This commit is contained in:
parent
d87e5de56f
commit
630e58767b
@ -11,7 +11,7 @@ linters:
|
||||
|
||||
linters-settings:
|
||||
goimports:
|
||||
local-prefixes: github.com/go-task/task
|
||||
local-prefixes: github.com/go-task
|
||||
gofmt:
|
||||
rewrite-rules:
|
||||
- pattern: 'interface{}'
|
||||
|
3
go.mod
3
go.mod
@ -1,6 +1,6 @@
|
||||
module github.com/go-task/task/v3
|
||||
|
||||
go 1.21
|
||||
go 1.21.0
|
||||
|
||||
require (
|
||||
github.com/Masterminds/semver/v3 v3.2.1
|
||||
@ -8,6 +8,7 @@ require (
|
||||
github.com/dominikbraun/graph v0.23.0
|
||||
github.com/fatih/color v1.16.0
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0
|
||||
github.com/go-task/template v0.0.0-20240422130016-8f6b279b1e90
|
||||
github.com/joho/godotenv v1.5.1
|
||||
github.com/mattn/go-zglob v0.0.4
|
||||
github.com/mitchellh/hashstructure/v2 v2.0.2
|
||||
|
2
go.sum
2
go.sum
@ -12,6 +12,8 @@ github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHk
|
||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
||||
github.com/go-task/template v0.0.0-20240422130016-8f6b279b1e90 h1:JBbiZ2CXIZ9Upe3O2yI5+3ksWoa7hNVNi4BINs8TIrs=
|
||||
github.com/go-task/template v0.0.0-20240422130016-8f6b279b1e90/go.mod h1:RgwRaZK+kni/hJJ7/AaOE2lPQFPbAdji/DyhC6pxo4k=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||
|
@ -62,10 +62,6 @@ func (c *Compiler) getVariables(t *ast.Task, call *ast.Call, evaluateShVars bool
|
||||
cache := &templater.Cache{Vars: result}
|
||||
// Replace values
|
||||
newVar := templater.ReplaceVar(v, cache)
|
||||
// If the variable is a reference, we can resolve it
|
||||
if newVar.Ref != "" {
|
||||
newVar.Value = result.Get(newVar.Ref).Value
|
||||
}
|
||||
// If the variable should not be evaluated, but is nil, set it to an empty string
|
||||
// This stops empty interface errors when using the templater to replace values later
|
||||
if !evaluateShVars && newVar.Value == nil {
|
||||
|
@ -4,13 +4,13 @@ import (
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"mvdan.cc/sh/v3/shell"
|
||||
"mvdan.cc/sh/v3/syntax"
|
||||
|
||||
sprig "github.com/go-task/slim-sprig/v3"
|
||||
"github.com/go-task/template"
|
||||
)
|
||||
|
||||
var templateFuncs template.FuncMap
|
||||
@ -82,7 +82,7 @@ func init() {
|
||||
taskFuncs["ToSlash"] = taskFuncs["toSlash"]
|
||||
taskFuncs["ExeExt"] = taskFuncs["exeExt"]
|
||||
|
||||
templateFuncs = sprig.TxtFuncMap()
|
||||
templateFuncs = template.FuncMap(sprig.TxtFuncMap())
|
||||
for k, v := range taskFuncs {
|
||||
templateFuncs[k] = v
|
||||
}
|
||||
|
@ -4,10 +4,10 @@ import (
|
||||
"bytes"
|
||||
"maps"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/go-task/task/v3/internal/deepcopy"
|
||||
"github.com/go-task/task/v3/taskfile/ast"
|
||||
"github.com/go-task/template"
|
||||
)
|
||||
|
||||
// Cache is a help struct that allow us to call "replaceX" funcs multiple
|
||||
@ -29,6 +29,25 @@ func (r *Cache) Err() error {
|
||||
return r.err
|
||||
}
|
||||
|
||||
func ResolveRef(ref string, cache *Cache) any {
|
||||
// If there is already an error, do nothing
|
||||
if cache.err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Initialize the cache map if it's not already initialized
|
||||
if cache.cacheMap == nil {
|
||||
cache.cacheMap = cache.Vars.ToCacheMap()
|
||||
}
|
||||
|
||||
val, err := template.ResolveRef(ref, cache.cacheMap)
|
||||
if err != nil {
|
||||
cache.err = err
|
||||
return nil
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
func Replace[T any](v T, cache *Cache) T {
|
||||
return ReplaceWithExtra(v, cache, nil)
|
||||
}
|
||||
@ -91,6 +110,9 @@ func ReplaceVar(v ast.Var, cache *Cache) ast.Var {
|
||||
}
|
||||
|
||||
func ReplaceVarWithExtra(v ast.Var, cache *Cache, extra map[string]any) ast.Var {
|
||||
if v.Ref != "" {
|
||||
return ast.Var{Value: ResolveRef(v.Ref, cache)}
|
||||
}
|
||||
return ast.Var{
|
||||
Value: ReplaceWithExtra(v.Value, cache, extra),
|
||||
Sh: ReplaceWithExtra(v.Sh, cache, extra),
|
||||
|
43
testdata/vars/any2/Taskfile.yml
vendored
43
testdata/vars/any2/Taskfile.yml
vendored
@ -8,6 +8,7 @@ tasks:
|
||||
- task: ref
|
||||
- task: ref-sh
|
||||
- task: ref-dep
|
||||
- task: ref-resolver
|
||||
- task: json
|
||||
- task: yaml
|
||||
|
||||
@ -16,10 +17,10 @@ tasks:
|
||||
MAP:
|
||||
map: {"name":"Alice","age":30,"children":[{"name":"Bob","age":5},{"name":"Charlie","age":3},{"name":"Diane","age":1}]}
|
||||
cmds:
|
||||
- task: print-var
|
||||
- task: print-story
|
||||
vars:
|
||||
VAR:
|
||||
ref: MAP
|
||||
ref: .MAP
|
||||
|
||||
nested-map:
|
||||
vars:
|
||||
@ -44,12 +45,12 @@ tasks:
|
||||
MAP:
|
||||
map: {"name":"Alice","age":30,"children":[{"name":"Bob","age":5},{"name":"Charlie","age":3},{"name":"Diane","age":1}]}
|
||||
MAP_REF:
|
||||
ref: MAP
|
||||
ref: .MAP
|
||||
cmds:
|
||||
- task: print-var
|
||||
- task: print-story
|
||||
vars:
|
||||
VAR:
|
||||
ref: MAP_REF
|
||||
ref: .MAP_REF
|
||||
|
||||
ref-sh:
|
||||
vars:
|
||||
@ -58,22 +59,34 @@ tasks:
|
||||
JSON:
|
||||
json: "{{.JSON_STRING}}"
|
||||
MAP_REF:
|
||||
ref: JSON
|
||||
ref: .JSON
|
||||
cmds:
|
||||
- task: print-var
|
||||
- task: print-story
|
||||
vars:
|
||||
VAR:
|
||||
ref: MAP_REF
|
||||
ref: .MAP_REF
|
||||
|
||||
ref-dep:
|
||||
vars:
|
||||
MAP:
|
||||
map: {"name":"Alice","age":30,"children":[{"name":"Bob","age":5},{"name":"Charlie","age":3},{"name":"Diane","age":1}]}
|
||||
deps:
|
||||
- task: print-story
|
||||
vars:
|
||||
VAR:
|
||||
ref: .MAP
|
||||
|
||||
ref-resolver:
|
||||
vars:
|
||||
MAP:
|
||||
map: {"name":"Alice","age":30,"children":[{"name":"Bob","age":5},{"name":"Charlie","age":3},{"name":"Diane","age":1}]}
|
||||
MAP_REF:
|
||||
ref: .MAP
|
||||
cmds:
|
||||
- task: print-var
|
||||
vars:
|
||||
VAR:
|
||||
ref: MAP
|
||||
ref: (index .MAP_REF.children 0).name
|
||||
|
||||
json:
|
||||
vars:
|
||||
@ -82,10 +95,10 @@ tasks:
|
||||
JSON:
|
||||
json: "{{.JSON_STRING}}"
|
||||
cmds:
|
||||
- task: print-var
|
||||
- task: print-story
|
||||
vars:
|
||||
VAR:
|
||||
ref: JSON
|
||||
ref: .JSON
|
||||
|
||||
yaml:
|
||||
vars:
|
||||
@ -94,12 +107,16 @@ tasks:
|
||||
YAML:
|
||||
yaml: "{{.YAML_STRING}}"
|
||||
cmds:
|
||||
- task: print-var
|
||||
- task: print-story
|
||||
vars:
|
||||
VAR:
|
||||
ref: YAML
|
||||
ref: .YAML
|
||||
|
||||
print-var:
|
||||
cmds:
|
||||
- echo "{{.VAR}}"
|
||||
|
||||
print-story:
|
||||
cmds:
|
||||
- >-
|
||||
echo "{{.VAR.name}} has {{len .VAR.children}} children called
|
||||
|
22
variables.go
22
variables.go
@ -164,17 +164,6 @@ func (e *Executor) compiledTask(call *ast.Call, evaluateShVars bool) (*ast.Task,
|
||||
newCmd.Cmd = templater.Replace(cmd.Cmd, cache)
|
||||
newCmd.Task = templater.Replace(cmd.Task, cache)
|
||||
newCmd.Vars = templater.ReplaceVars(cmd.Vars, cache)
|
||||
// Loop over the command's variables and resolve any references to other variables
|
||||
err := cmd.Vars.Range(func(k string, v ast.Var) error {
|
||||
if v.Ref != "" {
|
||||
refVal := vars.Get(v.Ref)
|
||||
newCmd.Vars.Set(k, refVal)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
new.Cmds = append(new.Cmds, newCmd)
|
||||
}
|
||||
}
|
||||
@ -214,17 +203,6 @@ func (e *Executor) compiledTask(call *ast.Call, evaluateShVars bool) (*ast.Task,
|
||||
newDep := dep.DeepCopy()
|
||||
newDep.Task = templater.Replace(dep.Task, cache)
|
||||
newDep.Vars = templater.ReplaceVars(dep.Vars, cache)
|
||||
// Loop over the dep's variables and resolve any references to other variables
|
||||
err := dep.Vars.Range(func(k string, v ast.Var) error {
|
||||
if v.Ref != "" {
|
||||
refVal := vars.Get(v.Ref)
|
||||
newDep.Vars.Set(k, refVal)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
new.Deps = append(new.Deps, newDep)
|
||||
}
|
||||
}
|
||||
|
@ -222,7 +222,7 @@ tasks:
|
||||
- task: bar
|
||||
vars:
|
||||
FOO:
|
||||
ref: FOO # <-- FOO gets passed by reference to bar and maintains its type
|
||||
ref: .FOO # <-- FOO gets passed by reference to bar and maintains its type
|
||||
bar:
|
||||
cmds:
|
||||
- 'echo {{index .FOO 0}}' # <-- FOO is still a map so the task outputs 'A' as expected
|
||||
@ -242,17 +242,38 @@ tasks:
|
||||
vars:
|
||||
FOO: [A, B, C] # <-- FOO is defined as an array
|
||||
BAR:
|
||||
ref: FOO # <-- BAR is defined as a reference to FOO
|
||||
ref: .FOO # <-- BAR is defined as a reference to FOO
|
||||
deps:
|
||||
- task: bar
|
||||
vars:
|
||||
BAR:
|
||||
ref: BAR # <-- BAR gets passed by reference to bar and maintains its type
|
||||
ref: .BAR # <-- BAR gets passed by reference to bar and maintains its type
|
||||
bar:
|
||||
cmds:
|
||||
- 'echo {{index .BAR 0}}' # <-- BAR still refers to FOO so the task outputs 'A'
|
||||
```
|
||||
|
||||
All references use the same templating syntax as regular templates, so in
|
||||
addition to simply calling `.FOO`, you can also pass subkeys (`.FOO.BAR`) or
|
||||
indexes (`index .FOO 0`) and use functions (`len .FOO`):
|
||||
|
||||
```yaml
|
||||
version: 3
|
||||
|
||||
tasks:
|
||||
foo:
|
||||
vars:
|
||||
FOO: [A, B, C] # <-- FOO is defined as an array
|
||||
cmds:
|
||||
- task: bar
|
||||
vars:
|
||||
FOO:
|
||||
ref: index .FOO 0 # <-- The element at index 0 is passed by reference to bar
|
||||
bar:
|
||||
cmds:
|
||||
- 'echo {{.MYVAR}}' # <-- FOO is just the letter 'A'
|
||||
```
|
||||
|
||||
</TabItem></Tabs>
|
||||
|
||||
## Looping over maps
|
||||
|
Loading…
x
Reference in New Issue
Block a user