-
-
Notifications
You must be signed in to change notification settings - Fork 699
Add Preconditions to Tasks #205
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
bd5882f
659cae6
044d3a0
12ab01d
d1463b3
cc92648
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,3 +21,6 @@ dist/ | |
|
||
# intellij idea/goland | ||
.idea/ | ||
|
||
# exuberant ctags | ||
tags |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
package taskfile | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
) | ||
|
||
var ( | ||
// ErrCantUnmarshalPrecondition is returned for invalid precond YAML. | ||
ErrCantUnmarshalPrecondition = errors.New("task: Can't unmarshal precondition value") | ||
) | ||
|
||
// Precondition represents a precondition necessary for a task to run | ||
type Precondition struct { | ||
Sh string | ||
Msg string | ||
} | ||
|
||
// UnmarshalYAML implements yaml.Unmarshaler interface. | ||
func (p *Precondition) UnmarshalYAML(unmarshal func(interface{}) error) error { | ||
var cmd string | ||
|
||
if err := unmarshal(&cmd); err == nil { | ||
p.Sh = cmd | ||
p.Msg = fmt.Sprintf("`%s` failed", cmd) | ||
return nil | ||
} | ||
|
||
var sh struct { | ||
Sh string | ||
Msg string | ||
} | ||
|
||
if err := unmarshal(&sh); err != nil { | ||
return err | ||
} | ||
|
||
p.Sh = sh.Sh | ||
p.Msg = sh.Msg | ||
if p.Msg == "" { | ||
p.Msg = fmt.Sprintf("%s failed", sh.Sh) | ||
} | ||
|
||
return nil | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
package taskfile_test | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/go-task/task/v2/internal/taskfile" | ||
|
||
"github.com/stretchr/testify/assert" | ||
"gopkg.in/yaml.v2" | ||
) | ||
|
||
func TestPreconditionParse(t *testing.T) { | ||
tests := []struct { | ||
content string | ||
v interface{} | ||
expected interface{} | ||
}{ | ||
{ | ||
"test -f foo.txt", | ||
&taskfile.Precondition{}, | ||
&taskfile.Precondition{Sh: `test -f foo.txt`, Msg: "`test -f foo.txt` failed"}, | ||
}, | ||
{ | ||
"sh: '[ 1 = 0 ]'", | ||
&taskfile.Precondition{}, | ||
&taskfile.Precondition{Sh: "[ 1 = 0 ]", Msg: "[ 1 = 0 ] failed"}, | ||
}, | ||
{` | ||
sh: "[ 1 = 2 ]" | ||
msg: "1 is not 2" | ||
`, | ||
&taskfile.Precondition{}, | ||
&taskfile.Precondition{Sh: "[ 1 = 2 ]", Msg: "1 is not 2"}, | ||
}, | ||
{` | ||
sh: "[ 1 = 2 ]" | ||
msg: "1 is not 2" | ||
`, | ||
&taskfile.Precondition{}, | ||
&taskfile.Precondition{Sh: "[ 1 = 2 ]", Msg: "1 is not 2"}, | ||
}, | ||
} | ||
for _, test := range tests { | ||
err := yaml.Unmarshal([]byte(test.content), test.v) | ||
assert.NoError(t, err) | ||
assert.Equal(t, test.expected, test.v) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
package task | ||
|
||
import ( | ||
"context" | ||
"errors" | ||
|
||
"github.com/go-task/task/v2/internal/execext" | ||
"github.com/go-task/task/v2/internal/taskfile" | ||
) | ||
|
||
var ( | ||
// ErrPreconditionFailed is returned when a precondition fails | ||
ErrPreconditionFailed = errors.New("task: precondition not met") | ||
) | ||
|
||
func (e *Executor) areTaskPreconditionsMet(ctx context.Context, t *taskfile.Task) (bool, error) { | ||
for _, p := range t.Preconditions { | ||
err := execext.RunCommand(ctx, &execext.RunCommandOptions{ | ||
Command: p.Sh, | ||
Dir: t.Dir, | ||
Env: getEnviron(t), | ||
}) | ||
|
||
if err != nil { | ||
e.Logger.Errf("task: %s", p.Msg) | ||
return false, ErrPreconditionFailed | ||
} | ||
} | ||
|
||
return true, nil | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -119,16 +119,17 @@ func (e *Executor) Setup() error { | |
Vars: e.taskvars, | ||
Logger: e.Logger, | ||
} | ||
case version.IsV2(v), version.IsV21(v), version.IsV22(v): | ||
case version.IsV2(v), version.IsV21(v), version.IsV22(v), version.IsV23(v): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @andreynering Should this be changed to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. minimal repro:
This seems like a bug to me in that '2.3' is obviously > 2.1 - but maybe the version checking is only against the major version? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @stephenprater Indeed, the version code needs some love. In theory this should actually be: case version.IsV2(v), version.IsV21(v), version.IsV22(v), version.IsV23(v), version.IsV24(v), version.IsV25(v), version.IsV26(v): But there's likely a way to create a specific constraint for I'd say, let this as is, and I'll add a reminder to fix this once I merge this PR to master. 🙂 |
||
e.Compiler = &compilerv2.CompilerV2{ | ||
Dir: e.Dir, | ||
Taskvars: e.taskvars, | ||
TaskfileVars: e.Taskfile.Vars, | ||
Expansions: e.Taskfile.Expansions, | ||
Logger: e.Logger, | ||
} | ||
case version.IsV23(v): | ||
return fmt.Errorf(`task: Taskfile versions greater than v2.3 not implemented in the version of Task`) | ||
|
||
case version.IsV24(v): | ||
return fmt.Errorf(`task: Taskfile versions greater than v2.4 not implemented in the version of Task`) | ||
} | ||
|
||
if !version.IsV21(v) && e.Taskfile.Output != "" { | ||
|
@@ -188,11 +189,17 @@ func (e *Executor) RunTask(ctx context.Context, call taskfile.Call) error { | |
} | ||
|
||
if !e.Force { | ||
preCondMet, err := e.areTaskPreconditionsMet(ctx, t) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
upToDate, err := e.isTaskUpToDate(ctx, t) | ||
if err != nil { | ||
return err | ||
} | ||
if upToDate { | ||
|
||
if upToDate && preCondMet { | ||
if !e.Silent { | ||
e.Logger.Errf(`task: Task "%s" is up to date`, t.Task) | ||
} | ||
|
@@ -224,7 +231,11 @@ func (e *Executor) runDeps(ctx context.Context, t *taskfile.Task) error { | |
d := d | ||
|
||
g.Go(func() error { | ||
return e.RunTask(ctx, taskfile.Call{Task: d.Task, Vars: d.Vars}) | ||
err := e.RunTask(ctx, taskfile.Call{Task: d.Task, Vars: d.Vars}) | ||
if err != nil { | ||
return err | ||
} | ||
return nil | ||
}) | ||
} | ||
|
||
|
@@ -236,7 +247,11 @@ func (e *Executor) runCommand(ctx context.Context, t *taskfile.Task, call taskfi | |
|
||
switch { | ||
case cmd.Task != "": | ||
return e.RunTask(ctx, taskfile.Call{Task: cmd.Task, Vars: cmd.Vars}) | ||
err := e.RunTask(ctx, taskfile.Call{Task: cmd.Task, Vars: cmd.Vars}) | ||
if err != nil { | ||
return err | ||
} | ||
return nil | ||
case cmd.Cmd != "": | ||
if e.Verbose || (!cmd.Silent && !t.Silent && !e.Silent) { | ||
e.Logger.Errf(cmd.Cmd) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@andreynering. please correct me if I am wrong.
2.4 and 2.5 have been released, so when adding a new feature to the taskfile here, it should probably come as a 2.6 version.
Since there are not checks for 2.4 and 2.5 today, I presume these two releases does not provide any updated to the Taskfile format, and does not need the
v2X
andIsv2X()
definitions?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I have forgot to bump these version checks. Let's keep these new variables, and I plan to fix the checks for for the [few] changes we had recently.
This new change should be guarded on v2.6 (the next release), though.