mirror of
https://github.com/go-task/task.git
synced 2025-06-17 00:17:51 +02:00
This shouldn't have any behavior changes for now. This is a code refactor that should allow us to do further improvements on how variables are handled, specially regarding respecting the declaration order in Taskfiles, which should make it easier for the users. Initial work on #218
112 lines
2.6 KiB
Go
112 lines
2.6 KiB
Go
package v2
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
"sync"
|
|
|
|
"github.com/go-task/task/v2/internal/compiler"
|
|
"github.com/go-task/task/v2/internal/execext"
|
|
"github.com/go-task/task/v2/internal/logger"
|
|
"github.com/go-task/task/v2/internal/taskfile"
|
|
"github.com/go-task/task/v2/internal/templater"
|
|
)
|
|
|
|
var _ compiler.Compiler = &CompilerV2{}
|
|
|
|
type CompilerV2 struct {
|
|
Dir string
|
|
|
|
Taskvars *taskfile.Vars
|
|
TaskfileVars *taskfile.Vars
|
|
|
|
Expansions int
|
|
|
|
Logger *logger.Logger
|
|
|
|
dynamicCache map[string]string
|
|
muDynamicCache sync.Mutex
|
|
}
|
|
|
|
// GetVariables returns fully resolved variables following the priority order:
|
|
// 1. Task variables
|
|
// 2. Call variables
|
|
// 3. Taskfile variables
|
|
// 4. Taskvars file variables
|
|
// 5. Environment variables
|
|
func (c *CompilerV2) GetVariables(t *taskfile.Task, call taskfile.Call) (*taskfile.Vars, error) {
|
|
vr := varResolver{c: c, vars: compiler.GetEnviron()}
|
|
vr.vars.Set("TASK", taskfile.Var{Static: t.Task})
|
|
for _, vars := range []*taskfile.Vars{c.Taskvars, c.TaskfileVars, call.Vars, t.Vars} {
|
|
for i := 0; i < c.Expansions; i++ {
|
|
vr.merge(vars)
|
|
}
|
|
}
|
|
return vr.vars, vr.err
|
|
}
|
|
|
|
type varResolver struct {
|
|
c *CompilerV2
|
|
vars *taskfile.Vars
|
|
err error
|
|
}
|
|
|
|
func (vr *varResolver) merge(vars *taskfile.Vars) {
|
|
if vr.err != nil {
|
|
return
|
|
}
|
|
tr := templater.Templater{Vars: vr.vars}
|
|
vars.Range(func(k string, v taskfile.Var) error {
|
|
v = taskfile.Var{
|
|
Static: tr.Replace(v.Static),
|
|
Sh: tr.Replace(v.Sh),
|
|
}
|
|
static, err := vr.c.HandleDynamicVar(v)
|
|
if err != nil {
|
|
vr.err = err
|
|
return err
|
|
}
|
|
vr.vars.Set(k, taskfile.Var{Static: static})
|
|
return nil
|
|
})
|
|
vr.err = tr.Err()
|
|
}
|
|
|
|
func (c *CompilerV2) HandleDynamicVar(v taskfile.Var) (string, error) {
|
|
if v.Static != "" || v.Sh == "" {
|
|
return v.Static, nil
|
|
}
|
|
|
|
c.muDynamicCache.Lock()
|
|
defer c.muDynamicCache.Unlock()
|
|
|
|
if c.dynamicCache == nil {
|
|
c.dynamicCache = make(map[string]string, 30)
|
|
}
|
|
if result, ok := c.dynamicCache[v.Sh]; ok {
|
|
return result, nil
|
|
}
|
|
|
|
var stdout bytes.Buffer
|
|
opts := &execext.RunCommandOptions{
|
|
Command: v.Sh,
|
|
Dir: c.Dir,
|
|
Stdout: &stdout,
|
|
Stderr: c.Logger.Stderr,
|
|
}
|
|
if err := execext.RunCommand(context.Background(), opts); err != nil {
|
|
return "", fmt.Errorf(`task: Command "%s" in taskvars file failed: %s`, opts.Command, err)
|
|
}
|
|
|
|
// Trim a single trailing newline from the result to make most command
|
|
// output easier to use in shell commands.
|
|
result := strings.TrimSuffix(stdout.String(), "\n")
|
|
|
|
c.dynamicCache[v.Sh] = result
|
|
c.Logger.VerboseErrf(logger.Magenta, `task: dynamic variable: '%s' result: '%s'`, v.Sh, result)
|
|
|
|
return result, nil
|
|
}
|