mirror of
				https://github.com/go-task/task.git
				synced 2025-10-30 23:58:01 +02:00 
			
		
		
		
	feat: custom error codes (#1114)
This commit is contained in:
		| @@ -9,10 +9,12 @@ | ||||
|   #1107 by @danquah). | ||||
| - Add `.hg` (Mercurial) to the list of ignored directories when using `--watch` | ||||
|   (#1098 by @misery). | ||||
| - More improvements to the release tool (#1096 by @pd93) | ||||
| - More improvements to the release tool (#1096 by @pd93). | ||||
| - Enforce [gofumpt](https://github.com/mvdan/gofumpt) linter (#1099 by @pd93) | ||||
| - Add `--sort` flag for use with `--list` and `--list-all` (#946, #1105 by | ||||
|   @pd93) | ||||
|   @pd93). | ||||
| - Task now has [custom exit codes](https://taskfile.dev/api/#exit-codes) | ||||
|   depending on the error (#1114 by @pd93). | ||||
|  | ||||
| ## v3.23.0 - 2023-03-26 | ||||
|  | ||||
|   | ||||
| @@ -14,6 +14,7 @@ import ( | ||||
|  | ||||
| 	"github.com/go-task/task/v3" | ||||
| 	"github.com/go-task/task/v3/args" | ||||
| 	"github.com/go-task/task/v3/errors" | ||||
| 	"github.com/go-task/task/v3/internal/logger" | ||||
| 	"github.com/go-task/task/v3/internal/sort" | ||||
| 	ver "github.com/go-task/task/v3/internal/version" | ||||
| @@ -43,6 +44,17 @@ Options: | ||||
| ` | ||||
|  | ||||
| func main() { | ||||
| 	if err := run(); err != nil { | ||||
| 		if err, ok := err.(errors.TaskError); ok { | ||||
| 			log.Print(err.Error()) | ||||
| 			os.Exit(err.Code()) | ||||
| 		} | ||||
| 		os.Exit(errors.CodeUnknown) | ||||
| 	} | ||||
| 	os.Exit(errors.CodeOk) | ||||
| } | ||||
|  | ||||
| func run() error { | ||||
| 	log.SetFlags(0) | ||||
| 	log.SetOutput(os.Stderr) | ||||
|  | ||||
| @@ -107,12 +119,12 @@ func main() { | ||||
|  | ||||
| 	if versionFlag { | ||||
| 		fmt.Printf("Task version: %s\n", ver.GetVersion()) | ||||
| 		return | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	if helpFlag { | ||||
| 		pflag.Usage() | ||||
| 		return | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	if init { | ||||
| @@ -123,25 +135,23 @@ func main() { | ||||
| 		if err := task.InitTaskfile(os.Stdout, wd); err != nil { | ||||
| 			log.Fatal(err) | ||||
| 		} | ||||
| 		return | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	if global && dir != "" { | ||||
| 		log.Fatal("task: You can't set both --global and --dir") | ||||
| 		return | ||||
| 		return nil | ||||
| 	} | ||||
| 	if global { | ||||
| 		home, err := os.UserHomeDir() | ||||
| 		if err != nil { | ||||
| 			log.Fatal("task: Failed to get user home directory: %w", err) | ||||
| 			return | ||||
| 			return fmt.Errorf("task: Failed to get user home directory: %w", err) | ||||
| 		} | ||||
| 		dir = home | ||||
| 	} | ||||
|  | ||||
| 	if dir != "" && entrypoint != "" { | ||||
| 		log.Fatal("task: You can't set both --dir and --taskfile") | ||||
| 		return | ||||
| 		return errors.New("task: You can't set both --dir and --taskfile") | ||||
| 	} | ||||
| 	if entrypoint != "" { | ||||
| 		dir = filepath.Dir(entrypoint) | ||||
| @@ -150,16 +160,13 @@ func main() { | ||||
|  | ||||
| 	if output.Name != "group" { | ||||
| 		if output.Group.Begin != "" { | ||||
| 			log.Fatal("task: You can't set --output-group-begin without --output=group") | ||||
| 			return | ||||
| 			return errors.New("task: You can't set --output-group-begin without --output=group") | ||||
| 		} | ||||
| 		if output.Group.End != "" { | ||||
| 			log.Fatal("task: You can't set --output-group-end without --output=group") | ||||
| 			return | ||||
| 			return errors.New("task: You can't set --output-group-end without --output=group") | ||||
| 		} | ||||
| 		if output.Group.ErrorOnly { | ||||
| 			log.Fatal("task: You can't set --output-group-error-only without --output=group") | ||||
| 			return | ||||
| 			return errors.New("task: You can't set --output-group-error-only without --output=group") | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| @@ -195,23 +202,27 @@ func main() { | ||||
|  | ||||
| 	listOptions := task.NewListOptions(list, listAll, listJson) | ||||
| 	if err := listOptions.Validate(); err != nil { | ||||
| 		log.Fatal(err) | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	if (listOptions.ShouldListTasks()) && silent { | ||||
| 		e.ListTaskNames(listAll) | ||||
| 		return | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	if err := e.Setup(); err != nil { | ||||
| 		log.Fatal(err) | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	if listOptions.ShouldListTasks() { | ||||
| 		if foundTasks, err := e.ListTasks(listOptions); !foundTasks || err != nil { | ||||
| 			os.Exit(1) | ||||
| 		foundTasks, err := e.ListTasks(listOptions) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		return | ||||
| 		if !foundTasks { | ||||
| 			os.Exit(errors.CodeUnknown) | ||||
| 		} | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	var ( | ||||
| @@ -221,7 +232,7 @@ func main() { | ||||
|  | ||||
| 	tasksAndVars, cliArgs, err := getArgs() | ||||
| 	if err != nil { | ||||
| 		log.Fatal(err) | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	if e.Taskfile.Version.Compare(taskfile.V3) >= 0 { | ||||
| @@ -240,22 +251,20 @@ func main() { | ||||
| 	ctx := context.Background() | ||||
|  | ||||
| 	if status { | ||||
| 		if err := e.Status(ctx, calls...); err != nil { | ||||
| 			log.Fatal(err) | ||||
| 		} | ||||
| 		return | ||||
| 		return e.Status(ctx, calls...) | ||||
| 	} | ||||
|  | ||||
| 	if err := e.Run(ctx, calls...); err != nil { | ||||
| 		e.Logger.Errf(logger.Red, "%v", err) | ||||
|  | ||||
| 		if exitCode { | ||||
| 			if err, ok := err.(*task.TaskRunError); ok { | ||||
| 				os.Exit(err.ExitCode()) | ||||
| 			if err, ok := err.(*errors.TaskRunError); ok { | ||||
| 				os.Exit(err.TaskExitCode()) | ||||
| 			} | ||||
| 		} | ||||
| 		os.Exit(1) | ||||
| 		return err | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func getArgs() ([]string, string, error) { | ||||
|   | ||||
| @@ -51,6 +51,35 @@ If `--` is given, all remaning arguments will be assigned to a special | ||||
| |       | `--version`                 | `bool`   | `false`                                      | Show Task version.                                                                                                                                                     | | ||||
| | `-w`  | `--watch`                   | `bool`   | `false`                                      | Enables watch of the given task.                                                                                                                                       | | ||||
|  | ||||
| ## Exit Codes | ||||
|  | ||||
| Task will sometimes exit with specific exit codes. These codes are split into three groups with the following ranges: | ||||
|  | ||||
| - General errors (0-99) | ||||
| - Taskfile errors (100-199) | ||||
| - Task errors (200-299) | ||||
|  | ||||
| A full list of the exit codes and their descriptions can be found below: | ||||
|  | ||||
| | Code | Description                                                  | | ||||
| | ---- | ------------------------------------------------------------ | | ||||
| | 0    | Success                                                      | | ||||
| | 1    | An unknown error occurred                                    | | ||||
| | 100  | No Taskfile was found                                        | | ||||
| | 101  | A Taskfile already exists when trying to initialize one      | | ||||
| | 102  | The Taskfile is invalid or cannot be parsed                  | | ||||
| | 200  | The specified task could not be found                        | | ||||
| | 201  | An error occurred while executing a command inside of a task | | ||||
| | 202  | The user tried to invoke a task that is internal             | | ||||
| | 203  | There a multiple tasks with the same name or alias           | | ||||
| | 204  | A task was called too many times                             | | ||||
|  | ||||
| These codes can also be found in the repository in [`errors/errors.go`](https://github.com/go-task/task/blob/master/errors/errors.go). | ||||
|  | ||||
| :::info | ||||
| When Task is run with the `-x`/`--exit-code` flag, the exit code of any failed commands will be passed through to the user instead. | ||||
| ::: | ||||
|  | ||||
| ## JSON Output | ||||
|  | ||||
| When using the `--json` flag in combination with either the `--list` or | ||||
|   | ||||
							
								
								
									
										78
									
								
								errors.go
									
									
									
									
									
								
							
							
						
						
									
										78
									
								
								errors.go
									
									
									
									
									
								
							| @@ -1,78 +0,0 @@ | ||||
| package task | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"strings" | ||||
|  | ||||
| 	"mvdan.cc/sh/v3/interp" | ||||
| ) | ||||
|  | ||||
| // ErrTaskfileAlreadyExists is returned on creating a Taskfile if one already exists | ||||
| var ErrTaskfileAlreadyExists = errors.New("task: A Taskfile already exists") | ||||
|  | ||||
| type taskNotFoundError struct { | ||||
| 	taskName   string | ||||
| 	didYouMean string | ||||
| } | ||||
|  | ||||
| func (err *taskNotFoundError) Error() string { | ||||
| 	if err.didYouMean != "" { | ||||
| 		return fmt.Sprintf( | ||||
| 			`task: Task %q does not exist. Did you mean %q?`, | ||||
| 			err.taskName, | ||||
| 			err.didYouMean, | ||||
| 		) | ||||
| 	} | ||||
|  | ||||
| 	return fmt.Sprintf(`task: Task %q does not exist`, err.taskName) | ||||
| } | ||||
|  | ||||
| type multipleTasksWithAliasError struct { | ||||
| 	aliasName string | ||||
| 	taskNames []string | ||||
| } | ||||
|  | ||||
| func (err *multipleTasksWithAliasError) Error() string { | ||||
| 	return fmt.Sprintf(`task: Multiple tasks (%s) with alias %q found`, strings.Join(err.taskNames, ", "), err.aliasName) | ||||
| } | ||||
|  | ||||
| type taskInternalError struct { | ||||
| 	taskName string | ||||
| } | ||||
|  | ||||
| func (err *taskInternalError) Error() string { | ||||
| 	return fmt.Sprintf(`task: Task "%s" is internal`, err.taskName) | ||||
| } | ||||
|  | ||||
| type TaskRunError struct { | ||||
| 	taskName string | ||||
| 	err      error | ||||
| } | ||||
|  | ||||
| func (err *TaskRunError) Error() string { | ||||
| 	return fmt.Sprintf(`task: Failed to run task %q: %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 | ||||
| // many times. In this case you probably have a cyclic dependendy or | ||||
| // infinite loop | ||||
| type MaximumTaskCallExceededError struct { | ||||
| 	task string | ||||
| } | ||||
|  | ||||
| func (e *MaximumTaskCallExceededError) Error() string { | ||||
| 	return fmt.Sprintf( | ||||
| 		`task: maximum task call exceeded (%d) for task %q: probably an cyclic dep or infinite loop`, | ||||
| 		MaximumTaskCall, | ||||
| 		e.task, | ||||
| 	) | ||||
| } | ||||
							
								
								
									
										40
									
								
								errors/errors.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								errors/errors.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,40 @@ | ||||
| package errors | ||||
|  | ||||
| import "errors" | ||||
|  | ||||
| // General exit codes | ||||
| const ( | ||||
| 	CodeOk      int = iota // Used when the program exits without errors | ||||
| 	CodeUnknown            // Used when no other exit code is appropriate | ||||
| ) | ||||
|  | ||||
| // Taskfile related exit codes | ||||
| const ( | ||||
| 	CodeTaskfileNotFound int = iota + 100 | ||||
| 	CodeTaskfileAlreadyExists | ||||
| 	CodeTaskfileInvalid | ||||
| ) | ||||
|  | ||||
| // Task related exit codes | ||||
| const ( | ||||
| 	CodeTaskNotFound int = iota + 200 | ||||
| 	CodeTaskRunError | ||||
| 	CodeTaskInternal | ||||
| 	CodeTaskNameConflict | ||||
| 	CodeTaskCalledTooManyTimes | ||||
| ) | ||||
|  | ||||
| // TaskError extends the standard error interface with a Code method. This code will | ||||
| // be used as the exit code of the program which allows the user to distinguish | ||||
| // between different types of errors. | ||||
| type TaskError interface { | ||||
| 	error | ||||
| 	Code() int | ||||
| } | ||||
|  | ||||
| // New returns an error that formats as the given text. Each call to New returns | ||||
| // a distinct error value even if the text is identical. This wraps the standard | ||||
| // errors.New function so that we don't need to alias that package. | ||||
| func New(text string) error { | ||||
| 	return errors.New(text) | ||||
| } | ||||
							
								
								
									
										100
									
								
								errors/errors_task.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								errors/errors_task.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,100 @@ | ||||
| package errors | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"strings" | ||||
|  | ||||
| 	"mvdan.cc/sh/v3/interp" | ||||
| ) | ||||
|  | ||||
| // TaskNotFoundError is returned when the specified task is not found in the | ||||
| // Taskfile. | ||||
| type TaskNotFoundError struct { | ||||
| 	TaskName   string | ||||
| 	DidYouMean string | ||||
| } | ||||
|  | ||||
| func (err *TaskNotFoundError) Error() string { | ||||
| 	if err.DidYouMean != "" { | ||||
| 		return fmt.Sprintf( | ||||
| 			`task: Task %q does not exist. Did you mean %q?`, | ||||
| 			err.TaskName, | ||||
| 			err.DidYouMean, | ||||
| 		) | ||||
| 	} | ||||
|  | ||||
| 	return fmt.Sprintf(`task: Task %q does not exist`, err.TaskName) | ||||
| } | ||||
|  | ||||
| func (err *TaskNotFoundError) Code() int { | ||||
| 	return CodeTaskNotFound | ||||
| } | ||||
|  | ||||
| // TaskRunError is returned when a command in a task returns a non-zero exit | ||||
| // code. | ||||
| type TaskRunError struct { | ||||
| 	TaskName string | ||||
| 	Err      error | ||||
| } | ||||
|  | ||||
| func (err *TaskRunError) Error() string { | ||||
| 	return fmt.Sprintf(`task: Failed to run task %q: %v`, err.TaskName, err.Err) | ||||
| } | ||||
|  | ||||
| func (err *TaskRunError) Code() int { | ||||
| 	return CodeTaskRunError | ||||
| } | ||||
|  | ||||
| func (err *TaskRunError) TaskExitCode() int { | ||||
| 	if c, ok := interp.IsExitStatus(err.Err); ok { | ||||
| 		return int(c) | ||||
| 	} | ||||
| 	return err.Code() | ||||
| } | ||||
|  | ||||
| // TaskInternalError when the user attempts to invoke a task that is internal. | ||||
| type TaskInternalError struct { | ||||
| 	TaskName string | ||||
| } | ||||
|  | ||||
| func (err *TaskInternalError) Error() string { | ||||
| 	return fmt.Sprintf(`task: Task "%s" is internal`, err.TaskName) | ||||
| } | ||||
|  | ||||
| func (err *TaskInternalError) Code() int { | ||||
| 	return CodeTaskInternal | ||||
| } | ||||
|  | ||||
| // TaskNameConflictError is returned when multiple tasks with the same name or | ||||
| // alias are found. | ||||
| type TaskNameConflictError struct { | ||||
| 	AliasName string | ||||
| 	TaskNames []string | ||||
| } | ||||
|  | ||||
| func (err *TaskNameConflictError) Error() string { | ||||
| 	return fmt.Sprintf(`task: Multiple tasks (%s) with alias %q found`, strings.Join(err.TaskNames, ", "), err.AliasName) | ||||
| } | ||||
|  | ||||
| func (err *TaskNameConflictError) Code() int { | ||||
| 	return CodeTaskNameConflict | ||||
| } | ||||
|  | ||||
| // TaskCalledTooManyTimesError is returned when the maximum task call limit is | ||||
| // exceeded. This is to prevent infinite loops and cyclic dependencies. | ||||
| type TaskCalledTooManyTimesError struct { | ||||
| 	TaskName        string | ||||
| 	MaximumTaskCall int | ||||
| } | ||||
|  | ||||
| func (err *TaskCalledTooManyTimesError) Error() string { | ||||
| 	return fmt.Sprintf( | ||||
| 		`task: maximum task call exceeded (%d) for task %q: probably an cyclic dep or infinite loop`, | ||||
| 		err.MaximumTaskCall, | ||||
| 		err.TaskName, | ||||
| 	) | ||||
| } | ||||
|  | ||||
| func (err *TaskCalledTooManyTimesError) Code() int { | ||||
| 	return CodeTaskCalledTooManyTimes | ||||
| } | ||||
							
								
								
									
										51
									
								
								errors/errors_taskfile.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								errors/errors_taskfile.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,51 @@ | ||||
| package errors | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| ) | ||||
|  | ||||
| // TaskfileNotFoundError is returned when no appropriate Taskfile is found when | ||||
| // searching the filesystem. | ||||
| type TaskfileNotFoundError struct { | ||||
| 	Dir  string | ||||
| 	Walk bool | ||||
| } | ||||
|  | ||||
| func (err TaskfileNotFoundError) Error() string { | ||||
| 	var walkText string | ||||
| 	if err.Walk { | ||||
| 		walkText = " (or any of the parent directories)" | ||||
| 	} | ||||
| 	return fmt.Sprintf(`task: No Taskfile found in "%s"%s. Use "task --init" to create a new one`, err.Dir, walkText) | ||||
| } | ||||
|  | ||||
| func (err TaskfileNotFoundError) Code() int { | ||||
| 	return CodeTaskfileNotFound | ||||
| } | ||||
|  | ||||
| // TaskfileAlreadyExistsError is returned on creating a Taskfile if one already | ||||
| // exists. | ||||
| type TaskfileAlreadyExistsError struct{} | ||||
|  | ||||
| func (err TaskfileAlreadyExistsError) Error() string { | ||||
| 	return "task: A Taskfile already exists" | ||||
| } | ||||
|  | ||||
| func (err TaskfileAlreadyExistsError) Code() int { | ||||
| 	return CodeTaskfileAlreadyExists | ||||
| } | ||||
|  | ||||
| // TaskfileInvalidError is returned when the Taskfile contains syntax errors or | ||||
| // cannot be parsed for some reason. | ||||
| type TaskfileInvalidError struct { | ||||
| 	FilePath string | ||||
| 	Err      error | ||||
| } | ||||
|  | ||||
| func (err TaskfileInvalidError) Error() string { | ||||
| 	return fmt.Sprintf("task: Failed to parse %s:\n%v", err.FilePath, err.Err) | ||||
| } | ||||
|  | ||||
| func (err TaskfileInvalidError) Code() int { | ||||
| 	return CodeTaskfileInvalid | ||||
| } | ||||
							
								
								
									
										3
									
								
								init.go
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								init.go
									
									
									
									
									
								
							| @@ -5,6 +5,7 @@ import ( | ||||
| 	"io" | ||||
| 	"os" | ||||
|  | ||||
| 	"github.com/go-task/task/v3/errors" | ||||
| 	"github.com/go-task/task/v3/internal/filepathext" | ||||
| ) | ||||
|  | ||||
| @@ -29,7 +30,7 @@ func InitTaskfile(w io.Writer, dir string) error { | ||||
| 	f := filepathext.SmartJoin(dir, defaultTaskfileName) | ||||
|  | ||||
| 	if _, err := os.Stat(f); err == nil { | ||||
| 		return ErrTaskfileAlreadyExists | ||||
| 		return errors.TaskfileAlreadyExistsError{} | ||||
| 	} | ||||
|  | ||||
| 	if err := os.WriteFile(f, []byte(defaultTaskfile), 0o644); err != nil { | ||||
|   | ||||
							
								
								
									
										23
									
								
								task.go
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								task.go
									
									
									
									
									
								
							| @@ -10,6 +10,7 @@ import ( | ||||
| 	"sync/atomic" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/go-task/task/v3/errors" | ||||
| 	"github.com/go-task/task/v3/internal/compiler" | ||||
| 	"github.com/go-task/task/v3/internal/env" | ||||
| 	"github.com/go-task/task/v3/internal/execext" | ||||
| @@ -77,7 +78,7 @@ func (e *Executor) Run(ctx context.Context, calls ...taskfile.Call) error { | ||||
| 	for _, call := range calls { | ||||
| 		task, err := e.GetTask(call) | ||||
| 		if err != nil { | ||||
| 			if _, ok := err.(*taskNotFoundError); ok { | ||||
| 			if _, ok := err.(*errors.TaskNotFoundError); ok { | ||||
| 				if _, err := e.ListTasks(ListOptions{ListOnlyTasksWithDescriptions: true}); err != nil { | ||||
| 					return err | ||||
| 				} | ||||
| @@ -86,12 +87,12 @@ func (e *Executor) Run(ctx context.Context, calls ...taskfile.Call) error { | ||||
| 		} | ||||
|  | ||||
| 		if task.Internal { | ||||
| 			if _, ok := err.(*taskNotFoundError); ok { | ||||
| 			if _, ok := err.(*errors.TaskNotFoundError); ok { | ||||
| 				if _, err := e.ListTasks(ListOptions{ListOnlyTasksWithDescriptions: true}); err != nil { | ||||
| 					return err | ||||
| 				} | ||||
| 			} | ||||
| 			return &taskInternalError{taskName: call.Task} | ||||
| 			return &errors.TaskInternalError{TaskName: call.Task} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| @@ -132,7 +133,7 @@ func (e *Executor) RunTask(ctx context.Context, call taskfile.Call) error { | ||||
| 		return err | ||||
| 	} | ||||
| 	if !e.Watch && atomic.AddInt32(e.taskCallCount[t.Task], 1) >= MaximumTaskCall { | ||||
| 		return &MaximumTaskCallExceededError{task: t.Task} | ||||
| 		return &errors.TaskCalledTooManyTimesError{TaskName: t.Task} | ||||
| 	} | ||||
|  | ||||
| 	release := e.acquireConcurrencyLimit() | ||||
| @@ -203,7 +204,7 @@ func (e *Executor) RunTask(ctx context.Context, call taskfile.Call) error { | ||||
| 					continue | ||||
| 				} | ||||
|  | ||||
| 				return &TaskRunError{t.Task, err} | ||||
| 				return &errors.TaskRunError{TaskName: t.Task, Err: err} | ||||
| 			} | ||||
| 		} | ||||
| 		e.Logger.VerboseErrf(logger.Magenta, `task: "%s" finished`, call.Task) | ||||
| @@ -372,9 +373,9 @@ func (e *Executor) GetTask(call taskfile.Call) (*taskfile.Task, error) { | ||||
| 	} | ||||
| 	// If we found multiple tasks | ||||
| 	if len(aliasedTasks) > 1 { | ||||
| 		return nil, &multipleTasksWithAliasError{ | ||||
| 			aliasName: call.Task, | ||||
| 			taskNames: aliasedTasks, | ||||
| 		return nil, &errors.TaskNameConflictError{ | ||||
| 			AliasName: call.Task, | ||||
| 			TaskNames: aliasedTasks, | ||||
| 		} | ||||
| 	} | ||||
| 	// If we found no tasks | ||||
| @@ -383,9 +384,9 @@ func (e *Executor) GetTask(call taskfile.Call) (*taskfile.Task, error) { | ||||
| 		if e.fuzzyModel != nil { | ||||
| 			didYouMean = e.fuzzyModel.SpellCheck(call.Task) | ||||
| 		} | ||||
| 		return nil, &taskNotFoundError{ | ||||
| 			taskName:   call.Task, | ||||
| 			didYouMean: didYouMean, | ||||
| 		return nil, &errors.TaskNotFoundError{ | ||||
| 			TaskName:   call.Task, | ||||
| 			DidYouMean: didYouMean, | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
|   | ||||
| @@ -16,6 +16,7 @@ import ( | ||||
| 	"github.com/stretchr/testify/require" | ||||
|  | ||||
| 	"github.com/go-task/task/v3" | ||||
| 	"github.com/go-task/task/v3/errors" | ||||
| 	"github.com/go-task/task/v3/internal/filepathext" | ||||
| 	"github.com/go-task/task/v3/taskfile" | ||||
| ) | ||||
| @@ -770,7 +771,7 @@ func TestCyclicDep(t *testing.T) { | ||||
| 		Stderr: io.Discard, | ||||
| 	} | ||||
| 	require.NoError(t, e.Setup()) | ||||
| 	assert.IsType(t, &task.MaximumTaskCallExceededError{}, e.Run(context.Background(), taskfile.Call{Task: "task-1"})) | ||||
| 	assert.IsType(t, &errors.TaskCalledTooManyTimesError{}, e.Run(context.Background(), taskfile.Call{Task: "task-1"})) | ||||
| } | ||||
|  | ||||
| func TestTaskVersion(t *testing.T) { | ||||
| @@ -1691,9 +1692,9 @@ func TestErrorCode(t *testing.T) { | ||||
|  | ||||
| 	err := e.Run(context.Background(), taskfile.Call{Task: "test-exit-code"}) | ||||
| 	require.Error(t, err) | ||||
| 	casted, ok := err.(*task.TaskRunError) | ||||
| 	casted, ok := err.(*errors.TaskRunError) | ||||
| 	assert.True(t, ok, "cannot cast returned error to *task.TaskRunError") | ||||
| 	assert.Equal(t, 42, casted.ExitCode(), "unexpected exit code from task") | ||||
| 	assert.Equal(t, 42, casted.TaskExitCode(), "unexpected exit code from task") | ||||
| } | ||||
|  | ||||
| func TestEvaluateSymlinksInPaths(t *testing.T) { | ||||
|   | ||||
| @@ -1,7 +1,6 @@ | ||||
| package read | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
| @@ -9,6 +8,7 @@ import ( | ||||
|  | ||||
| 	"gopkg.in/yaml.v3" | ||||
|  | ||||
| 	"github.com/go-task/task/v3/errors" | ||||
| 	"github.com/go-task/task/v3/internal/filepathext" | ||||
| 	"github.com/go-task/task/v3/internal/sysinfo" | ||||
| 	"github.com/go-task/task/v3/internal/templater" | ||||
| @@ -208,7 +208,7 @@ func readTaskfile(file string) (*taskfile.Taskfile, error) { | ||||
|  | ||||
| 	var t taskfile.Taskfile | ||||
| 	if err := yaml.NewDecoder(f).Decode(&t); err != nil { | ||||
| 		return nil, fmt.Errorf("task: Failed to parse %s:\n%w", filepathext.TryAbsToRel(file), err) | ||||
| 		return nil, &errors.TaskfileInvalidError{FilePath: filepathext.TryAbsToRel(file), Err: err} | ||||
| 	} | ||||
| 	return &t, nil | ||||
| } | ||||
| @@ -229,7 +229,7 @@ func exists(path string) (string, error) { | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return "", fmt.Errorf(`task: No Taskfile found in "%s". Use "task --init" to create a new one`, path) | ||||
| 	return "", errors.TaskfileNotFoundError{Dir: path, Walk: false} | ||||
| } | ||||
|  | ||||
| func existsWalk(path string) (string, error) { | ||||
| @@ -254,7 +254,7 @@ func existsWalk(path string) (string, error) { | ||||
| 		// Error if we reached the root directory and still haven't found a file | ||||
| 		// OR if the user id of the directory changes | ||||
| 		if path == parentPath || (parentOwner != owner) { | ||||
| 			return "", fmt.Errorf(`task: No Taskfile found in "%s" (or any of the parent directories). Use "task --init" to create a new one`, origPath) | ||||
| 			return "", errors.TaskfileNotFoundError{Dir: origPath, Walk: false} | ||||
| 		} | ||||
|  | ||||
| 		owner = parentOwner | ||||
|   | ||||
							
								
								
									
										5
									
								
								watch.go
									
									
									
									
									
								
							
							
						
						
									
										5
									
								
								watch.go
									
									
									
									
									
								
							| @@ -12,6 +12,7 @@ import ( | ||||
|  | ||||
| 	"github.com/radovskyb/watcher" | ||||
|  | ||||
| 	"github.com/go-task/task/v3/errors" | ||||
| 	"github.com/go-task/task/v3/internal/fingerprint" | ||||
| 	"github.com/go-task/task/v3/internal/logger" | ||||
| 	"github.com/go-task/task/v3/taskfile" | ||||
| @@ -102,8 +103,8 @@ func (e *Executor) watchTasks(calls ...taskfile.Call) error { | ||||
| } | ||||
|  | ||||
| func isContextError(err error) bool { | ||||
| 	if taskRunErr, ok := err.(*TaskRunError); ok { | ||||
| 		err = taskRunErr.err | ||||
| 	if taskRunErr, ok := err.(*errors.TaskRunError); ok { | ||||
| 		err = taskRunErr.Err | ||||
| 	} | ||||
|  | ||||
| 	return err == context.Canceled || err == context.DeadlineExceeded | ||||
|   | ||||
		Reference in New Issue
	
	Block a user