mirror of
https://github.com/go-task/task.git
synced 2025-04-21 12:17:07 +02:00
Merge pull request #755 from BrunoDelor/feature/carry-error-code
Add --exit-code (-x) flag to enable carrying error codes from task cmds
This commit is contained in:
commit
63aad1e501
@ -68,6 +68,7 @@ func main() {
|
|||||||
silent bool
|
silent bool
|
||||||
dry bool
|
dry bool
|
||||||
summary bool
|
summary bool
|
||||||
|
exitCode bool
|
||||||
parallel bool
|
parallel bool
|
||||||
concurrency int
|
concurrency int
|
||||||
dir string
|
dir string
|
||||||
@ -89,6 +90,7 @@ func main() {
|
|||||||
pflag.BoolVarP(¶llel, "parallel", "p", false, "executes tasks provided on command line in parallel")
|
pflag.BoolVarP(¶llel, "parallel", "p", false, "executes tasks provided on command line in parallel")
|
||||||
pflag.BoolVar(&dry, "dry", false, "compiles and prints tasks in the order that they would be run, without executing them")
|
pflag.BoolVar(&dry, "dry", false, "compiles and prints tasks in the order that they would be run, without executing them")
|
||||||
pflag.BoolVar(&summary, "summary", false, "show summary about a task")
|
pflag.BoolVar(&summary, "summary", false, "show summary about a task")
|
||||||
|
pflag.BoolVarP(&exitCode, "exit-code", "x", false, "pass-through the exit code of the task command")
|
||||||
pflag.StringVarP(&dir, "dir", "d", "", "sets directory of execution")
|
pflag.StringVarP(&dir, "dir", "d", "", "sets directory of execution")
|
||||||
pflag.StringVarP(&entrypoint, "taskfile", "t", "", `choose which Taskfile to run. Defaults to "Taskfile.yml"`)
|
pflag.StringVarP(&entrypoint, "taskfile", "t", "", `choose which Taskfile to run. Defaults to "Taskfile.yml"`)
|
||||||
pflag.StringVarP(&output.Name, "output", "o", "", "sets output style: [interleaved|group|prefixed]")
|
pflag.StringVarP(&output.Name, "output", "o", "", "sets output style: [interleaved|group|prefixed]")
|
||||||
@ -216,7 +218,13 @@ func main() {
|
|||||||
|
|
||||||
if err := e.Run(ctx, calls...); err != nil {
|
if err := e.Run(ctx, calls...); err != nil {
|
||||||
e.Logger.Errf(logger.Red, "%v", err)
|
e.Logger.Errf(logger.Red, "%v", err)
|
||||||
os.Exit(1)
|
code := 1
|
||||||
|
if exitCode {
|
||||||
|
if tre, ok := err.(*task.TaskRunError); ok {
|
||||||
|
code = tre.ExitCode()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
os.Exit(code)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
13
errors.go
13
errors.go
@ -3,6 +3,7 @@ package task
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"mvdan.cc/sh/v3/interp"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -18,15 +19,23 @@ func (err *taskNotFoundError) Error() string {
|
|||||||
return fmt.Sprintf(`task: Task "%s" not found`, err.taskName)
|
return fmt.Sprintf(`task: Task "%s" not found`, err.taskName)
|
||||||
}
|
}
|
||||||
|
|
||||||
type taskRunError struct {
|
type TaskRunError struct {
|
||||||
taskName string
|
taskName string
|
||||||
err error
|
err error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (err *taskRunError) Error() string {
|
func (err *TaskRunError) Error() string {
|
||||||
return fmt.Sprintf(`task: Failed to run task "%s": %v`, err.taskName, err.err)
|
return fmt.Sprintf(`task: Failed to run task "%s": %v`, err.taskName, err.err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (err *TaskRunError) ExitCode() int {
|
||||||
|
if c, ok := interp.IsExitStatus(err.err); ok {
|
||||||
|
return int(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
// MaximumTaskCallExceededError is returned when a task is called too
|
// MaximumTaskCallExceededError is returned when a task is called too
|
||||||
// many times. In this case you probably have a cyclic dependendy or
|
// many times. In this case you probably have a cyclic dependendy or
|
||||||
// infinite loop
|
// infinite loop
|
||||||
|
2
task.go
2
task.go
@ -363,7 +363,7 @@ func (e *Executor) RunTask(ctx context.Context, call taskfile.Call) error {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
return &taskRunError{t.Task, err}
|
return &TaskRunError{t.Task, err}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
e.Logger.VerboseErrf(logger.Magenta, `task: "%s" finished`, call.Task)
|
e.Logger.VerboseErrf(logger.Magenta, `task: "%s" finished`, call.Task)
|
||||||
|
19
task_test.go
19
task_test.go
@ -1275,3 +1275,22 @@ VAR_2 is included-default-var2
|
|||||||
t.Log(buff.String())
|
t.Log(buff.String())
|
||||||
assert.Equal(t, strings.TrimSpace(buff.String()), expectedOutputOrder)
|
assert.Equal(t, strings.TrimSpace(buff.String()), expectedOutputOrder)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestErrorCode(t *testing.T) {
|
||||||
|
const dir = "testdata/error_code"
|
||||||
|
|
||||||
|
var buff bytes.Buffer
|
||||||
|
e := &task.Executor{
|
||||||
|
Dir: dir,
|
||||||
|
Stdout: &buff,
|
||||||
|
Stderr: &buff,
|
||||||
|
Silent: true,
|
||||||
|
}
|
||||||
|
assert.NoError(t, e.Setup())
|
||||||
|
|
||||||
|
err := e.Run(context.Background(), taskfile.Call{Task: "test-exit-code"})
|
||||||
|
assert.Error(t, err)
|
||||||
|
casted, ok := err.(*task.TaskRunError)
|
||||||
|
assert.True(t, ok, "cannot cast returned error to *task.TaskRunError")
|
||||||
|
assert.Equal(t, 42, casted.ExitCode(), "unexpected exit code from task")
|
||||||
|
}
|
||||||
|
6
testdata/error_code/Taskfile.yml
vendored
Normal file
6
testdata/error_code/Taskfile.yml
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
version: '3'
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
test-exit-code:
|
||||||
|
cmds:
|
||||||
|
- exit 42
|
2
watch.go
2
watch.go
@ -88,7 +88,7 @@ func (e *Executor) watchTasks(calls ...taskfile.Call) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func isContextError(err error) bool {
|
func isContextError(err error) bool {
|
||||||
if taskRunErr, ok := err.(*taskRunError); ok {
|
if taskRunErr, ok := err.(*TaskRunError); ok {
|
||||||
err = taskRunErr.err
|
err = taskRunErr.err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user