1
0
mirror of https://github.com/go-task/task.git synced 2025-01-20 04:59:37 +02:00

Create v3 compiler which respects declaration order of variables

Also, fix "<no value>" been printed when a non-existing variable is printed.
This commit is contained in:
Andrey Nering 2020-05-16 15:45:41 -03:00
parent 4913b6a0f1
commit 68ce8642b1
31 changed files with 225 additions and 35 deletions

View File

@ -0,0 +1,98 @@
package v3
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 = &CompilerV3{}
type CompilerV3 struct {
Dir string
TaskfileVars *taskfile.Vars
Logger *logger.Logger
dynamicCache map[string]string
muDynamicCache sync.Mutex
}
func (c *CompilerV3) GetVariables(t *taskfile.Task, call taskfile.Call) (*taskfile.Vars, error) {
result := compiler.GetEnviron()
result.Set("TASK", taskfile.Var{Static: t.Task})
rangeFunc := func(k string, v taskfile.Var) error {
tr := templater.Templater{Vars: result, RemoveNoValue: true}
v = taskfile.Var{
Static: tr.Replace(v.Static),
Sh: tr.Replace(v.Sh),
}
if err := tr.Err(); err != nil {
return err
}
static, err := c.HandleDynamicVar(v)
if err != nil {
return err
}
result.Set(k, taskfile.Var{Static: static})
return nil
}
if err := c.TaskfileVars.Range(rangeFunc); err != nil {
return nil, err
}
if err := call.Vars.Range(rangeFunc); err != nil {
return nil, err
}
if err := t.Vars.Range(rangeFunc); err != nil {
return nil, err
}
return result, nil
}
func (c *CompilerV3) 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
}

View File

@ -2,6 +2,7 @@ package templater
import (
"bytes"
"strings"
"text/template"
"github.com/go-task/task/v2/internal/taskfile"
@ -12,7 +13,8 @@ import (
// happen will be assigned to r.err, and consecutive calls to funcs will just
// return the zero value.
type Templater struct {
Vars *taskfile.Vars
Vars *taskfile.Vars
RemoveNoValue bool
cacheMap map[string]interface{}
err error
@ -42,6 +44,9 @@ func (r *Templater) Replace(str string) string {
r.err = err
return ""
}
if r.RemoveNoValue {
return strings.ReplaceAll(b.String(), "<no value>", "")
}
return b.String()
}

33
task.go
View File

@ -12,6 +12,7 @@ import (
"github.com/go-task/task/v2/internal/compiler"
compilerv2 "github.com/go-task/task/v2/internal/compiler/v2"
compilerv3 "github.com/go-task/task/v2/internal/compiler/v3"
"github.com/go-task/task/v2/internal/execext"
"github.com/go-task/task/v2/internal/logger"
"github.com/go-task/task/v2/internal/output"
@ -131,9 +132,9 @@ func (e *Executor) Setup() error {
Color: e.Color,
}
v, err := strconv.ParseFloat(e.Taskfile.Version, 64)
v, err := e.parsedVersion()
if err != nil {
return fmt.Errorf(`task: Could not parse taskfile version "%s": %v`, e.Taskfile.Version, err)
}
if v < 2 {
@ -154,12 +155,20 @@ func (e *Executor) Setup() error {
e.Logger.Color = false
}
e.Compiler = &compilerv2.CompilerV2{
Dir: e.Dir,
Taskvars: e.taskvars,
TaskfileVars: e.Taskfile.Vars,
Expansions: e.Taskfile.Expansions,
Logger: e.Logger,
if v < 3 {
e.Compiler = &compilerv2.CompilerV2{
Dir: e.Dir,
Taskvars: e.taskvars,
TaskfileVars: e.Taskfile.Vars,
Expansions: e.Taskfile.Expansions,
Logger: e.Logger,
}
} else {
e.Compiler = &compilerv3.CompilerV3{
Dir: e.Dir,
TaskfileVars: e.Taskfile.Vars,
Logger: e.Logger,
}
}
if v < 2.1 && e.Taskfile.Output != "" {
@ -231,6 +240,14 @@ func (e *Executor) Setup() error {
return nil
}
func (e *Executor) parsedVersion() (float64, error) {
v, err := strconv.ParseFloat(e.Taskfile.Version, 64)
if err != nil {
return 0, fmt.Errorf(`task: Could not parse taskfile version "%s": %v`, e.Taskfile.Version, err)
}
return v, nil
}
// RunTask runs a task by its name
func (e *Executor) RunTask(ctx context.Context, call taskfile.Call) error {
t, err := e.CompiledTask(call)

View File

@ -107,6 +107,20 @@ func TestVarsV2(t *testing.T) {
tt.Run(t)
}
func TestVarsV3(t *testing.T) {
tt := fileContentTest{
Dir: "testdata/vars/v3",
Target: "default",
Files: map[string]string{
"missing-var.txt": "\n",
"var-order.txt": "ABCDEF\n",
"dependent-sh.txt": "123456\n",
"with-call.txt": "Hi, ABC123!\n",
},
}
tt.Run(t)
}
func TestMultilineVars(t *testing.T) {
for _, dir := range []string{"testdata/vars/v2/multiline"} {
tt := fileContentTest{

View File

@ -1,4 +1,4 @@
version: '2'
version: '3'
tasks:
build:

View File

@ -1,4 +1,4 @@
version: '2'
version: '3'
tasks:
task-1:

View File

@ -1,4 +1,4 @@
version: '2'
version: '3'
tasks:
default:

View File

@ -1,4 +1,4 @@
version: '2'
version: '3'
tasks:
whereami:

View File

@ -1,4 +1,4 @@
version: '2'
version: '3'
tasks:
whereami:

View File

@ -1,4 +1,4 @@
version: '2'
version: '3'
tasks:
whereami:

View File

@ -1,4 +1,4 @@
version: '2'
version: '3'
tasks:
build:

View File

@ -1,4 +1,4 @@
version: '2'
version: '3'
tasks:
default:

View File

@ -1,4 +1,4 @@
version: '2'
version: '3'
vars:
BAZ:

View File

@ -1,4 +1,4 @@
version: '2'
version: '3'
tasks:
pwd:

View File

@ -1,4 +1,7 @@
version: '2'
version: '3'
vars:
BUILD_DIR: $pwd
tasks:
abs.txt:

View File

@ -1 +0,0 @@
BUILD_DIR: $pwd

View File

@ -1,4 +1,4 @@
version: '2'
version: '3'
tasks:
task-should-pass:

View File

@ -1,4 +1,4 @@
version: '2'
version: '3'
includes:
included: Taskfile2.yml

View File

@ -1,4 +1,4 @@
version: '2'
version: '3'
tasks:
call-root:

View File

@ -1,4 +1,4 @@
version: '2'
version: '3'
includes:
included: Taskfile2.yml

View File

@ -1,4 +1,4 @@
version: '2'
version: '3'
tasks:
default:

View File

@ -1,4 +1,4 @@
version: '2'
version: '3'
includes:
included: Taskfile2.yml

View File

@ -1,4 +1,4 @@
version: '2'
version: '3'
vars:
FILE: file.txt

View File

@ -1,4 +1,8 @@
version: '2'
version: '3'
vars:
PORTUGUESE_HELLO_WORLD: Olá, mundo!
GERMAN: Hello
tasks:
default:

View File

@ -1,2 +0,0 @@
PORTUGUESE_HELLO_WORLD: Olá, mundo!
GERMAN: "Hello"

View File

@ -1,4 +1,4 @@
version: '2'
version: '3'
tasks:
foo:

View File

@ -1,4 +1,4 @@
version: '2'
version: '3'
tasks:
gen-foo:

View File

@ -1,4 +1,4 @@
version: '2'
version: '3'
tasks:
task-with-summary:

1
testdata/vars/v3/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
*.txt

46
testdata/vars/v3/Taskfile.yml vendored Normal file
View File

@ -0,0 +1,46 @@
version: '3'
vars:
VAR_A: A
VAR_B: '{{.VAR_A}}B'
VAR_C: '{{.VAR_B}}C'
VAR_1: {sh: echo 1}
VAR_2: {sh: 'echo "{{.VAR_1}}2"'}
VAR_3: {sh: 'echo "{{.VAR_2}}3"'}
tasks:
default:
- task: missing-var
- task: var-order
- task: dependent-sh
- task: with-call
missing-var: echo '{{.NON_EXISTING_VAR}}' > missing-var.txt
var-order:
vars:
VAR_D: '{{.VAR_C}}D'
VAR_E: '{{.VAR_D}}E'
VAR_F: '{{.VAR_E}}F'
cmds:
- echo '{{.VAR_F}}' > var-order.txt
dependent-sh:
vars:
VAR_4: {sh: 'echo "{{.VAR_3}}4"'}
VAR_5: {sh: 'echo "{{.VAR_4}}5"'}
VAR_6: {sh: 'echo "{{.VAR_5}}6"'}
cmds:
- echo '{{.VAR_6}}' > dependent-sh.txt
with-call:
- task: called-task
vars:
ABC123: '{{.VAR_C}}{{.VAR_3}}'
called-task:
vars:
MESSAGE: Hi, {{.ABC123}}!
cmds:
- echo "{{.MESSAGE}}" > with-call.txt

View File

@ -23,7 +23,12 @@ func (e *Executor) CompiledTask(call taskfile.Call) (*taskfile.Task, error) {
return nil, err
}
r := templater.Templater{Vars: vars}
v, err := e.parsedVersion()
if err != nil {
return nil, err
}
r := templater.Templater{Vars: vars, RemoveNoValue: v >= 3.0}
new := taskfile.Task{
Task: origTask.Task,