1
0
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:
Pete Davison 2024-04-24 20:47:24 +01:00 committed by GitHub
parent d87e5de56f
commit 630e58767b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 84 additions and 47 deletions

View File

@ -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
View File

@ -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
View File

@ -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=

View File

@ -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 {

View File

@ -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
}

View File

@ -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),

View File

@ -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

View File

@ -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)
}
}

View File

@ -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