Merge branch 'Eun-master'

This commit is contained in:
Andrey Nering
2018-08-05 13:02:22 -03:00
7 changed files with 122 additions and 37 deletions

View File

@@ -640,6 +640,39 @@ tasks:
Dry run mode (`--dry`) compiles and steps through each task, printing the commands Dry run mode (`--dry`) compiles and steps through each task, printing the commands
that would be run without executing them. This is useful for debugging your Taskfiles. that would be run without executing them. This is useful for debugging your Taskfiles.
## Ignore errors
You have the option to ignore errors during command execution.
Given the following Taskfile:
```yml
version: '2'
tasks:
echo:
cmds:
- exit 1
- echo "Hello World"
```
Task will abort the execution after running `exit 1` because the status code `1` stands for `EXIT_FAILURE`.
However it is possible to continue with execution using `ignore_error`:
```yml
version: '2'
tasks:
echo:
cmds:
- cmd: exit 1
ignore_error: true
- echo "Hello World"
```
`ignore_error` can also be set for a task, which mean errors will be supressed
for all commands. But keep in mind this option won't propagate to other tasks
called either by `deps` or `cmds`!
## Output syntax ## Output syntax
By default, Task just redirect the STDOUT and STDERR of the running commands By default, Task just redirect the STDOUT and STDERR of the running commands

View File

@@ -7,10 +7,11 @@ import (
// Cmd is a task command // Cmd is a task command
type Cmd struct { type Cmd struct {
Cmd string Cmd string
Silent bool Silent bool
Task string Task string
Vars Vars Vars Vars
IgnoreError bool
} }
// Dep is a task dependency // Dep is a task dependency
@@ -38,12 +39,14 @@ func (c *Cmd) UnmarshalYAML(unmarshal func(interface{}) error) error {
return nil return nil
} }
var cmdStruct struct { var cmdStruct struct {
Cmd string Cmd string
Silent bool Silent bool
IgnoreError bool `yaml:"ignore_error"`
} }
if err := unmarshal(&cmdStruct); err == nil && cmdStruct.Cmd != "" { if err := unmarshal(&cmdStruct); err == nil && cmdStruct.Cmd != "" {
c.Cmd = cmdStruct.Cmd c.Cmd = cmdStruct.Cmd
c.Silent = cmdStruct.Silent c.Silent = cmdStruct.Silent
c.IgnoreError = cmdStruct.IgnoreError
return nil return nil
} }
var taskCall struct { var taskCall struct {

View File

@@ -5,17 +5,18 @@ type Tasks map[string]*Task
// Task represents a task // Task represents a task
type Task struct { type Task struct {
Task string Task string
Cmds []*Cmd Cmds []*Cmd
Deps []*Dep Deps []*Dep
Desc string Desc string
Sources []string Sources []string
Generates []string Generates []string
Status []string Status []string
Dir string Dir string
Vars Vars Vars Vars
Env Vars Env Vars
Silent bool Silent bool
Method string Method string
Prefix string Prefix string
IgnoreError bool `yaml:"ignore_error"`
} }

14
task.go
View File

@@ -19,6 +19,7 @@ import (
"github.com/Masterminds/semver" "github.com/Masterminds/semver"
"golang.org/x/sync/errgroup" "golang.org/x/sync/errgroup"
"mvdan.cc/sh/interp"
) )
const ( const (
@@ -181,6 +182,12 @@ func (e *Executor) RunTask(ctx context.Context, call taskfile.Call) error {
if err2 := statusOnError(t); err2 != nil { if err2 := statusOnError(t); err2 != nil {
e.Logger.VerboseErrf("task: error cleaning status on error: %v", err2) e.Logger.VerboseErrf("task: error cleaning status on error: %v", err2)
} }
if _, ok := err.(interp.ExitCode); ok && t.IgnoreError {
e.Logger.VerboseErrf("task: task error ignored: %v", err)
continue
}
return &taskRunError{t.Task, err} return &taskRunError{t.Task, err}
} }
} }
@@ -221,7 +228,7 @@ func (e *Executor) runCommand(ctx context.Context, t *taskfile.Task, call taskfi
defer stdOut.Close() defer stdOut.Close()
defer stdErr.Close() defer stdErr.Close()
return execext.RunCommand(&execext.RunCommandOptions{ err := execext.RunCommand(&execext.RunCommandOptions{
Context: ctx, Context: ctx,
Command: cmd.Cmd, Command: cmd.Cmd,
Dir: t.Dir, Dir: t.Dir,
@@ -230,6 +237,11 @@ func (e *Executor) runCommand(ctx context.Context, t *taskfile.Task, call taskfi
Stdout: stdOut, Stdout: stdOut,
Stderr: stdErr, Stderr: stdErr,
}) })
if _, ok := err.(interp.ExitCode); ok && cmd.IgnoreError {
e.Logger.VerboseErrf("task: command error ignored: %v", err)
return nil
}
return err
default: default:
return nil return nil
} }

View File

@@ -53,7 +53,6 @@ func (fct fileContentTest) Run(t *testing.T) {
assert.Equal(t, expectContent, s, "unexpected file content") assert.Equal(t, expectContent, s, "unexpected file content")
}) })
} }
} }
func TestEnv(t *testing.T) { func TestEnv(t *testing.T) {
@@ -414,6 +413,22 @@ func TestTaskVersion(t *testing.T) {
} }
} }
func TestTaskIgnoreErrors(t *testing.T) {
const dir = "testdata/ignore_errors"
e := task.Executor{
Dir: dir,
Stdout: ioutil.Discard,
Stderr: ioutil.Discard,
}
assert.NoError(t, e.Setup())
assert.NoError(t, e.Run(taskfile.Call{Task: "task-should-pass"}))
assert.Error(t, e.Run(taskfile.Call{Task: "task-should-fail"}))
assert.NoError(t, e.Run(taskfile.Call{Task: "cmd-should-pass"}))
assert.Error(t, e.Run(taskfile.Call{Task: "cmd-should-fail"}))
}
func TestExpand(t *testing.T) { func TestExpand(t *testing.T) {
const dir = "testdata/expand" const dir = "testdata/expand"

20
testdata/ignore_errors/Taskfile.yml vendored Normal file
View File

@@ -0,0 +1,20 @@
version: '2'
tasks:
task-should-pass:
cmds:
- exit 1
ignore_error: true
task-should-fail:
cmds:
- exit 1
cmd-should-pass:
cmds:
- cmd: exit 1
ignore_error: true
cmd-should-fail:
cmds:
- cmd: exit 1

View File

@@ -24,17 +24,18 @@ func (e *Executor) CompiledTask(call taskfile.Call) (*taskfile.Task, error) {
r := templater.Templater{Vars: vars} r := templater.Templater{Vars: vars}
new := taskfile.Task{ new := taskfile.Task{
Task: origTask.Task, Task: origTask.Task,
Desc: r.Replace(origTask.Desc), Desc: r.Replace(origTask.Desc),
Sources: r.ReplaceSlice(origTask.Sources), Sources: r.ReplaceSlice(origTask.Sources),
Generates: r.ReplaceSlice(origTask.Generates), Generates: r.ReplaceSlice(origTask.Generates),
Status: r.ReplaceSlice(origTask.Status), Status: r.ReplaceSlice(origTask.Status),
Dir: r.Replace(origTask.Dir), Dir: r.Replace(origTask.Dir),
Vars: nil, Vars: nil,
Env: r.ReplaceVars(origTask.Env), Env: r.ReplaceVars(origTask.Env),
Silent: origTask.Silent, Silent: origTask.Silent,
Method: r.Replace(origTask.Method), Method: r.Replace(origTask.Method),
Prefix: r.Replace(origTask.Prefix), Prefix: r.Replace(origTask.Prefix),
IgnoreError: origTask.IgnoreError,
} }
new.Dir, err = shell.Expand(new.Dir, nil) new.Dir, err = shell.Expand(new.Dir, nil)
if err != nil { if err != nil {
@@ -58,12 +59,12 @@ func (e *Executor) CompiledTask(call taskfile.Call) (*taskfile.Task, error) {
new.Cmds = make([]*taskfile.Cmd, len(origTask.Cmds)) new.Cmds = make([]*taskfile.Cmd, len(origTask.Cmds))
for i, cmd := range origTask.Cmds { for i, cmd := range origTask.Cmds {
new.Cmds[i] = &taskfile.Cmd{ new.Cmds[i] = &taskfile.Cmd{
Task: r.Replace(cmd.Task), Task: r.Replace(cmd.Task),
Silent: cmd.Silent, Silent: cmd.Silent,
Cmd: r.Replace(cmd.Cmd), Cmd: r.Replace(cmd.Cmd),
Vars: r.ReplaceVars(cmd.Vars), Vars: r.ReplaceVars(cmd.Vars),
IgnoreError: cmd.IgnoreError,
} }
} }
} }
if len(origTask.Deps) > 0 { if len(origTask.Deps) > 0 {