这是indexloc提供的服务,不要输入任何密码
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions taskfile/loader.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package taskfile

import (
stdErrors "errors"

"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/taskfile/ast"
)

// Loader defines the behavior required to load a Taskfile from raw data.
//
// Note: the returned [ast.Taskfile] is still backed by YAML-specific
// unmarshalling logic within the ast package. Loaders for alternative
// formats must populate the AST structures directly without relying on the
// YAML-only helpers. Future work will extract those bindings out of the ast
// package so it becomes truly format agnostic.
type Loader interface {
Load(data []byte, location string) (*ast.Taskfile, error)
}

// YAMLLoader implements [Loader] using YAML as the configuration format.
type YAMLLoader struct{}

// Load parses the given data as YAML into a Taskfile structure.
func (YAMLLoader) Load(data []byte, location string) (*ast.Taskfile, error) {
var tf ast.Taskfile
if err := yaml.Unmarshal(data, &tf); err != nil {
taskfileDecodeErr := &errors.TaskfileDecodeError{}
if stdErrors.As(err, &taskfileDecodeErr) {
snippet := NewSnippet(data,
WithLine(taskfileDecodeErr.Line),
WithColumn(taskfileDecodeErr.Column),
WithPadding(2),
)
return nil, taskfileDecodeErr.WithFileInfo(location, snippet.String())
}
return nil, &errors.TaskfileInvalidError{URI: filepathext.TryAbsToRel(location), Err: err}
}
return &tf, nil
}
23 changes: 23 additions & 0 deletions taskfile/loader_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package taskfile

import (
"testing"

"github.com/stretchr/testify/require"
"gopkg.in/yaml.v3"

"github.com/go-task/task/v3/taskfile/ast"
)

func TestYAMLLoader(t *testing.T) {
t.Parallel()

data := []byte("version: '3'\n\ntasks:\n default:\n cmds:\n - echo hello\n")
loader := YAMLLoader{}
tf, err := loader.Load(data, "Taskfile.yml")
require.NoError(t, err)

var expected ast.Taskfile
require.NoError(t, yaml.Unmarshal(data, &expected))
require.Equal(t, &expected, tf)
}
54 changes: 37 additions & 17 deletions taskfile/reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@ import (
"context"
"fmt"
"os"
"path/filepath"
"strings"
"sync"
"time"

"github.com/dominikbraun/graph"
"golang.org/x/sync/errgroup"
"gopkg.in/yaml.v3"

"github.com/go-task/task/v3/errors"
"github.com/go-task/task/v3/internal/env"
"github.com/go-task/task/v3/internal/filepathext"
"github.com/go-task/task/v3/internal/templater"
"github.com/go-task/task/v3/taskfile/ast"
)
Expand All @@ -40,6 +40,7 @@ type (
// [ast.TaskfileGraph] from them.
Reader struct {
graph *ast.TaskfileGraph
loaders map[string]Loader
insecure bool
download bool
offline bool
Expand All @@ -55,7 +56,11 @@ type (
// options.
func NewReader(opts ...ReaderOption) *Reader {
r := &Reader{
graph: ast.NewTaskfileGraph(),
graph: ast.NewTaskfileGraph(),
loaders: map[string]Loader{
".yml": YAMLLoader{},
".yaml": YAMLLoader{},
},
insecure: false,
download: false,
offline: false,
Expand Down Expand Up @@ -119,6 +124,24 @@ func (o *offlineOption) ApplyToReader(r *Reader) {
r.offline = o.offline
}

// WithLoader registers a new [Loader] for the given file extension. If a loader
// already exists for the extension, it will be replaced.
func WithLoader(ext string, loader Loader) ReaderOption {
return &loaderOption{ext: strings.ToLower(ext), loader: loader}
}

type loaderOption struct {
ext string
loader Loader
}

func (o *loaderOption) ApplyToReader(r *Reader) {
if r.loaders == nil {
r.loaders = map[string]Loader{}
}
r.loaders[o.ext] = o.loader
}

// WithTempDir sets the temporary directory that will be used by the [Reader].
// By default, the reader uses [os.TempDir].
func WithTempDir(tempDir string) ReaderOption {
Expand Down Expand Up @@ -324,19 +347,16 @@ func (r *Reader) readNode(ctx context.Context, node Node) (*ast.Taskfile, error)
return nil, err
}

var tf ast.Taskfile
if err := yaml.Unmarshal(b, &tf); err != nil {
// Decode the taskfile and add the file info the any errors
taskfileDecodeErr := &errors.TaskfileDecodeError{}
if errors.As(err, &taskfileDecodeErr) {
snippet := NewSnippet(b,
WithLine(taskfileDecodeErr.Line),
WithColumn(taskfileDecodeErr.Column),
WithPadding(2),
)
return nil, taskfileDecodeErr.WithFileInfo(node.Location(), snippet.String())
}
return nil, &errors.TaskfileInvalidError{URI: filepathext.TryAbsToRel(node.Location()), Err: err}
ext := strings.ToLower(filepath.Ext(node.Location()))
loader, ok := r.loaders[ext]
if !ok {
// Fallback to YAML loader if no loader is registered for the extension
loader = YAMLLoader{}
}

tf, err := loader.Load(b, node.Location())
if err != nil {
return nil, err
}

// Check that the Taskfile is set and has a schema version
Expand All @@ -357,7 +377,7 @@ func (r *Reader) readNode(ctx context.Context, node Node) (*ast.Taskfile, error)
}
}

return &tf, nil
return tf, nil
}

func (r *Reader) readNodeContent(ctx context.Context, node Node) ([]byte, error) {
Expand Down