mirror of
https://github.com/go-task/task.git
synced 2024-12-04 10:24:45 +02:00
feat: better yaml parsing and error handling (#1619)
This commit is contained in:
parent
635e3f4e7d
commit
8d138a5eea
@ -131,7 +131,8 @@ func run() error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := e.Setup(); err != nil {
|
||||
err := e.Setup()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
179
errors/error_taskfile_decode.go
Normal file
179
errors/error_taskfile_decode.go
Normal file
@ -0,0 +1,179 @@
|
||||
package errors
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"embed"
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/alecthomas/chroma/v2"
|
||||
"github.com/alecthomas/chroma/v2/quick"
|
||||
"github.com/alecthomas/chroma/v2/styles"
|
||||
"github.com/fatih/color"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
//go:embed themes/*.xml
|
||||
var embedded embed.FS
|
||||
|
||||
var typeErrorRegex = regexp.MustCompile(`line \d+: (.*)`)
|
||||
|
||||
func init() {
|
||||
r, err := embedded.Open("themes/task.xml")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
style, err := chroma.NewXMLStyle(r)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
styles.Register(style)
|
||||
}
|
||||
|
||||
type (
|
||||
TaskfileDecodeError struct {
|
||||
Message string
|
||||
Location string
|
||||
Line int
|
||||
Column int
|
||||
Tag string
|
||||
Snippet TaskfileSnippet
|
||||
Err error
|
||||
}
|
||||
TaskfileSnippet struct {
|
||||
Lines []string
|
||||
StartLine int
|
||||
EndLine int
|
||||
Padding int
|
||||
}
|
||||
)
|
||||
|
||||
func NewTaskfileDecodeError(err error, node *yaml.Node) *TaskfileDecodeError {
|
||||
// If the error is already a DecodeError, return it
|
||||
taskfileInvalidErr := &TaskfileDecodeError{}
|
||||
if errors.As(err, &taskfileInvalidErr) {
|
||||
return taskfileInvalidErr
|
||||
}
|
||||
return &TaskfileDecodeError{
|
||||
Line: node.Line,
|
||||
Column: node.Column,
|
||||
Tag: node.ShortTag(),
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
|
||||
func (err *TaskfileDecodeError) Error() string {
|
||||
buf := &bytes.Buffer{}
|
||||
|
||||
// Print the error message
|
||||
if err.Message != "" {
|
||||
fmt.Fprintln(buf, color.RedString("err: %s", err.Message))
|
||||
} else {
|
||||
// Extract the errors from the TypeError
|
||||
te := &yaml.TypeError{}
|
||||
if errors.As(err.Err, &te) {
|
||||
if len(te.Errors) > 1 {
|
||||
fmt.Fprintln(buf, color.RedString("errs:"))
|
||||
for _, message := range te.Errors {
|
||||
fmt.Fprintln(buf, color.RedString("- %s", extractTypeErrorMessage(message)))
|
||||
}
|
||||
} else {
|
||||
fmt.Fprintln(buf, color.RedString("err: %s", extractTypeErrorMessage(te.Errors[0])))
|
||||
}
|
||||
} else {
|
||||
// Otherwise print the error message normally
|
||||
fmt.Fprintln(buf, color.RedString("err: %s", err.Err))
|
||||
}
|
||||
}
|
||||
fmt.Fprintln(buf, color.RedString("file: %s:%d:%d", err.Location, err.Line, err.Column))
|
||||
|
||||
// Print the snippet
|
||||
maxLineNumberDigits := digits(err.Snippet.EndLine)
|
||||
lineNumberSpacer := strings.Repeat(" ", maxLineNumberDigits)
|
||||
columnSpacer := strings.Repeat(" ", err.Column-1)
|
||||
for i, line := range err.Snippet.Lines {
|
||||
currentLine := err.Snippet.StartLine + i + 1
|
||||
|
||||
lineIndicator := " "
|
||||
if currentLine == err.Line {
|
||||
lineIndicator = ">"
|
||||
}
|
||||
columnIndicator := "^"
|
||||
|
||||
// Print each line
|
||||
lineIndicator = color.RedString(lineIndicator)
|
||||
columnIndicator = color.RedString(columnIndicator)
|
||||
lineNumberFormat := fmt.Sprintf("%%%dd", maxLineNumberDigits)
|
||||
lineNumber := fmt.Sprintf(lineNumberFormat, currentLine)
|
||||
fmt.Fprintf(buf, "%s %s | %s", lineIndicator, lineNumber, line)
|
||||
|
||||
// Print the column indicator
|
||||
if currentLine == err.Line {
|
||||
fmt.Fprintf(buf, "\n %s | %s%s", lineNumberSpacer, columnSpacer, columnIndicator)
|
||||
}
|
||||
|
||||
// If there are more lines to print, add a newline
|
||||
if i < len(err.Snippet.Lines)-1 {
|
||||
fmt.Fprintln(buf)
|
||||
}
|
||||
}
|
||||
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func (err *TaskfileDecodeError) Unwrap() error {
|
||||
return err.Err
|
||||
}
|
||||
|
||||
func (err *TaskfileDecodeError) Code() int {
|
||||
return CodeTaskfileDecode
|
||||
}
|
||||
|
||||
func (err *TaskfileDecodeError) WithMessage(format string, a ...any) *TaskfileDecodeError {
|
||||
err.Message = fmt.Sprintf(format, a...)
|
||||
return err
|
||||
}
|
||||
|
||||
func (err *TaskfileDecodeError) WithTypeMessage(t string) *TaskfileDecodeError {
|
||||
err.Message = fmt.Sprintf("cannot unmarshal %s into %s", err.Tag, t)
|
||||
return err
|
||||
}
|
||||
|
||||
func (err *TaskfileDecodeError) WithFileInfo(location string, b []byte, padding int) *TaskfileDecodeError {
|
||||
buf := &bytes.Buffer{}
|
||||
if err := quick.Highlight(buf, string(b), "yaml", "terminal", "task"); err != nil {
|
||||
buf.WriteString(string(b))
|
||||
}
|
||||
lines := strings.Split(buf.String(), "\n")
|
||||
start := max(err.Line-1-padding, 0)
|
||||
end := min(err.Line+padding, len(lines)-1)
|
||||
|
||||
err.Location = location
|
||||
err.Snippet = TaskfileSnippet{
|
||||
Lines: lines[start:end],
|
||||
StartLine: start,
|
||||
EndLine: end,
|
||||
Padding: padding,
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func extractTypeErrorMessage(message string) string {
|
||||
matches := typeErrorRegex.FindStringSubmatch(message)
|
||||
if len(matches) == 2 {
|
||||
return matches[1]
|
||||
}
|
||||
return message
|
||||
}
|
||||
|
||||
func digits(number int) int {
|
||||
count := 0
|
||||
for number != 0 {
|
||||
number /= 10
|
||||
count += 1
|
||||
}
|
||||
return count
|
||||
}
|
@ -12,14 +12,14 @@ const (
|
||||
const (
|
||||
CodeTaskfileNotFound int = iota + 100
|
||||
CodeTaskfileAlreadyExists
|
||||
CodeTaskfileInvalid
|
||||
CodeTaskfileDecode
|
||||
CodeTaskfileFetchFailed
|
||||
CodeTaskfileNotTrusted
|
||||
CodeTaskfileNotSecure
|
||||
CodeTaskfileCacheNotFound
|
||||
CodeTaskfileVersionCheckError
|
||||
CodeTaskfileNetworkTimeout
|
||||
_ // CodeTaskfileDuplicateInclude
|
||||
CodeTaskfileInvalid
|
||||
CodeTaskfileCycle
|
||||
)
|
||||
|
||||
@ -58,3 +58,8 @@ func Is(err, target error) bool {
|
||||
func As(err error, target any) bool {
|
||||
return errors.As(err, target)
|
||||
}
|
||||
|
||||
// Unwrap wraps the standard errors.Unwrap function so that we don't need to alias that package.
|
||||
func Unwrap(err error) error {
|
||||
return errors.Unwrap(err)
|
||||
}
|
||||
|
17
errors/themes/task.xml
Normal file
17
errors/themes/task.xml
Normal file
@ -0,0 +1,17 @@
|
||||
<style name="task">
|
||||
<entry type="Background" style="bg:#eee8d5"/>
|
||||
<entry type="Keyword" style="#859900"/>
|
||||
<entry type="KeywordConstant" style=""/>
|
||||
<entry type="KeywordNamespace" style="#dc322f"/>
|
||||
<entry type="KeywordType" style=""/>
|
||||
<entry type="Name" style="#268bd2"/>
|
||||
<entry type="NameBuiltin" style="#cb4b16"/>
|
||||
<entry type="NameClass" style="#cb4b16"/>
|
||||
<entry type="NameTag" style=""/>
|
||||
<entry type="Literal" style="#2aa198"/>
|
||||
<entry type="LiteralNumber" style=""/>
|
||||
<entry type="OperatorWord" style="#859900"/>
|
||||
<entry type="Comment" style="italic #93a1a1"/>
|
||||
<entry type="Generic" style="#d33682"/>
|
||||
<entry type="Text" style="#586e75"/>
|
||||
</style>
|
2
go.mod
2
go.mod
@ -4,6 +4,7 @@ go 1.21.0
|
||||
|
||||
require (
|
||||
github.com/Masterminds/semver/v3 v3.2.1
|
||||
github.com/alecthomas/chroma/v2 v2.13.0
|
||||
github.com/davecgh/go-spew v1.1.1
|
||||
github.com/dominikbraun/graph v0.23.0
|
||||
github.com/fatih/color v1.16.0
|
||||
@ -25,6 +26,7 @@ require (
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/dlclark/regexp2 v1.11.0 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.0.9 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
|
10
go.sum
10
go.sum
@ -1,9 +1,17 @@
|
||||
github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0=
|
||||
github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
|
||||
github.com/alecthomas/assert/v2 v2.6.0 h1:o3WJwILtexrEUk3cUVal3oiQY2tfgr/FHWiz/v2n4FU=
|
||||
github.com/alecthomas/assert/v2 v2.6.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
|
||||
github.com/alecthomas/chroma/v2 v2.13.0 h1:VP72+99Fb2zEcYM0MeaWJmV+xQvz5v5cxRHd+ooU1lI=
|
||||
github.com/alecthomas/chroma/v2 v2.13.0/go.mod h1:BUGjjsD+ndS6eX37YgTchSEG+Jg9Jv1GiZs9sqPqztk=
|
||||
github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc=
|
||||
github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
|
||||
github.com/creack/pty v1.1.21 h1:1/QdRyBaHHJP61QkWMXlOIBfsgdDeeKfK8SYVUWJKf0=
|
||||
github.com/creack/pty v1.1.21/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI=
|
||||
github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||
github.com/dominikbraun/graph v0.23.0 h1:TdZB4pPqCLFxYhdyMFb1TBdFxp8XLcJfTTBQucVPgCo=
|
||||
github.com/dominikbraun/graph v0.23.0/go.mod h1:yOjYyogZLY1LSG9E33JWZJiq5k83Qy2C6POAuiViluc=
|
||||
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
|
||||
@ -16,6 +24,8 @@ github.com/go-task/template v0.0.0-20240422130016-8f6b279b1e90 h1:JBbiZ2CXIZ9Upe
|
||||
github.com/go-task/template v0.0.0-20240422130016-8f6b279b1e90/go.mod h1:RgwRaZK+kni/hJJ7/AaOE2lPQFPbAdji/DyhC6pxo4k=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
|
||||
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
|
||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4=
|
||||
|
@ -1042,7 +1042,7 @@ func TestIncludesIncorrect(t *testing.T) {
|
||||
|
||||
err := e.Setup()
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "task: Failed to parse testdata/includes_incorrect/incomplete.yml:")
|
||||
assert.Contains(t, err.Error(), "Failed to parse testdata/includes_incorrect/incomplete.yml:", err.Error())
|
||||
}
|
||||
|
||||
func TestIncludesEmptyMain(t *testing.T) {
|
||||
|
@ -1,10 +1,9 @@
|
||||
package ast
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
|
||||
"github.com/go-task/task/v3/errors"
|
||||
"github.com/go-task/task/v3/internal/deepcopy"
|
||||
)
|
||||
|
||||
@ -46,7 +45,7 @@ func (c *Cmd) UnmarshalYAML(node *yaml.Node) error {
|
||||
case yaml.ScalarNode:
|
||||
var cmd string
|
||||
if err := node.Decode(&cmd); err != nil {
|
||||
return err
|
||||
return errors.NewTaskfileDecodeError(err, node)
|
||||
}
|
||||
c.Cmd = cmd
|
||||
return nil
|
||||
@ -110,8 +109,8 @@ func (c *Cmd) UnmarshalYAML(node *yaml.Node) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("yaml: line %d: invalid keys in command", node.Line)
|
||||
return errors.NewTaskfileDecodeError(nil, node).WithMessage("invalid keys in command")
|
||||
}
|
||||
|
||||
return fmt.Errorf("yaml: line %d: cannot unmarshal %s into command", node.Line, node.ShortTag())
|
||||
return errors.NewTaskfileDecodeError(nil, node).WithTypeMessage("command")
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
package ast
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
|
||||
"github.com/go-task/task/v3/errors"
|
||||
)
|
||||
|
||||
// Dep is a task dependency
|
||||
@ -32,7 +32,7 @@ func (d *Dep) UnmarshalYAML(node *yaml.Node) error {
|
||||
case yaml.ScalarNode:
|
||||
var task string
|
||||
if err := node.Decode(&task); err != nil {
|
||||
return err
|
||||
return errors.NewTaskfileDecodeError(err, node)
|
||||
}
|
||||
d.Task = task
|
||||
return nil
|
||||
@ -45,7 +45,7 @@ func (d *Dep) UnmarshalYAML(node *yaml.Node) error {
|
||||
Silent bool
|
||||
}
|
||||
if err := node.Decode(&taskCall); err != nil {
|
||||
return err
|
||||
return errors.NewTaskfileDecodeError(err, node)
|
||||
}
|
||||
d.Task = taskCall.Task
|
||||
d.For = taskCall.For
|
||||
@ -54,5 +54,5 @@ func (d *Dep) UnmarshalYAML(node *yaml.Node) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("cannot unmarshal %s into dependency", node.ShortTag())
|
||||
return errors.NewTaskfileDecodeError(nil, node).WithTypeMessage("dependency")
|
||||
}
|
||||
|
@ -1,10 +1,9 @@
|
||||
package ast
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
|
||||
"github.com/go-task/task/v3/errors"
|
||||
"github.com/go-task/task/v3/internal/deepcopy"
|
||||
)
|
||||
|
||||
@ -22,7 +21,7 @@ func (f *For) UnmarshalYAML(node *yaml.Node) error {
|
||||
case yaml.ScalarNode:
|
||||
var from string
|
||||
if err := node.Decode(&from); err != nil {
|
||||
return err
|
||||
return errors.NewTaskfileDecodeError(err, node)
|
||||
}
|
||||
f.From = from
|
||||
return nil
|
||||
@ -30,7 +29,7 @@ func (f *For) UnmarshalYAML(node *yaml.Node) error {
|
||||
case yaml.SequenceNode:
|
||||
var list []any
|
||||
if err := node.Decode(&list); err != nil {
|
||||
return err
|
||||
return errors.NewTaskfileDecodeError(err, node)
|
||||
}
|
||||
f.List = list
|
||||
return nil
|
||||
@ -41,17 +40,19 @@ func (f *For) UnmarshalYAML(node *yaml.Node) error {
|
||||
Split string
|
||||
As string
|
||||
}
|
||||
if err := node.Decode(&forStruct); err == nil && forStruct.Var != "" {
|
||||
f.Var = forStruct.Var
|
||||
f.Split = forStruct.Split
|
||||
f.As = forStruct.As
|
||||
return nil
|
||||
if err := node.Decode(&forStruct); err != nil {
|
||||
return errors.NewTaskfileDecodeError(err, node)
|
||||
}
|
||||
|
||||
return fmt.Errorf("yaml: line %d: invalid keys in for", node.Line)
|
||||
if forStruct.Var == "" {
|
||||
return errors.NewTaskfileDecodeError(nil, node).WithMessage("invalid keys in for")
|
||||
}
|
||||
f.Var = forStruct.Var
|
||||
f.Split = forStruct.Split
|
||||
f.As = forStruct.As
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("yaml: line %d: cannot unmarshal %s into for", node.Line, node.ShortTag())
|
||||
return errors.NewTaskfileDecodeError(nil, node).WithTypeMessage("for")
|
||||
}
|
||||
|
||||
func (f *For) DeepCopy() *For {
|
||||
|
@ -1,9 +1,9 @@
|
||||
package ast
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
|
||||
"github.com/go-task/task/v3/errors"
|
||||
)
|
||||
|
||||
type Glob struct {
|
||||
@ -13,20 +13,22 @@ type Glob struct {
|
||||
|
||||
func (g *Glob) UnmarshalYAML(node *yaml.Node) error {
|
||||
switch node.Kind {
|
||||
|
||||
case yaml.ScalarNode:
|
||||
g.Glob = node.Value
|
||||
return nil
|
||||
|
||||
case yaml.MappingNode:
|
||||
var glob struct {
|
||||
Exclude string
|
||||
}
|
||||
if err := node.Decode(&glob); err != nil {
|
||||
return err
|
||||
return errors.NewTaskfileDecodeError(err, node)
|
||||
}
|
||||
g.Glob = glob.Exclude
|
||||
g.Negate = true
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf("yaml: line %d: cannot unmarshal %s into task", node.Line, node.ShortTag())
|
||||
}
|
||||
|
||||
return errors.NewTaskfileDecodeError(nil, node).WithTypeMessage("glob")
|
||||
}
|
||||
|
@ -1,10 +1,9 @@
|
||||
package ast
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
|
||||
"github.com/go-task/task/v3/errors"
|
||||
omap "github.com/go-task/task/v3/internal/omap"
|
||||
)
|
||||
|
||||
@ -38,7 +37,7 @@ func (includes *Includes) UnmarshalYAML(node *yaml.Node) error {
|
||||
|
||||
var v Include
|
||||
if err := valueNode.Decode(&v); err != nil {
|
||||
return err
|
||||
return errors.NewTaskfileDecodeError(err, node)
|
||||
}
|
||||
v.Namespace = keyNode.Value
|
||||
includes.Set(keyNode.Value, &v)
|
||||
@ -46,7 +45,7 @@ func (includes *Includes) UnmarshalYAML(node *yaml.Node) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("yaml: line %d: cannot unmarshal %s into included taskfiles", node.Line, node.ShortTag())
|
||||
return errors.NewTaskfileDecodeError(nil, node).WithTypeMessage("includes")
|
||||
}
|
||||
|
||||
// Len returns the length of the map
|
||||
@ -71,7 +70,7 @@ func (include *Include) UnmarshalYAML(node *yaml.Node) error {
|
||||
case yaml.ScalarNode:
|
||||
var str string
|
||||
if err := node.Decode(&str); err != nil {
|
||||
return err
|
||||
return errors.NewTaskfileDecodeError(err, node)
|
||||
}
|
||||
include.Taskfile = str
|
||||
return nil
|
||||
@ -86,7 +85,7 @@ func (include *Include) UnmarshalYAML(node *yaml.Node) error {
|
||||
Vars *Vars
|
||||
}
|
||||
if err := node.Decode(&includedTaskfile); err != nil {
|
||||
return err
|
||||
return errors.NewTaskfileDecodeError(err, node)
|
||||
}
|
||||
include.Taskfile = includedTaskfile.Taskfile
|
||||
include.Dir = includedTaskfile.Dir
|
||||
@ -98,7 +97,7 @@ func (include *Include) UnmarshalYAML(node *yaml.Node) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("yaml: line %d: cannot unmarshal %s into included taskfile", node.Line, node.ShortTag())
|
||||
return errors.NewTaskfileDecodeError(nil, node).WithTypeMessage("include")
|
||||
}
|
||||
|
||||
// DeepCopy creates a new instance of IncludedTaskfile and copies
|
||||
|
@ -1,9 +1,9 @@
|
||||
package ast
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
|
||||
"github.com/go-task/task/v3/errors"
|
||||
)
|
||||
|
||||
// Output of the Task output
|
||||
@ -25,7 +25,7 @@ func (s *Output) UnmarshalYAML(node *yaml.Node) error {
|
||||
case yaml.ScalarNode:
|
||||
var name string
|
||||
if err := node.Decode(&name); err != nil {
|
||||
return err
|
||||
return errors.NewTaskfileDecodeError(err, node)
|
||||
}
|
||||
s.Name = name
|
||||
return nil
|
||||
@ -35,10 +35,10 @@ func (s *Output) UnmarshalYAML(node *yaml.Node) error {
|
||||
Group *OutputGroup
|
||||
}
|
||||
if err := node.Decode(&tmp); err != nil {
|
||||
return fmt.Errorf("task: output style must be a string or mapping with a \"group\" key: %w", err)
|
||||
return errors.NewTaskfileDecodeError(err, node)
|
||||
}
|
||||
if tmp.Group == nil {
|
||||
return fmt.Errorf("task: output style must have the \"group\" key when in mapping form")
|
||||
return errors.NewTaskfileDecodeError(nil, node).WithMessage(`output style must have the "group" key when in mapping form`)
|
||||
}
|
||||
*s = Output{
|
||||
Name: "group",
|
||||
@ -47,7 +47,7 @@ func (s *Output) UnmarshalYAML(node *yaml.Node) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("yaml: line %d: cannot unmarshal %s into output", node.Line, node.ShortTag())
|
||||
return errors.NewTaskfileDecodeError(nil, node).WithTypeMessage("output")
|
||||
}
|
||||
|
||||
// OutputGroup is the style options specific to the Group style.
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
|
||||
"github.com/go-task/task/v3/errors"
|
||||
"github.com/go-task/task/v3/internal/goext"
|
||||
)
|
||||
|
||||
@ -30,7 +31,7 @@ type ErrInvalidPlatform struct {
|
||||
}
|
||||
|
||||
func (err *ErrInvalidPlatform) Error() string {
|
||||
return fmt.Sprintf(`task: Invalid platform "%s"`, err.Platform)
|
||||
return fmt.Sprintf(`invalid platform "%s"`, err.Platform)
|
||||
}
|
||||
|
||||
// UnmarshalYAML implements yaml.Unmarshaler interface.
|
||||
@ -39,14 +40,14 @@ func (p *Platform) UnmarshalYAML(node *yaml.Node) error {
|
||||
case yaml.ScalarNode:
|
||||
var platform string
|
||||
if err := node.Decode(&platform); err != nil {
|
||||
return err
|
||||
return errors.NewTaskfileDecodeError(err, node)
|
||||
}
|
||||
if err := p.parsePlatform(platform); err != nil {
|
||||
return err
|
||||
return errors.NewTaskfileDecodeError(err, node)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("yaml: line %d: cannot unmarshal %s into platform", node.Line, node.ShortTag())
|
||||
return errors.NewTaskfileDecodeError(nil, node).WithTypeMessage("platform")
|
||||
}
|
||||
|
||||
// parsePlatform takes a string representing an OS/Arch combination (or either on their own)
|
||||
|
@ -26,10 +26,10 @@ func TestPlatformParsing(t *testing.T) {
|
||||
{Input: "windows/amd64", ExpectedOS: "windows", ExpectedArch: "amd64"},
|
||||
{Input: "windows/arm64", ExpectedOS: "windows", ExpectedArch: "arm64"},
|
||||
|
||||
{Input: "invalid", Error: `task: Invalid platform "invalid"`},
|
||||
{Input: "invalid/invalid", Error: `task: Invalid platform "invalid/invalid"`},
|
||||
{Input: "windows/invalid", Error: `task: Invalid platform "windows/invalid"`},
|
||||
{Input: "invalid/amd64", Error: `task: Invalid platform "invalid/amd64"`},
|
||||
{Input: "invalid", Error: `invalid platform "invalid"`},
|
||||
{Input: "invalid/invalid", Error: `invalid platform "invalid/invalid"`},
|
||||
{Input: "windows/invalid", Error: `invalid platform "windows/invalid"`},
|
||||
{Input: "invalid/amd64", Error: `invalid platform "invalid/amd64"`},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
|
@ -1,14 +1,12 @@
|
||||
package ast
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
// ErrCantUnmarshalPrecondition is returned for invalid precond YAML.
|
||||
var ErrCantUnmarshalPrecondition = errors.New("task: Can't unmarshal precondition value")
|
||||
"github.com/go-task/task/v3/errors"
|
||||
)
|
||||
|
||||
// Precondition represents a precondition necessary for a task to run
|
||||
type Precondition struct {
|
||||
@ -33,7 +31,7 @@ func (p *Precondition) UnmarshalYAML(node *yaml.Node) error {
|
||||
case yaml.ScalarNode:
|
||||
var cmd string
|
||||
if err := node.Decode(&cmd); err != nil {
|
||||
return err
|
||||
return errors.NewTaskfileDecodeError(err, node)
|
||||
}
|
||||
p.Sh = cmd
|
||||
p.Msg = fmt.Sprintf("`%s` failed", cmd)
|
||||
@ -45,7 +43,7 @@ func (p *Precondition) UnmarshalYAML(node *yaml.Node) error {
|
||||
Msg string
|
||||
}
|
||||
if err := node.Decode(&sh); err != nil {
|
||||
return err
|
||||
return errors.NewTaskfileDecodeError(err, node)
|
||||
}
|
||||
p.Sh = sh.Sh
|
||||
p.Msg = sh.Msg
|
||||
@ -55,5 +53,5 @@ func (p *Precondition) UnmarshalYAML(node *yaml.Node) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("yaml: line %d: cannot unmarshal %s into precondition", node.Line, node.ShortTag())
|
||||
return errors.NewTaskfileDecodeError(nil, node).WithTypeMessage("precondition")
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import (
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
|
||||
"github.com/go-task/task/v3/errors"
|
||||
"github.com/go-task/task/v3/internal/deepcopy"
|
||||
)
|
||||
|
||||
@ -83,7 +84,7 @@ func (t *Task) UnmarshalYAML(node *yaml.Node) error {
|
||||
case yaml.ScalarNode:
|
||||
var cmd Cmd
|
||||
if err := node.Decode(&cmd); err != nil {
|
||||
return err
|
||||
return errors.NewTaskfileDecodeError(err, node)
|
||||
}
|
||||
t.Cmds = append(t.Cmds, &cmd)
|
||||
return nil
|
||||
@ -92,7 +93,7 @@ func (t *Task) UnmarshalYAML(node *yaml.Node) error {
|
||||
case yaml.SequenceNode:
|
||||
var cmds []*Cmd
|
||||
if err := node.Decode(&cmds); err != nil {
|
||||
return err
|
||||
return errors.NewTaskfileDecodeError(err, node)
|
||||
}
|
||||
t.Cmds = cmds
|
||||
return nil
|
||||
@ -130,11 +131,11 @@ func (t *Task) UnmarshalYAML(node *yaml.Node) error {
|
||||
Watch bool
|
||||
}
|
||||
if err := node.Decode(&task); err != nil {
|
||||
return err
|
||||
return errors.NewTaskfileDecodeError(err, node)
|
||||
}
|
||||
if task.Cmd != nil {
|
||||
if task.Cmds != nil {
|
||||
return fmt.Errorf("yaml: line %d: task cannot have both cmd and cmds", node.Line)
|
||||
return errors.NewTaskfileDecodeError(nil, node).WithMessage("task cannot have both cmd and cmds")
|
||||
}
|
||||
t.Cmds = []*Cmd{task.Cmd}
|
||||
} else {
|
||||
@ -169,7 +170,7 @@ func (t *Task) UnmarshalYAML(node *yaml.Node) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("yaml: line %d: cannot unmarshal %s into task", node.Line, node.ShortTag())
|
||||
return errors.NewTaskfileDecodeError(nil, node).WithTypeMessage("task")
|
||||
}
|
||||
|
||||
// DeepCopy creates a new instance of Task and copies
|
||||
|
@ -1,12 +1,13 @@
|
||||
package ast
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/Masterminds/semver/v3"
|
||||
"gopkg.in/yaml.v3"
|
||||
|
||||
"github.com/go-task/task/v3/errors"
|
||||
)
|
||||
|
||||
// NamespaceSeparator contains the character that separates namespaces
|
||||
@ -77,7 +78,7 @@ func (tf *Taskfile) UnmarshalYAML(node *yaml.Node) error {
|
||||
Interval time.Duration
|
||||
}
|
||||
if err := node.Decode(&taskfile); err != nil {
|
||||
return err
|
||||
return errors.NewTaskfileDecodeError(err, node)
|
||||
}
|
||||
tf.Version = taskfile.Version
|
||||
tf.Output = taskfile.Output
|
||||
@ -101,5 +102,5 @@ func (tf *Taskfile) UnmarshalYAML(node *yaml.Node) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("yaml: line %d: cannot unmarshal %s into taskfile", node.Line, node.ShortTag())
|
||||
return errors.NewTaskfileDecodeError(nil, node).WithTypeMessage("taskfile")
|
||||
}
|
||||
|
@ -6,6 +6,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/omap"
|
||||
)
|
||||
@ -118,7 +119,7 @@ func (t *Tasks) UnmarshalYAML(node *yaml.Node) error {
|
||||
case yaml.MappingNode:
|
||||
tasks := omap.New[string, *Task]()
|
||||
if err := node.Decode(&tasks); err != nil {
|
||||
return err
|
||||
return errors.NewTaskfileDecodeError(err, node)
|
||||
}
|
||||
|
||||
// nolint: errcheck
|
||||
@ -150,7 +151,7 @@ func (t *Tasks) UnmarshalYAML(node *yaml.Node) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("yaml: line %d: cannot unmarshal %s into tasks", node.Line, node.ShortTag())
|
||||
return errors.NewTaskfileDecodeError(nil, node).WithTypeMessage("tasks")
|
||||
}
|
||||
|
||||
func taskNameWithNamespace(taskName string, namespace string) string {
|
||||
|
@ -1,11 +1,11 @@
|
||||
package ast
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
|
||||
"github.com/go-task/task/v3/errors"
|
||||
"github.com/go-task/task/v3/internal/experiments"
|
||||
"github.com/go-task/task/v3/internal/omap"
|
||||
)
|
||||
@ -95,7 +95,7 @@ func (v *Var) UnmarshalYAML(node *yaml.Node) error {
|
||||
if experiments.MapVariables.Value == "1" {
|
||||
var value any
|
||||
if err := node.Decode(&value); err != nil {
|
||||
return err
|
||||
return errors.NewTaskfileDecodeError(err, node)
|
||||
}
|
||||
// If the value is a string and it starts with $, then it's a shell command
|
||||
if str, ok := value.(string); ok {
|
||||
@ -123,7 +123,7 @@ func (v *Var) UnmarshalYAML(node *yaml.Node) error {
|
||||
Yaml string
|
||||
}
|
||||
if err := node.Decode(&m); err != nil {
|
||||
return err
|
||||
return errors.NewTaskfileDecodeError(err, node)
|
||||
}
|
||||
v.Sh = m.Sh
|
||||
v.Ref = m.Ref
|
||||
@ -132,12 +132,12 @@ func (v *Var) UnmarshalYAML(node *yaml.Node) error {
|
||||
v.Yaml = m.Yaml
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf(`yaml: line %d: %q is not a valid variable type. Try "sh", "ref", "map", "json", "yaml" or using a scalar value`, node.Line, key)
|
||||
return errors.NewTaskfileDecodeError(nil, node).WithMessage(`%q is not a valid variable type. Try "sh", "ref", "map", "json", "yaml" or using a scalar value`, key)
|
||||
}
|
||||
default:
|
||||
var value any
|
||||
if err := node.Decode(&value); err != nil {
|
||||
return err
|
||||
return errors.NewTaskfileDecodeError(err, node)
|
||||
}
|
||||
v.Value = value
|
||||
return nil
|
||||
@ -149,13 +149,13 @@ func (v *Var) UnmarshalYAML(node *yaml.Node) error {
|
||||
|
||||
case yaml.MappingNode:
|
||||
if len(node.Content) > 2 || node.Content[0].Value != "sh" {
|
||||
return fmt.Errorf(`task: line %d: maps cannot be assigned to variables`, node.Line)
|
||||
return errors.NewTaskfileDecodeError(nil, node).WithMessage("maps cannot be assigned to variables")
|
||||
}
|
||||
var sh struct {
|
||||
Sh string
|
||||
}
|
||||
if err := node.Decode(&sh); err != nil {
|
||||
return err
|
||||
return errors.NewTaskfileDecodeError(err, node)
|
||||
}
|
||||
v.Sh = sh.Sh
|
||||
return nil
|
||||
@ -163,7 +163,7 @@ func (v *Var) UnmarshalYAML(node *yaml.Node) error {
|
||||
default:
|
||||
var value any
|
||||
if err := node.Decode(&value); err != nil {
|
||||
return err
|
||||
return errors.NewTaskfileDecodeError(err, node)
|
||||
}
|
||||
v.Value = value
|
||||
return nil
|
||||
|
@ -265,6 +265,11 @@ func (r *Reader) readNode(node Node) (*ast.Taskfile, error) {
|
||||
|
||||
var tf ast.Taskfile
|
||||
if err := yaml.Unmarshal(b, &tf); err != nil {
|
||||
// Decode the taskfile and add the file info the any errors
|
||||
taskfileInvalidErr := &errors.TaskfileDecodeError{}
|
||||
if errors.As(err, &taskfileInvalidErr) {
|
||||
return nil, taskfileInvalidErr.WithFileInfo(node.Location(), b, 2)
|
||||
}
|
||||
return nil, &errors.TaskfileInvalidError{URI: filepathext.TryAbsToRel(node.Location()), Err: err}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user