1
0
mirror of https://github.com/go-task/task.git synced 2025-11-29 22:48:03 +02:00

feat: custom error codes (#1114)

This commit is contained in:
Pete Davison
2023-04-15 21:22:25 +01:00
committed by GitHub
parent 9ec544817f
commit f9c77acd96
12 changed files with 286 additions and 129 deletions

40
errors/errors.go Normal file
View 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
View 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
View 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
}