mirror of
https://github.com/go-task/task.git
synced 2026-07-09 06:55:14 +00:00
Merge branch 'Eun-master'
This commit is contained in:
33
README.md
33
README.md
@@ -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
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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
14
task.go
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
17
task_test.go
17
task_test.go
@@ -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
20
testdata/ignore_errors/Taskfile.yml
vendored
Normal 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
|
||||||
33
variables.go
33
variables.go
@@ -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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user