From f204b96e5666d2b1b5873d532d1f3a11fd038c64 Mon Sep 17 00:00:00 2001 From: Timothy Rule <34501912+trulede@users.noreply.github.com> Date: Thu, 27 Nov 2025 21:05:46 +0100 Subject: [PATCH 1/2] Add taskfile node for byte[] objects. --- taskfile/node_byte.go | 49 +++++++++++++++++++++++ taskfile/node_byte_test.go | 25 ++++++++++++ taskfile/testdata/node_byte_taskfile.yaml | 10 +++++ 3 files changed, 84 insertions(+) create mode 100644 taskfile/node_byte.go create mode 100644 taskfile/node_byte_test.go create mode 100644 taskfile/testdata/node_byte_taskfile.yaml diff --git a/taskfile/node_byte.go b/taskfile/node_byte.go new file mode 100644 index 0000000000..e5a188f817 --- /dev/null +++ b/taskfile/node_byte.go @@ -0,0 +1,49 @@ +package taskfile + +import ( + "github.com/go-task/task/v3/internal/execext" + "github.com/go-task/task/v3/internal/filepathext" +) + +// A ByteNode is a node that reads a taskfile direct from a []byte object. +type ByteNode struct { + *baseNode + data []byte +} + +func NewByteNode(data []byte, dir string) (*ByteNode, error) { + return &ByteNode{ + baseNode: NewBaseNode(dir), + data: data, + }, nil +} + +func (node *ByteNode) Location() string { + return "__bytes__" +} + +func (node *ByteNode) Remote() bool { + return true +} + +func (node *ByteNode) Read() ([]byte, error) { + return node.data, nil +} + +func (node *ByteNode) ResolveEntrypoint(entrypoint string) (string, error) { + // A ByteNode has no presence on the local file system. + return entrypoint, nil +} + +func (node *ByteNode) ResolveDir(dir string) (string, error) { + path, err := execext.ExpandLiteral(dir) + if err != nil { + return "", err + } + + if filepathext.IsAbs(path) { + return path, nil + } + + return filepathext.SmartJoin(node.Dir(), path), nil +} diff --git a/taskfile/node_byte_test.go b/taskfile/node_byte_test.go new file mode 100644 index 0000000000..6cf09de74a --- /dev/null +++ b/taskfile/node_byte_test.go @@ -0,0 +1,25 @@ +package taskfile + +import ( + _ "embed" + "testing" + + "github.com/stretchr/testify/assert" +) + +//go:embed testdata/node_byte_taskfile.yaml +var taskfileYamlBytes []byte + +func TestByteNode(t *testing.T) { + t.Parallel() + workingDir := t.TempDir() + + node, err := NewByteNode(taskfileYamlBytes, workingDir) + assert.NoError(t, err) + assert.Equal(t, "__bytes__", node.Location()) + assert.Equal(t, workingDir, node.Dir()) + assert.True(t, node.Remote()) + data, err := node.Read() + assert.NoError(t, err) + assert.Equal(t, taskfileYamlBytes, data) +} diff --git a/taskfile/testdata/node_byte_taskfile.yaml b/taskfile/testdata/node_byte_taskfile.yaml new file mode 100644 index 0000000000..3177ca96b8 --- /dev/null +++ b/taskfile/testdata/node_byte_taskfile.yaml @@ -0,0 +1,10 @@ +version: '3' + +vars: + GREETING: Hello, World! + +tasks: + default: + cmds: + - echo "{{.GREETING}}" + silent: true From 87cf5bc78a9daabadcc5aa046a4a149605c95f8d Mon Sep 17 00:00:00 2001 From: Timothy Rule <34501912+trulede@users.noreply.github.com> Date: Sat, 29 Nov 2025 12:21:34 +0100 Subject: [PATCH 2/2] Add Executor WithTaskfile() to support Package API use cases. --- executor.go | 18 ++++++- executor_test.go | 48 +++++++++++++++++++ setup.go | 14 +++--- testdata/with_taskfile/build/Taskfile.yml | 6 +++ ...TestWithTaskfile-WithTaskfile_build.golden | 1 + testdata/with_taskfile/run/.gitkeep | 0 .../TestWithTaskfile-WithTaskfile_run.golden | 2 + 7 files changed, 82 insertions(+), 7 deletions(-) create mode 100644 testdata/with_taskfile/build/Taskfile.yml create mode 100644 testdata/with_taskfile/build/testdata/TestWithTaskfile-WithTaskfile_build.golden create mode 100644 testdata/with_taskfile/run/.gitkeep create mode 100644 testdata/with_taskfile/run/testdata/TestWithTaskfile-WithTaskfile_run.golden diff --git a/executor.go b/executor.go index 6ecf910a5b..f7d73fd130 100644 --- a/executor.go +++ b/executor.go @@ -53,8 +53,10 @@ type ( Stdout io.Writer Stderr io.Writer + // Taskfile + Taskfile *ast.Taskfile + // Internal - Taskfile *ast.Taskfile Logger *logger.Logger Compiler *Compiler Output output.Output @@ -86,6 +88,7 @@ func NewExecutor(opts ...ExecutorOption) *Executor { Stdin: os.Stdin, Stdout: os.Stdout, Stderr: os.Stderr, + Taskfile: nil, Logger: nil, Compiler: nil, Output: nil, @@ -502,3 +505,16 @@ type versionCheckOption struct { func (o *versionCheckOption) ApplyToExecutor(e *Executor) { e.EnableVersionCheck = o.enableVersionCheck } + +// WithTaskfile set the [Executor]'s Taskfile to the provided [ast.Taskfile]. +func WithTaskfile(taskfile *ast.Taskfile) ExecutorOption { + return &taskfileOption{taskfile} +} + +type taskfileOption struct { + taskfile *ast.Taskfile +} + +func (o *taskfileOption) ApplyToExecutor(e *Executor) { + e.Taskfile = o.taskfile +} diff --git a/executor_test.go b/executor_test.go index 7f6a3feb4b..aa3fbe6bbd 100644 --- a/executor_test.go +++ b/executor_test.go @@ -37,6 +37,7 @@ type ( wantSetupError bool wantRunError bool wantStatusError bool + callbackFunc func(e *task.Executor) } ) @@ -113,6 +114,21 @@ func (opt *statusErrorTestOption) applyToExecutorTest(t *ExecutorTest) { t.wantStatusError = true } +// WithCallback calls the provided function after the the test execution. +func WithCallback(f func(e *task.Executor)) ExecutorTestOption { + return &callbackTestOption{ + callback: f, + } +} + +type callbackTestOption struct { + callback func(e *task.Executor) +} + +func (opt *callbackTestOption) applyToExecutorTest(t *ExecutorTest) { + t.callbackFunc = opt.callback +} + // Helpers // writeFixtureErrRun is a wrapper for writing the output of an error during the @@ -161,6 +177,11 @@ func (tt *ExecutorTest) run(t *testing.T) { // Set up the task executor e := task.NewExecutor(opts...) + if tt.callbackFunc != nil { + defer func() { + tt.callbackFunc(e) + }() + } // Create a golden fixture file for the output g := goldie.New(t, @@ -996,3 +1017,30 @@ func TestIncludeChecksum(t *testing.T) { WithFixtureTemplating(), ) } + +func TestWithTaskfile(t *testing.T) { + t.Parallel() + + // Build an ast.Taskfile (using an executor). + var executor *task.Executor + NewExecutorTest(t, + WithName("WithTaskfile build"), + WithExecutorOptions( + task.WithDir("testdata/with_taskfile/build"), + task.WithDry(true), + ), + WithCallback(func(e *task.Executor) { + executor = e + }), + ) + require.NotNil(t, executor) + + // Run executor using the pre-built ast.Taskfile. + NewExecutorTest(t, + WithName("WithTaskfile run"), + WithExecutorOptions( + task.WithDir("testdata/with_taskfile/run"), + task.WithTaskfile(executor.Taskfile), + ), + ) +} diff --git a/setup.go b/setup.go index 6234ceefa0..49c2bd797f 100644 --- a/setup.go +++ b/setup.go @@ -26,15 +26,17 @@ import ( func (e *Executor) Setup() error { e.setupLogger() - node, err := e.getRootNode() - if err != nil { - return err - } if err := e.setupTempDir(); err != nil { return err } - if err := e.readTaskfile(node); err != nil { - return err + if e.Taskfile == nil { + node, err := e.getRootNode() + if err != nil { + return err + } + if err := e.readTaskfile(node); err != nil { + return err + } } e.setupFuzzyModel() e.setupStdFiles() diff --git a/testdata/with_taskfile/build/Taskfile.yml b/testdata/with_taskfile/build/Taskfile.yml new file mode 100644 index 0000000000..979318ad0c --- /dev/null +++ b/testdata/with_taskfile/build/Taskfile.yml @@ -0,0 +1,6 @@ +version: "3" + +tasks: + default: + cmds: + - echo "Fubar" \ No newline at end of file diff --git a/testdata/with_taskfile/build/testdata/TestWithTaskfile-WithTaskfile_build.golden b/testdata/with_taskfile/build/testdata/TestWithTaskfile-WithTaskfile_build.golden new file mode 100644 index 0000000000..a0714969b6 --- /dev/null +++ b/testdata/with_taskfile/build/testdata/TestWithTaskfile-WithTaskfile_build.golden @@ -0,0 +1 @@ +task: [default] echo "Fubar" diff --git a/testdata/with_taskfile/run/.gitkeep b/testdata/with_taskfile/run/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/testdata/with_taskfile/run/testdata/TestWithTaskfile-WithTaskfile_run.golden b/testdata/with_taskfile/run/testdata/TestWithTaskfile-WithTaskfile_run.golden new file mode 100644 index 0000000000..d4ed1f499e --- /dev/null +++ b/testdata/with_taskfile/run/testdata/TestWithTaskfile-WithTaskfile_run.golden @@ -0,0 +1,2 @@ +task: [default] echo "Fubar" +Fubar