mirror of
https://github.com/go-task/task.git
synced 2026-06-16 12:21:41 +00:00
Compare commits
1 Commits
split-exec
...
bump-versi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3c30a8066d |
2
.github/workflows/release-nightly.yml
vendored
2
.github/workflows/release-nightly.yml
vendored
@@ -23,7 +23,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
distribution: goreleaser-pro
|
distribution: goreleaser-pro
|
||||||
version: latest
|
version: latest
|
||||||
args: release --clean --nightly -f .goreleaser-nightly.yml
|
args: release --clean --nightly
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{secrets.GH_PAT}}
|
GITHUB_TOKEN: ${{secrets.GH_PAT}}
|
||||||
GORELEASER_KEY: ${{secrets.GORELEASER_KEY}}
|
GORELEASER_KEY: ${{secrets.GORELEASER_KEY}}
|
||||||
|
|||||||
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -24,7 +24,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
distribution: goreleaser-pro
|
distribution: goreleaser-pro
|
||||||
version: latest
|
version: latest
|
||||||
args: release --clean --draft
|
args: release --clean
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{secrets.GH_PAT}}
|
GITHUB_TOKEN: ${{secrets.GH_PAT}}
|
||||||
GORELEASER_KEY: ${{secrets.GORELEASER_KEY}}
|
GORELEASER_KEY: ${{secrets.GORELEASER_KEY}}
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
# yaml-language-server: $schema=https://goreleaser.com/static/schema.json
|
|
||||||
version: 2
|
|
||||||
pro: true
|
|
||||||
|
|
||||||
release:
|
|
||||||
name_template: 'v{{.Version}}'
|
|
||||||
|
|
||||||
nightly:
|
|
||||||
publish_release: true
|
|
||||||
keep_single_release: true
|
|
||||||
version_template: "{{incminor .Version}}-nightly"
|
|
||||||
|
|
||||||
includes:
|
|
||||||
- from_file:
|
|
||||||
path: ./.goreleaser.yml
|
|
||||||
@@ -30,8 +30,7 @@ builds:
|
|||||||
flags:
|
flags:
|
||||||
- -trimpath
|
- -trimpath
|
||||||
ldflags:
|
ldflags:
|
||||||
- "-s -w"
|
- -s -w # Don't set main.version.
|
||||||
- "{{if .IsNightly}}-X github.com/go-task/task/v3/internal/version.version={{.Version}}{{end}}"
|
|
||||||
|
|
||||||
gomod:
|
gomod:
|
||||||
proxy: true
|
proxy: true
|
||||||
@@ -46,10 +45,19 @@ archives:
|
|||||||
- goos: windows
|
- goos: windows
|
||||||
formats: [zip]
|
formats: [zip]
|
||||||
|
|
||||||
|
release:
|
||||||
|
draft: true
|
||||||
|
|
||||||
|
|
||||||
git:
|
git:
|
||||||
ignore_tags:
|
ignore_tags:
|
||||||
- "{{if not .IsNightly}}nightly{{end}}"
|
- "{{if not .IsNightly}}nightly{{end}}"
|
||||||
|
|
||||||
|
nightly:
|
||||||
|
publish_release: true
|
||||||
|
keep_single_release: true
|
||||||
|
version_template: "{{incminor .Version}}-nightly"
|
||||||
|
|
||||||
snapshot:
|
snapshot:
|
||||||
version_template: '{{.Version}}'
|
version_template: '{{.Version}}'
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ import (
|
|||||||
"github.com/go-task/task/v3/internal/flags"
|
"github.com/go-task/task/v3/internal/flags"
|
||||||
"github.com/go-task/task/v3/internal/logger"
|
"github.com/go-task/task/v3/internal/logger"
|
||||||
"github.com/go-task/task/v3/internal/version"
|
"github.com/go-task/task/v3/internal/version"
|
||||||
"github.com/go-task/task/v3/taskfile"
|
|
||||||
"github.com/go-task/task/v3/taskfile/ast"
|
"github.com/go-task/task/v3/taskfile/ast"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -111,63 +110,16 @@ func run() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := experiments.Validate(); err != nil {
|
e := task.NewExecutor(
|
||||||
log.Warnf("%s\n", err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a new root node for the given entrypoint
|
|
||||||
node, err := taskfile.NewRootNode(
|
|
||||||
flags.Entrypoint,
|
|
||||||
flags.Dir,
|
|
||||||
flags.Insecure,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
tempDir, err := task.NewTempDir(node.Dir())
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
reader := taskfile.NewReader(
|
|
||||||
flags.WithFlags(),
|
flags.WithFlags(),
|
||||||
taskfile.WithTempDir(tempDir.Remote),
|
task.WithVersionCheck(true),
|
||||||
taskfile.WithDebugFunc(func(s string) {
|
|
||||||
log.VerboseOutf(logger.Magenta, s)
|
|
||||||
}),
|
|
||||||
taskfile.WithPromptFunc(func(s string) error {
|
|
||||||
return log.Prompt(logger.Yellow, s, "n", "y", "yes")
|
|
||||||
}),
|
|
||||||
)
|
)
|
||||||
|
if err := e.Setup(); err != nil {
|
||||||
ctx, cf := context.WithTimeout(context.Background(), flags.Timeout)
|
|
||||||
defer cf()
|
|
||||||
graph, err := reader.Read(ctx, node)
|
|
||||||
if err != nil {
|
|
||||||
if errors.Is(err, context.DeadlineExceeded) {
|
|
||||||
return &errors.TaskfileNetworkTimeoutError{URI: node.Location(), Timeout: flags.Timeout}
|
|
||||||
}
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
executor, err := task.NewExecutor(graph,
|
|
||||||
flags.WithFlags(),
|
|
||||||
task.WithDir(node.Dir()),
|
|
||||||
task.WithTempDir(tempDir),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the download flag is specified, we should stop execution as soon as
|
|
||||||
// taskfile is downloaded
|
|
||||||
if flags.Download {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if flags.ClearCache {
|
if flags.ClearCache {
|
||||||
cachePath := filepath.Join(executor.TempDir.Remote, "remote")
|
cachePath := filepath.Join(e.TempDir.Remote, "remote")
|
||||||
return os.RemoveAll(cachePath)
|
return os.RemoveAll(cachePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -179,9 +131,9 @@ func run() error {
|
|||||||
)
|
)
|
||||||
if listOptions.ShouldListTasks() {
|
if listOptions.ShouldListTasks() {
|
||||||
if flags.Silent {
|
if flags.Silent {
|
||||||
return executor.ListTaskNames(flags.ListAll)
|
return e.ListTaskNames(flags.ListAll)
|
||||||
}
|
}
|
||||||
foundTasks, err := executor.ListTasks(listOptions)
|
foundTasks, err := e.ListTasks(listOptions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -213,17 +165,17 @@ func run() error {
|
|||||||
globals.Set("CLI_SILENT", ast.Var{Value: flags.Silent})
|
globals.Set("CLI_SILENT", ast.Var{Value: flags.Silent})
|
||||||
globals.Set("CLI_VERBOSE", ast.Var{Value: flags.Verbose})
|
globals.Set("CLI_VERBOSE", ast.Var{Value: flags.Verbose})
|
||||||
globals.Set("CLI_OFFLINE", ast.Var{Value: flags.Offline})
|
globals.Set("CLI_OFFLINE", ast.Var{Value: flags.Offline})
|
||||||
executor.Taskfile.Vars.Merge(globals, nil)
|
e.Taskfile.Vars.Merge(globals, nil)
|
||||||
|
|
||||||
if !flags.Watch {
|
if !flags.Watch {
|
||||||
executor.InterceptInterruptSignals()
|
e.InterceptInterruptSignals()
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx = context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
if flags.Status {
|
if flags.Status {
|
||||||
return executor.Status(ctx, calls...)
|
return e.Status(ctx, calls...)
|
||||||
}
|
}
|
||||||
|
|
||||||
return executor.Run(ctx, calls...)
|
return e.Run(ctx, calls...)
|
||||||
}
|
}
|
||||||
|
|||||||
150
executor.go
150
executor.go
@@ -4,7 +4,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -27,21 +26,27 @@ type (
|
|||||||
// within them.
|
// within them.
|
||||||
Executor struct {
|
Executor struct {
|
||||||
// Flags
|
// Flags
|
||||||
Dir string
|
Dir string
|
||||||
TempDir *TempDir
|
Entrypoint string
|
||||||
Force bool
|
TempDir TempDir
|
||||||
ForceAll bool
|
Force bool
|
||||||
Watch bool
|
ForceAll bool
|
||||||
Verbose bool
|
Insecure bool
|
||||||
Silent bool
|
Download bool
|
||||||
AssumeYes bool
|
Offline bool
|
||||||
AssumeTerm bool // Used for testing
|
Timeout time.Duration
|
||||||
Dry bool
|
CacheExpiryDuration time.Duration
|
||||||
Summary bool
|
Watch bool
|
||||||
Parallel bool
|
Verbose bool
|
||||||
Color bool
|
Silent bool
|
||||||
Concurrency int
|
AssumeYes bool
|
||||||
Interval time.Duration
|
AssumeTerm bool // Used for testing
|
||||||
|
Dry bool
|
||||||
|
Summary bool
|
||||||
|
Parallel bool
|
||||||
|
Color bool
|
||||||
|
Concurrency int
|
||||||
|
Interval time.Duration
|
||||||
|
|
||||||
// I/O
|
// I/O
|
||||||
Stdin io.Reader
|
Stdin io.Reader
|
||||||
@@ -67,17 +72,17 @@ type (
|
|||||||
executionHashesMutex sync.Mutex
|
executionHashesMutex sync.Mutex
|
||||||
watchedDirs *xsync.MapOf[string, bool]
|
watchedDirs *xsync.MapOf[string, bool]
|
||||||
}
|
}
|
||||||
|
TempDir struct {
|
||||||
|
Remote string
|
||||||
|
Fingerprint string
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewExecutor creates a new [Executor] and applies the given functional options
|
// NewExecutor creates a new [Executor] and applies the given functional options
|
||||||
// to it.
|
// to it.
|
||||||
func NewExecutor(graph *ast.TaskfileGraph, opts ...ExecutorOption) (*Executor, error) {
|
func NewExecutor(opts ...ExecutorOption) *Executor {
|
||||||
tf, err := graph.Merge()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
e := &Executor{
|
e := &Executor{
|
||||||
Taskfile: tf,
|
Timeout: time.Second * 10,
|
||||||
Stdin: os.Stdin,
|
Stdin: os.Stdin,
|
||||||
Stdout: os.Stdout,
|
Stdout: os.Stdout,
|
||||||
Stderr: os.Stderr,
|
Stderr: os.Stderr,
|
||||||
@@ -95,10 +100,7 @@ func NewExecutor(graph *ast.TaskfileGraph, opts ...ExecutorOption) (*Executor, e
|
|||||||
executionHashesMutex: sync.Mutex{},
|
executionHashesMutex: sync.Mutex{},
|
||||||
}
|
}
|
||||||
e.Options(opts...)
|
e.Options(opts...)
|
||||||
if err := e.setup(); err != nil {
|
return e
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return e, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Options loops through the given [ExecutorOption] functions and applies them
|
// Options loops through the given [ExecutorOption] functions and applies them
|
||||||
@@ -120,23 +122,33 @@ type dirOption struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (o *dirOption) ApplyToExecutor(e *Executor) {
|
func (o *dirOption) ApplyToExecutor(e *Executor) {
|
||||||
absDir, err := filepath.Abs(o.dir)
|
e.Dir = o.dir
|
||||||
if err != nil {
|
}
|
||||||
e.Dir = o.dir
|
|
||||||
return
|
// WithEntrypoint sets the entrypoint (main Taskfile) of the [Executor]. By
|
||||||
}
|
// default, Task will search for one of the default Taskfiles in the given
|
||||||
e.Dir = absDir
|
// directory.
|
||||||
|
func WithEntrypoint(entrypoint string) ExecutorOption {
|
||||||
|
return &entrypointOption{entrypoint}
|
||||||
|
}
|
||||||
|
|
||||||
|
type entrypointOption struct {
|
||||||
|
entrypoint string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *entrypointOption) ApplyToExecutor(e *Executor) {
|
||||||
|
e.Entrypoint = o.entrypoint
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithTempDir sets the temporary directory that will be used by [Executor] for
|
// WithTempDir sets the temporary directory that will be used by [Executor] for
|
||||||
// storing temporary files like checksums and cached remote files. By default,
|
// storing temporary files like checksums and cached remote files. By default,
|
||||||
// the temporary directory is set to the user's temporary directory.
|
// the temporary directory is set to the user's temporary directory.
|
||||||
func WithTempDir(tempDir *TempDir) ExecutorOption {
|
func WithTempDir(tempDir TempDir) ExecutorOption {
|
||||||
return &tempDirOption{tempDir}
|
return &tempDirOption{tempDir}
|
||||||
}
|
}
|
||||||
|
|
||||||
type tempDirOption struct {
|
type tempDirOption struct {
|
||||||
tempDir *TempDir
|
tempDir TempDir
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *tempDirOption) ApplyToExecutor(e *Executor) {
|
func (o *tempDirOption) ApplyToExecutor(e *Executor) {
|
||||||
@@ -171,6 +183,76 @@ func (o *forceAllOption) ApplyToExecutor(e *Executor) {
|
|||||||
e.ForceAll = o.forceAll
|
e.ForceAll = o.forceAll
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithInsecure allows the [Executor] to make insecure connections when reading
|
||||||
|
// remote taskfiles. By default, insecure connections are rejected.
|
||||||
|
func WithInsecure(insecure bool) ExecutorOption {
|
||||||
|
return &insecureOption{insecure}
|
||||||
|
}
|
||||||
|
|
||||||
|
type insecureOption struct {
|
||||||
|
insecure bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *insecureOption) ApplyToExecutor(e *Executor) {
|
||||||
|
e.Insecure = o.insecure
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithDownload forces the [Executor] to download a fresh copy of the taskfile
|
||||||
|
// from the remote source.
|
||||||
|
func WithDownload(download bool) ExecutorOption {
|
||||||
|
return &downloadOption{download}
|
||||||
|
}
|
||||||
|
|
||||||
|
type downloadOption struct {
|
||||||
|
download bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *downloadOption) ApplyToExecutor(e *Executor) {
|
||||||
|
e.Download = o.download
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithOffline stops the [Executor] from being able to make network connections.
|
||||||
|
// It will still be able to read local files and cached copies of remote files.
|
||||||
|
func WithOffline(offline bool) ExecutorOption {
|
||||||
|
return &offlineOption{offline}
|
||||||
|
}
|
||||||
|
|
||||||
|
type offlineOption struct {
|
||||||
|
offline bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *offlineOption) ApplyToExecutor(e *Executor) {
|
||||||
|
e.Offline = o.offline
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithTimeout sets the [Executor]'s timeout for fetching remote taskfiles. By
|
||||||
|
// default, the timeout is set to 10 seconds.
|
||||||
|
func WithTimeout(timeout time.Duration) ExecutorOption {
|
||||||
|
return &timeoutOption{timeout}
|
||||||
|
}
|
||||||
|
|
||||||
|
type timeoutOption struct {
|
||||||
|
timeout time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *timeoutOption) ApplyToExecutor(e *Executor) {
|
||||||
|
e.Timeout = o.timeout
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithCacheExpiryDuration sets the duration after which the cache is considered
|
||||||
|
// expired. By default, the cache is considered expired after 24 hours.
|
||||||
|
func WithCacheExpiryDuration(duration time.Duration) ExecutorOption {
|
||||||
|
return &cacheExpiryDurationOption{duration: duration}
|
||||||
|
}
|
||||||
|
|
||||||
|
type cacheExpiryDurationOption struct {
|
||||||
|
duration time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *cacheExpiryDurationOption) ApplyToExecutor(r *Executor) {
|
||||||
|
r.CacheExpiryDuration = o.duration
|
||||||
|
}
|
||||||
|
|
||||||
// WithWatch tells the [Executor] to keep running in the background and watch
|
// WithWatch tells the [Executor] to keep running in the background and watch
|
||||||
// for changes to the fingerprint of the tasks that are run. When changes are
|
// for changes to the fingerprint of the tasks that are run. When changes are
|
||||||
// detected, a new task run is triggered.
|
// detected, a new task run is triggered.
|
||||||
|
|||||||
313
executor_test.go
313
executor_test.go
@@ -7,7 +7,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"slices"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/sebdah/goldie/v2"
|
"github.com/sebdah/goldie/v2"
|
||||||
@@ -16,7 +15,6 @@ import (
|
|||||||
"github.com/go-task/task/v3"
|
"github.com/go-task/task/v3"
|
||||||
"github.com/go-task/task/v3/experiments"
|
"github.com/go-task/task/v3/experiments"
|
||||||
"github.com/go-task/task/v3/internal/filepathext"
|
"github.com/go-task/task/v3/internal/filepathext"
|
||||||
"github.com/go-task/task/v3/taskfile"
|
|
||||||
"github.com/go-task/task/v3/taskfile/ast"
|
"github.com/go-task/task/v3/taskfile/ast"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -36,12 +34,7 @@ type (
|
|||||||
task string
|
task string
|
||||||
vars map[string]any
|
vars map[string]any
|
||||||
input string
|
input string
|
||||||
nodeDir string
|
|
||||||
nodeEntrypoint string
|
|
||||||
nodeInsecure bool
|
|
||||||
readerOpts []taskfile.ReaderOption
|
|
||||||
executorOpts []task.ExecutorOption
|
executorOpts []task.ExecutorOption
|
||||||
wantReaderError bool
|
|
||||||
wantSetupError bool
|
wantSetupError bool
|
||||||
wantRunError bool
|
wantRunError bool
|
||||||
wantStatusError bool
|
wantStatusError bool
|
||||||
@@ -54,9 +47,8 @@ type (
|
|||||||
func NewExecutorTest(t *testing.T, opts ...ExecutorTestOption) {
|
func NewExecutorTest(t *testing.T, opts ...ExecutorTestOption) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
tt := &ExecutorTest{
|
tt := &ExecutorTest{
|
||||||
task: "default",
|
task: "default",
|
||||||
vars: map[string]any{},
|
vars: map[string]any{},
|
||||||
nodeDir: ".",
|
|
||||||
TaskTest: TaskTest{
|
TaskTest: TaskTest{
|
||||||
experiments: map[*experiments.Experiment]int{},
|
experiments: map[*experiments.Experiment]int{},
|
||||||
fixtureTemplateData: map[string]any{},
|
fixtureTemplateData: map[string]any{},
|
||||||
@@ -153,52 +145,11 @@ func (tt *ExecutorTest) run(t *testing.T) {
|
|||||||
f := func(t *testing.T) {
|
f := func(t *testing.T) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
ctx := context.Background()
|
|
||||||
|
|
||||||
// Create a new root node for the given entrypoint
|
opts := append(
|
||||||
node, err := taskfile.NewRootNode(
|
|
||||||
tt.nodeEntrypoint,
|
|
||||||
tt.nodeDir,
|
|
||||||
tt.nodeInsecure,
|
|
||||||
)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
// Create a golden fixture file for the output
|
|
||||||
g := goldie.New(t,
|
|
||||||
goldie.WithFixtureDir(filepath.Join(node.Dir(), "testdata")),
|
|
||||||
)
|
|
||||||
|
|
||||||
// Set up a temporary directory for the taskfile reader and task executor
|
|
||||||
tempDir, err := task.NewTempDir(node.Dir())
|
|
||||||
require.NoError(t, err)
|
|
||||||
tt.readerOpts = append(tt.readerOpts, taskfile.WithTempDir(tempDir.Remote))
|
|
||||||
|
|
||||||
// Set up the taskfile reader
|
|
||||||
reader := taskfile.NewReader(tt.readerOpts...)
|
|
||||||
graph, err := reader.Read(ctx, node)
|
|
||||||
if tt.wantReaderError {
|
|
||||||
require.Error(t, err)
|
|
||||||
tt.writeFixtureErrReader(t, g, err)
|
|
||||||
tt.writeFixtureBuffer(t, g, buf)
|
|
||||||
return
|
|
||||||
} else {
|
|
||||||
require.NoError(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
executorOpts := slices.Concat(
|
|
||||||
// Apply the node directory and temp directory to the executor options
|
|
||||||
// by default, but allow them to by overridden by the test options
|
|
||||||
[]task.ExecutorOption{
|
|
||||||
task.WithDir(node.Dir()),
|
|
||||||
task.WithTempDir(tempDir),
|
|
||||||
},
|
|
||||||
// Apply the executor options from the test
|
|
||||||
tt.executorOpts,
|
tt.executorOpts,
|
||||||
// Force the input/output streams to be set to the test buffer
|
task.WithStdout(&buf),
|
||||||
[]task.ExecutorOption{
|
task.WithStderr(&buf),
|
||||||
task.WithStdout(&buf),
|
|
||||||
task.WithStderr(&buf),
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// If the test has input, create a reader for it and add it to the
|
// If the test has input, create a reader for it and add it to the
|
||||||
@@ -206,12 +157,19 @@ func (tt *ExecutorTest) run(t *testing.T) {
|
|||||||
if tt.input != "" {
|
if tt.input != "" {
|
||||||
var reader bytes.Buffer
|
var reader bytes.Buffer
|
||||||
reader.WriteString(tt.input)
|
reader.WriteString(tt.input)
|
||||||
executorOpts = append(executorOpts, task.WithStdin(&reader))
|
opts = append(opts, task.WithStdin(&reader))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set up the task executor
|
// Set up the task executor
|
||||||
executor, err := task.NewExecutor(graph, executorOpts...)
|
e := task.NewExecutor(opts...)
|
||||||
if tt.wantSetupError {
|
|
||||||
|
// Create a golden fixture file for the output
|
||||||
|
g := goldie.New(t,
|
||||||
|
goldie.WithFixtureDir(filepath.Join(e.Dir, "testdata")),
|
||||||
|
)
|
||||||
|
|
||||||
|
// Call setup and check for errors
|
||||||
|
if err := e.Setup(); tt.wantSetupError {
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
tt.writeFixtureErrSetup(t, g, err)
|
tt.writeFixtureErrSetup(t, g, err)
|
||||||
tt.writeFixtureBuffer(t, g, buf)
|
tt.writeFixtureBuffer(t, g, buf)
|
||||||
@@ -231,7 +189,8 @@ func (tt *ExecutorTest) run(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Run the task and check for errors
|
// Run the task and check for errors
|
||||||
if err := executor.Run(ctx, call); tt.wantRunError {
|
ctx := context.Background()
|
||||||
|
if err := e.Run(ctx, call); tt.wantRunError {
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
tt.writeFixtureErrRun(t, g, err)
|
tt.writeFixtureErrRun(t, g, err)
|
||||||
tt.writeFixtureBuffer(t, g, buf)
|
tt.writeFixtureBuffer(t, g, buf)
|
||||||
@@ -242,7 +201,7 @@ func (tt *ExecutorTest) run(t *testing.T) {
|
|||||||
|
|
||||||
// If the status flag is set, run the status check
|
// If the status flag is set, run the status check
|
||||||
if tt.wantStatusError {
|
if tt.wantStatusError {
|
||||||
if err := executor.Status(ctx, call); err != nil {
|
if err := e.Status(ctx, call); err != nil {
|
||||||
tt.writeFixtureStatus(t, g, err.Error())
|
tt.writeFixtureStatus(t, g, err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -261,16 +220,19 @@ func (tt *ExecutorTest) run(t *testing.T) {
|
|||||||
func TestEmptyTask(t *testing.T) {
|
func TestEmptyTask(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithNodeDir("testdata/empty_task"),
|
WithExecutorOptions(
|
||||||
WithExecutorOptions(),
|
task.WithDir("testdata/empty_task"),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEmptyTaskfile(t *testing.T) {
|
func TestEmptyTaskfile(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithNodeDir("testdata/empty_taskfile"),
|
WithExecutorOptions(
|
||||||
WithReaderError(),
|
task.WithDir("testdata/empty_taskfile"),
|
||||||
|
),
|
||||||
|
WithSetupError(),
|
||||||
WithFixtureTemplating(),
|
WithFixtureTemplating(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -279,15 +241,15 @@ func TestEnv(t *testing.T) {
|
|||||||
t.Setenv("QUX", "from_os")
|
t.Setenv("QUX", "from_os")
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("env precedence disabled"),
|
WithName("env precedence disabled"),
|
||||||
WithNodeDir("testdata/env"),
|
|
||||||
WithExecutorOptions(
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/env"),
|
||||||
task.WithSilent(true),
|
task.WithSilent(true),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("env precedence enabled"),
|
WithName("env precedence enabled"),
|
||||||
WithNodeDir("testdata/env"),
|
|
||||||
WithExecutorOptions(
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/env"),
|
||||||
task.WithSilent(true),
|
task.WithSilent(true),
|
||||||
),
|
),
|
||||||
WithExperiment(&experiments.EnvPrecedence, 1),
|
WithExperiment(&experiments.EnvPrecedence, 1),
|
||||||
@@ -297,8 +259,8 @@ func TestEnv(t *testing.T) {
|
|||||||
func TestVars(t *testing.T) {
|
func TestVars(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithNodeDir("testdata/vars"),
|
|
||||||
WithExecutorOptions(
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/vars"),
|
||||||
task.WithSilent(true),
|
task.WithSilent(true),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@@ -308,19 +270,25 @@ func TestRequires(t *testing.T) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("required var missing"),
|
WithName("required var missing"),
|
||||||
WithNodeDir("testdata/requires"),
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/requires"),
|
||||||
|
),
|
||||||
WithTask("missing-var"),
|
WithTask("missing-var"),
|
||||||
WithRunError(),
|
WithRunError(),
|
||||||
)
|
)
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("required var ok"),
|
WithName("required var ok"),
|
||||||
WithNodeDir("testdata/requires"),
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/requires"),
|
||||||
|
),
|
||||||
WithTask("missing-var"),
|
WithTask("missing-var"),
|
||||||
WithVar("FOO", "bar"),
|
WithVar("FOO", "bar"),
|
||||||
)
|
)
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("fails validation"),
|
WithName("fails validation"),
|
||||||
WithNodeDir("testdata/requires"),
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/requires"),
|
||||||
|
),
|
||||||
WithTask("validation-var"),
|
WithTask("validation-var"),
|
||||||
WithVar("ENV", "dev"),
|
WithVar("ENV", "dev"),
|
||||||
WithVar("FOO", "bar"),
|
WithVar("FOO", "bar"),
|
||||||
@@ -328,37 +296,48 @@ func TestRequires(t *testing.T) {
|
|||||||
)
|
)
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("passes validation"),
|
WithName("passes validation"),
|
||||||
WithNodeDir("testdata/requires"),
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/requires"),
|
||||||
|
),
|
||||||
WithTask("validation-var"),
|
WithTask("validation-var"),
|
||||||
WithVar("FOO", "one"),
|
WithVar("FOO", "one"),
|
||||||
WithVar("ENV", "dev"),
|
WithVar("ENV", "dev"),
|
||||||
)
|
)
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("required var missing + fails validation"),
|
WithName("required var missing + fails validation"),
|
||||||
WithNodeDir("testdata/requires"),
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/requires"),
|
||||||
|
),
|
||||||
WithTask("validation-var"),
|
WithTask("validation-var"),
|
||||||
WithRunError(),
|
WithRunError(),
|
||||||
)
|
)
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("required var missing + fails validation"),
|
WithName("required var missing + fails validation"),
|
||||||
WithNodeDir("testdata/requires"),
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/requires"),
|
||||||
|
),
|
||||||
WithTask("validation-var-dynamic"),
|
WithTask("validation-var-dynamic"),
|
||||||
WithVar("FOO", "one"),
|
WithVar("FOO", "one"),
|
||||||
WithVar("ENV", "dev"),
|
WithVar("ENV", "dev"),
|
||||||
)
|
)
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("require before compile"),
|
WithName("require before compile"),
|
||||||
WithNodeDir("testdata/requires"),
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/requires"),
|
||||||
|
),
|
||||||
WithTask("require-before-compile"),
|
WithTask("require-before-compile"),
|
||||||
WithRunError(),
|
WithRunError(),
|
||||||
)
|
)
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("var defined in task"),
|
WithName("var defined in task"),
|
||||||
WithNodeDir("testdata/requires"),
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/requires"),
|
||||||
|
),
|
||||||
WithTask("var-defined-in-task"),
|
WithTask("var-defined-in-task"),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: mock fs
|
||||||
func TestSpecialVars(t *testing.T) {
|
func TestSpecialVars(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
@@ -379,13 +358,12 @@ func TestSpecialVars(t *testing.T) {
|
|||||||
"included:print-taskfile-dir",
|
"included:print-taskfile-dir",
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, executorDir := range []string{dir, subdir} {
|
for _, dir := range []string{dir, subdir} {
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
name := fmt.Sprintf("%s-%s", executorDir, test)
|
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName(name),
|
WithName(fmt.Sprintf("%s-%s", dir, test)),
|
||||||
WithNodeDir(executorDir),
|
|
||||||
WithExecutorOptions(
|
WithExecutorOptions(
|
||||||
|
task.WithDir(dir),
|
||||||
task.WithSilent(true),
|
task.WithSilent(true),
|
||||||
task.WithVersionCheck(true),
|
task.WithVersionCheck(true),
|
||||||
),
|
),
|
||||||
@@ -399,8 +377,8 @@ func TestSpecialVars(t *testing.T) {
|
|||||||
func TestConcurrency(t *testing.T) {
|
func TestConcurrency(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithNodeDir("testdata/concurrency"),
|
|
||||||
WithExecutorOptions(
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/concurrency"),
|
||||||
task.WithConcurrency(1),
|
task.WithConcurrency(1),
|
||||||
),
|
),
|
||||||
WithPostProcessFn(PPSortedLines),
|
WithPostProcessFn(PPSortedLines),
|
||||||
@@ -410,8 +388,8 @@ func TestConcurrency(t *testing.T) {
|
|||||||
func TestParams(t *testing.T) {
|
func TestParams(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithNodeDir("testdata/params"),
|
|
||||||
WithExecutorOptions(
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/params"),
|
||||||
task.WithSilent(true),
|
task.WithSilent(true),
|
||||||
),
|
),
|
||||||
WithPostProcessFn(PPSortedLines),
|
WithPostProcessFn(PPSortedLines),
|
||||||
@@ -421,14 +399,15 @@ func TestParams(t *testing.T) {
|
|||||||
func TestDeps(t *testing.T) {
|
func TestDeps(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithNodeDir("testdata/deps"),
|
|
||||||
WithExecutorOptions(
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/deps"),
|
||||||
task.WithSilent(true),
|
task.WithSilent(true),
|
||||||
),
|
),
|
||||||
WithPostProcessFn(PPSortedLines),
|
WithPostProcessFn(PPSortedLines),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: mock fs
|
||||||
func TestStatus(t *testing.T) {
|
func TestStatus(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
@@ -451,8 +430,8 @@ func TestStatus(t *testing.T) {
|
|||||||
// gen-foo creates foo.txt, and will always fail it's status check.
|
// gen-foo creates foo.txt, and will always fail it's status check.
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("run gen-foo 1 silent"),
|
WithName("run gen-foo 1 silent"),
|
||||||
WithNodeDir(dir),
|
|
||||||
WithExecutorOptions(
|
WithExecutorOptions(
|
||||||
|
task.WithDir(dir),
|
||||||
task.WithSilent(true),
|
task.WithSilent(true),
|
||||||
),
|
),
|
||||||
WithTask("gen-foo"),
|
WithTask("gen-foo"),
|
||||||
@@ -463,8 +442,8 @@ func TestStatus(t *testing.T) {
|
|||||||
// only exists after the first run.
|
// only exists after the first run.
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("run gen-bar 1 silent"),
|
WithName("run gen-bar 1 silent"),
|
||||||
WithNodeDir(dir),
|
|
||||||
WithExecutorOptions(
|
WithExecutorOptions(
|
||||||
|
task.WithDir(dir),
|
||||||
task.WithSilent(true),
|
task.WithSilent(true),
|
||||||
),
|
),
|
||||||
WithTask("gen-bar"),
|
WithTask("gen-bar"),
|
||||||
@@ -473,8 +452,8 @@ func TestStatus(t *testing.T) {
|
|||||||
// if e.Verbose is set to true.
|
// if e.Verbose is set to true.
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("run gen-baz silent"),
|
WithName("run gen-baz silent"),
|
||||||
WithNodeDir(dir),
|
|
||||||
WithExecutorOptions(
|
WithExecutorOptions(
|
||||||
|
task.WithDir(dir),
|
||||||
task.WithSilent(true),
|
task.WithSilent(true),
|
||||||
),
|
),
|
||||||
WithTask("gen-silent-baz"),
|
WithTask("gen-silent-baz"),
|
||||||
@@ -489,8 +468,8 @@ func TestStatus(t *testing.T) {
|
|||||||
// Run gen-bar a second time to produce a checksum file that matches bar.txt
|
// Run gen-bar a second time to produce a checksum file that matches bar.txt
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("run gen-bar 2 silent"),
|
WithName("run gen-bar 2 silent"),
|
||||||
WithNodeDir(dir),
|
|
||||||
WithExecutorOptions(
|
WithExecutorOptions(
|
||||||
|
task.WithDir(dir),
|
||||||
task.WithSilent(true),
|
task.WithSilent(true),
|
||||||
),
|
),
|
||||||
WithTask("gen-bar"),
|
WithTask("gen-bar"),
|
||||||
@@ -498,8 +477,8 @@ func TestStatus(t *testing.T) {
|
|||||||
// Run gen-bar a third time, to make sure we've triggered the status check.
|
// Run gen-bar a third time, to make sure we've triggered the status check.
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("run gen-bar 3 silent"),
|
WithName("run gen-bar 3 silent"),
|
||||||
WithNodeDir(dir),
|
|
||||||
WithExecutorOptions(
|
WithExecutorOptions(
|
||||||
|
task.WithDir(dir),
|
||||||
task.WithSilent(true),
|
task.WithSilent(true),
|
||||||
),
|
),
|
||||||
WithTask("gen-bar"),
|
WithTask("gen-bar"),
|
||||||
@@ -511,8 +490,8 @@ func TestStatus(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("run gen-bar 4 silent"),
|
WithName("run gen-bar 4 silent"),
|
||||||
WithNodeDir(dir),
|
|
||||||
WithExecutorOptions(
|
WithExecutorOptions(
|
||||||
|
task.WithDir(dir),
|
||||||
task.WithSilent(true),
|
task.WithSilent(true),
|
||||||
),
|
),
|
||||||
WithTask("gen-bar"),
|
WithTask("gen-bar"),
|
||||||
@@ -520,44 +499,56 @@ func TestStatus(t *testing.T) {
|
|||||||
// all: not up-to-date
|
// all: not up-to-date
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("run gen-foo 2"),
|
WithName("run gen-foo 2"),
|
||||||
WithNodeDir(dir),
|
WithExecutorOptions(
|
||||||
|
task.WithDir(dir),
|
||||||
|
),
|
||||||
WithTask("gen-foo"),
|
WithTask("gen-foo"),
|
||||||
)
|
)
|
||||||
// status: not up-to-date
|
// status: not up-to-date
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("run gen-foo 3"),
|
WithName("run gen-foo 3"),
|
||||||
WithNodeDir(dir),
|
WithExecutorOptions(
|
||||||
|
task.WithDir(dir),
|
||||||
|
),
|
||||||
WithTask("gen-foo"),
|
WithTask("gen-foo"),
|
||||||
)
|
)
|
||||||
// sources: not up-to-date
|
// sources: not up-to-date
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("run gen-bar 5"),
|
WithName("run gen-bar 5"),
|
||||||
WithNodeDir(dir),
|
WithExecutorOptions(
|
||||||
|
task.WithDir(dir),
|
||||||
|
),
|
||||||
WithTask("gen-bar"),
|
WithTask("gen-bar"),
|
||||||
)
|
)
|
||||||
// all: up-to-date
|
// all: up-to-date
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("run gen-bar 6"),
|
WithName("run gen-bar 6"),
|
||||||
WithNodeDir(dir),
|
WithExecutorOptions(
|
||||||
|
task.WithDir(dir),
|
||||||
|
),
|
||||||
WithTask("gen-bar"),
|
WithTask("gen-bar"),
|
||||||
)
|
)
|
||||||
// sources: not up-to-date, no output produced.
|
// sources: not up-to-date, no output produced.
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("run gen-baz 2"),
|
WithName("run gen-baz 2"),
|
||||||
WithNodeDir(dir),
|
WithExecutorOptions(
|
||||||
|
task.WithDir(dir),
|
||||||
|
),
|
||||||
WithTask("gen-silent-baz"),
|
WithTask("gen-silent-baz"),
|
||||||
)
|
)
|
||||||
// up-to-date, no output produced
|
// up-to-date, no output produced
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("run gen-baz 3"),
|
WithName("run gen-baz 3"),
|
||||||
WithNodeDir(dir),
|
WithExecutorOptions(
|
||||||
|
task.WithDir(dir),
|
||||||
|
),
|
||||||
WithTask("gen-silent-baz"),
|
WithTask("gen-silent-baz"),
|
||||||
)
|
)
|
||||||
// up-to-date, output produced due to Verbose mode.
|
// up-to-date, output produced due to Verbose mode.
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("run gen-baz 4 verbose"),
|
WithName("run gen-baz 4 verbose"),
|
||||||
WithNodeDir(dir),
|
|
||||||
WithExecutorOptions(
|
WithExecutorOptions(
|
||||||
|
task.WithDir(dir),
|
||||||
task.WithVerbose(true),
|
task.WithVerbose(true),
|
||||||
),
|
),
|
||||||
WithTask("gen-silent-baz"),
|
WithTask("gen-silent-baz"),
|
||||||
@@ -570,24 +561,32 @@ func TestPrecondition(t *testing.T) {
|
|||||||
const dir = "testdata/precondition"
|
const dir = "testdata/precondition"
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("a precondition has been met"),
|
WithName("a precondition has been met"),
|
||||||
WithNodeDir(dir),
|
WithExecutorOptions(
|
||||||
|
task.WithDir(dir),
|
||||||
|
),
|
||||||
WithTask("foo"),
|
WithTask("foo"),
|
||||||
)
|
)
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("a precondition was not met"),
|
WithName("a precondition was not met"),
|
||||||
WithNodeDir(dir),
|
WithExecutorOptions(
|
||||||
|
task.WithDir(dir),
|
||||||
|
),
|
||||||
WithTask("impossible"),
|
WithTask("impossible"),
|
||||||
WithRunError(),
|
WithRunError(),
|
||||||
)
|
)
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("precondition in dependency fails the task"),
|
WithName("precondition in dependency fails the task"),
|
||||||
WithNodeDir(dir),
|
WithExecutorOptions(
|
||||||
|
task.WithDir(dir),
|
||||||
|
),
|
||||||
WithTask("depends_on_impossible"),
|
WithTask("depends_on_impossible"),
|
||||||
WithRunError(),
|
WithRunError(),
|
||||||
)
|
)
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("precondition in cmd fails the task"),
|
WithName("precondition in cmd fails the task"),
|
||||||
WithNodeDir(dir),
|
WithExecutorOptions(
|
||||||
|
task.WithDir(dir),
|
||||||
|
),
|
||||||
WithTask("executes_failing_task_as_cmd"),
|
WithTask("executes_failing_task_as_cmd"),
|
||||||
WithRunError(),
|
WithRunError(),
|
||||||
)
|
)
|
||||||
@@ -598,21 +597,25 @@ func TestAlias(t *testing.T) {
|
|||||||
|
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("alias"),
|
WithName("alias"),
|
||||||
WithNodeDir("testdata/alias"),
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/alias"),
|
||||||
|
),
|
||||||
WithTask("f"),
|
WithTask("f"),
|
||||||
)
|
)
|
||||||
|
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("duplicate alias"),
|
WithName("duplicate alias"),
|
||||||
WithNodeDir("testdata/alias"),
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/alias"),
|
||||||
|
),
|
||||||
WithTask("x"),
|
WithTask("x"),
|
||||||
WithRunError(),
|
WithRunError(),
|
||||||
)
|
)
|
||||||
|
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("alias summary"),
|
WithName("alias summary"),
|
||||||
WithNodeDir("testdata/alias"),
|
|
||||||
WithExecutorOptions(
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/alias"),
|
||||||
task.WithSummary(true),
|
task.WithSummary(true),
|
||||||
),
|
),
|
||||||
WithTask("f"),
|
WithTask("f"),
|
||||||
@@ -624,14 +627,16 @@ func TestLabel(t *testing.T) {
|
|||||||
|
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("up to date"),
|
WithName("up to date"),
|
||||||
WithNodeDir("testdata/label_uptodate"),
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/label_uptodate"),
|
||||||
|
),
|
||||||
WithTask("foo"),
|
WithTask("foo"),
|
||||||
)
|
)
|
||||||
|
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("summary"),
|
WithName("summary"),
|
||||||
WithNodeDir("testdata/label_summary"),
|
|
||||||
WithExecutorOptions(
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/label_summary"),
|
||||||
task.WithSummary(true),
|
task.WithSummary(true),
|
||||||
),
|
),
|
||||||
WithTask("foo"),
|
WithTask("foo"),
|
||||||
@@ -639,20 +644,26 @@ func TestLabel(t *testing.T) {
|
|||||||
|
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("status"),
|
WithName("status"),
|
||||||
WithNodeDir("testdata/label_status"),
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/label_status"),
|
||||||
|
),
|
||||||
WithTask("foo"),
|
WithTask("foo"),
|
||||||
WithStatusError(),
|
WithStatusError(),
|
||||||
)
|
)
|
||||||
|
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("var"),
|
WithName("var"),
|
||||||
WithNodeDir("testdata/label_var"),
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/label_var"),
|
||||||
|
),
|
||||||
WithTask("foo"),
|
WithTask("foo"),
|
||||||
)
|
)
|
||||||
|
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("label in summary"),
|
WithName("label in summary"),
|
||||||
WithNodeDir("testdata/label_summary"),
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/label_summary"),
|
||||||
|
),
|
||||||
WithTask("foo"),
|
WithTask("foo"),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -679,8 +690,8 @@ func TestPromptInSummary(t *testing.T) {
|
|||||||
|
|
||||||
opts := []ExecutorTestOption{
|
opts := []ExecutorTestOption{
|
||||||
WithName(test.name),
|
WithName(test.name),
|
||||||
WithNodeDir("testdata/prompt"),
|
|
||||||
WithExecutorOptions(
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/prompt"),
|
||||||
task.WithAssumeTerm(true),
|
task.WithAssumeTerm(true),
|
||||||
),
|
),
|
||||||
WithTask("foo"),
|
WithTask("foo"),
|
||||||
@@ -698,8 +709,8 @@ func TestPromptWithIndirectTask(t *testing.T) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithNodeDir("testdata/prompt"),
|
|
||||||
WithExecutorOptions(
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/prompt"),
|
||||||
task.WithAssumeTerm(true),
|
task.WithAssumeTerm(true),
|
||||||
),
|
),
|
||||||
WithTask("bar"),
|
WithTask("bar"),
|
||||||
@@ -712,8 +723,8 @@ func TestPromptAssumeYes(t *testing.T) {
|
|||||||
|
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("--yes flag should skip prompt"),
|
WithName("--yes flag should skip prompt"),
|
||||||
WithNodeDir("testdata/prompt"),
|
|
||||||
WithExecutorOptions(
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/prompt"),
|
||||||
task.WithAssumeTerm(true),
|
task.WithAssumeTerm(true),
|
||||||
task.WithAssumeYes(true),
|
task.WithAssumeYes(true),
|
||||||
),
|
),
|
||||||
@@ -723,8 +734,8 @@ func TestPromptAssumeYes(t *testing.T) {
|
|||||||
|
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("task should raise errors.TaskCancelledError"),
|
WithName("task should raise errors.TaskCancelledError"),
|
||||||
WithNodeDir("testdata/prompt"),
|
|
||||||
WithExecutorOptions(
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/prompt"),
|
||||||
task.WithAssumeTerm(true),
|
task.WithAssumeTerm(true),
|
||||||
),
|
),
|
||||||
WithTask("foo"),
|
WithTask("foo"),
|
||||||
@@ -761,8 +772,8 @@ func TestForCmds(t *testing.T) {
|
|||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
opts := []ExecutorTestOption{
|
opts := []ExecutorTestOption{
|
||||||
WithName(test.name),
|
WithName(test.name),
|
||||||
WithNodeDir("testdata/for/cmds"),
|
|
||||||
WithExecutorOptions(
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/for/cmds"),
|
||||||
task.WithSilent(true),
|
task.WithSilent(true),
|
||||||
task.WithForce(true),
|
task.WithForce(true),
|
||||||
),
|
),
|
||||||
@@ -804,8 +815,8 @@ func TestForDeps(t *testing.T) {
|
|||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
opts := []ExecutorTestOption{
|
opts := []ExecutorTestOption{
|
||||||
WithName(test.name),
|
WithName(test.name),
|
||||||
WithNodeDir("testdata/for/deps"),
|
|
||||||
WithExecutorOptions(
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/for/deps"),
|
||||||
task.WithSilent(true),
|
task.WithSilent(true),
|
||||||
task.WithForce(true),
|
task.WithForce(true),
|
||||||
// Force output of each dep to be grouped together to prevent interleaving
|
// Force output of each dep to be grouped together to prevent interleaving
|
||||||
@@ -850,8 +861,8 @@ func TestReference(t *testing.T) {
|
|||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName(test.name),
|
WithName(test.name),
|
||||||
WithNodeDir("testdata/var_references"),
|
|
||||||
WithExecutorOptions(
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/var_references"),
|
||||||
task.WithSilent(true),
|
task.WithSilent(true),
|
||||||
task.WithForce(true),
|
task.WithForce(true),
|
||||||
),
|
),
|
||||||
@@ -918,8 +929,8 @@ func TestVarInheritance(t *testing.T) {
|
|||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName(test.name),
|
WithName(test.name),
|
||||||
WithNodeDir(fmt.Sprintf("testdata/var_inheritance/v3/%s", test.name)),
|
|
||||||
WithExecutorOptions(
|
WithExecutorOptions(
|
||||||
|
task.WithDir(fmt.Sprintf("testdata/var_inheritance/v3/%s", test.name)),
|
||||||
task.WithSilent(true),
|
task.WithSilent(true),
|
||||||
task.WithForce(true),
|
task.WithForce(true),
|
||||||
),
|
),
|
||||||
@@ -933,20 +944,26 @@ func TestFuzzyModel(t *testing.T) {
|
|||||||
|
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("fuzzy"),
|
WithName("fuzzy"),
|
||||||
WithNodeDir("testdata/fuzzy"),
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/fuzzy"),
|
||||||
|
),
|
||||||
WithTask("instal"),
|
WithTask("instal"),
|
||||||
WithRunError(),
|
WithRunError(),
|
||||||
)
|
)
|
||||||
|
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("not-fuzzy"),
|
WithName("not-fuzzy"),
|
||||||
WithNodeDir("testdata/fuzzy"),
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/fuzzy"),
|
||||||
|
),
|
||||||
WithTask("install"),
|
WithTask("install"),
|
||||||
)
|
)
|
||||||
|
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("intern"),
|
WithName("intern"),
|
||||||
WithNodeDir("testdata/fuzzy"),
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/fuzzy"),
|
||||||
|
),
|
||||||
WithTask("intern"),
|
WithTask("intern"),
|
||||||
WithRunError(),
|
WithRunError(),
|
||||||
)
|
)
|
||||||
@@ -957,65 +974,17 @@ func TestIncludeChecksum(t *testing.T) {
|
|||||||
|
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("correct"),
|
WithName("correct"),
|
||||||
WithNodeDir("testdata/includes_checksum/correct"),
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/includes_checksum/correct"),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("incorrect"),
|
WithName("incorrect"),
|
||||||
WithNodeDir("testdata/includes_checksum/incorrect"),
|
WithExecutorOptions(
|
||||||
WithReaderError(),
|
task.WithDir("testdata/includes_checksum/incorrect"),
|
||||||
|
),
|
||||||
|
WithSetupError(),
|
||||||
WithFixtureTemplating(),
|
WithFixtureTemplating(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWildcard(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
call string
|
|
||||||
wantErr bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "basic wildcard",
|
|
||||||
call: "wildcard-foo",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "double wildcard",
|
|
||||||
call: "foo-wildcard-bar",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "store wildcard",
|
|
||||||
call: "start-foo",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "matches exactly",
|
|
||||||
call: "matches-exactly-*",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "no matches",
|
|
||||||
call: "no-match",
|
|
||||||
wantErr: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "multiple matches",
|
|
||||||
call: "wildcard-foo-bar",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range tests {
|
|
||||||
opts := []ExecutorTestOption{
|
|
||||||
WithName(test.name),
|
|
||||||
WithNodeDir("testdata/wildcards"),
|
|
||||||
WithExecutorOptions(
|
|
||||||
task.WithSilent(true),
|
|
||||||
task.WithForce(true),
|
|
||||||
),
|
|
||||||
WithTask(test.call),
|
|
||||||
}
|
|
||||||
if test.wantErr {
|
|
||||||
opts = append(opts, WithRunError())
|
|
||||||
}
|
|
||||||
NewExecutorTest(t, opts...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -2,9 +2,7 @@ package task_test
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"slices"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/sebdah/goldie/v2"
|
"github.com/sebdah/goldie/v2"
|
||||||
@@ -12,7 +10,6 @@ import (
|
|||||||
|
|
||||||
"github.com/go-task/task/v3"
|
"github.com/go-task/task/v3"
|
||||||
"github.com/go-task/task/v3/experiments"
|
"github.com/go-task/task/v3/experiments"
|
||||||
"github.com/go-task/task/v3/taskfile"
|
|
||||||
"github.com/go-task/task/v3/taskfile/ast"
|
"github.com/go-task/task/v3/taskfile/ast"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -29,17 +26,12 @@ type (
|
|||||||
// running `task gen:fixtures`.
|
// running `task gen:fixtures`.
|
||||||
FormatterTest struct {
|
FormatterTest struct {
|
||||||
TaskTest
|
TaskTest
|
||||||
task string
|
task string
|
||||||
vars map[string]any
|
vars map[string]any
|
||||||
nodeDir string
|
executorOpts []task.ExecutorOption
|
||||||
nodeEntrypoint string
|
listOptions task.ListOptions
|
||||||
nodeInsecure bool
|
wantSetupError bool
|
||||||
readerOpts []taskfile.ReaderOption
|
wantListError bool
|
||||||
executorOpts []task.ExecutorOption
|
|
||||||
listOptions task.ListOptions
|
|
||||||
wantReaderError bool
|
|
||||||
wantSetupError bool
|
|
||||||
wantListError bool
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -49,9 +41,8 @@ type (
|
|||||||
func NewFormatterTest(t *testing.T, opts ...FormatterTestOption) {
|
func NewFormatterTest(t *testing.T, opts ...FormatterTestOption) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
tt := &FormatterTest{
|
tt := &FormatterTest{
|
||||||
task: "default",
|
task: "default",
|
||||||
vars: map[string]any{},
|
vars: map[string]any{},
|
||||||
nodeDir: ".",
|
|
||||||
TaskTest: TaskTest{
|
TaskTest: TaskTest{
|
||||||
experiments: map[*experiments.Experiment]int{},
|
experiments: map[*experiments.Experiment]int{},
|
||||||
fixtureTemplateData: map[string]any{},
|
fixtureTemplateData: map[string]any{},
|
||||||
@@ -123,57 +114,23 @@ func (tt *FormatterTest) run(t *testing.T) {
|
|||||||
f := func(t *testing.T) {
|
f := func(t *testing.T) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
ctx := context.Background()
|
|
||||||
|
|
||||||
// Create a new root node for the given entrypoint
|
opts := append(
|
||||||
node, err := taskfile.NewRootNode(
|
|
||||||
tt.nodeEntrypoint,
|
|
||||||
tt.nodeDir,
|
|
||||||
tt.nodeInsecure,
|
|
||||||
)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
// Create a golden fixture file for the output
|
|
||||||
g := goldie.New(t,
|
|
||||||
goldie.WithFixtureDir(filepath.Join(node.Dir(), "testdata")),
|
|
||||||
)
|
|
||||||
|
|
||||||
// Set up a temporary directory for the taskfile reader and task executor
|
|
||||||
tempDir, err := task.NewTempDir(node.Dir())
|
|
||||||
require.NoError(t, err)
|
|
||||||
tt.readerOpts = append(tt.readerOpts, taskfile.WithTempDir(tempDir.Remote))
|
|
||||||
|
|
||||||
// Set up the taskfile reader
|
|
||||||
reader := taskfile.NewReader(tt.readerOpts...)
|
|
||||||
graph, err := reader.Read(ctx, node)
|
|
||||||
if tt.wantReaderError {
|
|
||||||
require.Error(t, err)
|
|
||||||
tt.writeFixtureErrReader(t, g, err)
|
|
||||||
tt.writeFixtureBuffer(t, g, buf)
|
|
||||||
return
|
|
||||||
} else {
|
|
||||||
require.NoError(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
executorOpts := slices.Concat(
|
|
||||||
// Apply the node directory and temp directory to the executor options
|
|
||||||
// by default, but allow them to by overridden by the test options
|
|
||||||
[]task.ExecutorOption{
|
|
||||||
task.WithDir(node.Dir()),
|
|
||||||
task.WithTempDir(tempDir),
|
|
||||||
},
|
|
||||||
// Apply the executor options from the test
|
|
||||||
tt.executorOpts,
|
tt.executorOpts,
|
||||||
// Force the input/output streams to be set to the test buffer
|
task.WithStdout(&buf),
|
||||||
[]task.ExecutorOption{
|
task.WithStderr(&buf),
|
||||||
task.WithStdout(&buf),
|
|
||||||
task.WithStderr(&buf),
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Set up the task executor
|
// Set up the task executor
|
||||||
executor, err := task.NewExecutor(graph, executorOpts...)
|
e := task.NewExecutor(opts...)
|
||||||
if tt.wantSetupError {
|
|
||||||
|
// Create a golden fixture file for the output
|
||||||
|
g := goldie.New(t,
|
||||||
|
goldie.WithFixtureDir(filepath.Join(e.Dir, "testdata")),
|
||||||
|
)
|
||||||
|
|
||||||
|
// Call setup and check for errors
|
||||||
|
if err := e.Setup(); tt.wantSetupError {
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
tt.writeFixtureErrSetup(t, g, err)
|
tt.writeFixtureErrSetup(t, g, err)
|
||||||
tt.writeFixtureBuffer(t, g, buf)
|
tt.writeFixtureBuffer(t, g, buf)
|
||||||
@@ -189,7 +146,7 @@ func (tt *FormatterTest) run(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Run the formatter and check for errors
|
// Run the formatter and check for errors
|
||||||
if _, err := executor.ListTasks(tt.listOptions); tt.wantListError {
|
if _, err := e.ListTasks(tt.listOptions); tt.wantListError {
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
tt.writeFixtureErrList(t, g, err)
|
tt.writeFixtureErrList(t, g, err)
|
||||||
tt.writeFixtureBuffer(t, g, buf)
|
tt.writeFixtureBuffer(t, g, buf)
|
||||||
@@ -213,7 +170,9 @@ func TestNoLabelInList(t *testing.T) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
NewFormatterTest(t,
|
NewFormatterTest(t,
|
||||||
WithNodeDir("testdata/label_list"),
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/label_list"),
|
||||||
|
),
|
||||||
WithListOptions(task.ListOptions{
|
WithListOptions(task.ListOptions{
|
||||||
ListOnlyTasksWithDescriptions: true,
|
ListOnlyTasksWithDescriptions: true,
|
||||||
}),
|
}),
|
||||||
@@ -225,7 +184,9 @@ func TestListAllShowsNoDesc(t *testing.T) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
NewFormatterTest(t,
|
NewFormatterTest(t,
|
||||||
WithNodeDir("testdata/list_mixed_desc"),
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/list_mixed_desc"),
|
||||||
|
),
|
||||||
WithListOptions(task.ListOptions{
|
WithListOptions(task.ListOptions{
|
||||||
ListAllTasks: true,
|
ListAllTasks: true,
|
||||||
}),
|
}),
|
||||||
@@ -237,7 +198,9 @@ func TestListCanListDescOnly(t *testing.T) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
NewFormatterTest(t,
|
NewFormatterTest(t,
|
||||||
WithNodeDir("testdata/list_mixed_desc"),
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/list_mixed_desc"),
|
||||||
|
),
|
||||||
WithListOptions(task.ListOptions{
|
WithListOptions(task.ListOptions{
|
||||||
ListOnlyTasksWithDescriptions: true,
|
ListOnlyTasksWithDescriptions: true,
|
||||||
}),
|
}),
|
||||||
@@ -248,7 +211,9 @@ func TestListDescInterpolation(t *testing.T) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
NewFormatterTest(t,
|
NewFormatterTest(t,
|
||||||
WithNodeDir("testdata/list_desc_interpolation"),
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/list_desc_interpolation"),
|
||||||
|
),
|
||||||
WithListOptions(task.ListOptions{
|
WithListOptions(task.ListOptions{
|
||||||
ListOnlyTasksWithDescriptions: true,
|
ListOnlyTasksWithDescriptions: true,
|
||||||
}),
|
}),
|
||||||
@@ -259,7 +224,9 @@ func TestJsonListFormat(t *testing.T) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
NewFormatterTest(t,
|
NewFormatterTest(t,
|
||||||
WithNodeDir("testdata/json_list_format"),
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/json_list_format"),
|
||||||
|
),
|
||||||
WithListOptions(task.ListOptions{
|
WithListOptions(task.ListOptions{
|
||||||
FormatTaskListAsJSON: true,
|
FormatTaskListAsJSON: true,
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ import (
|
|||||||
"github.com/go-task/task/v3/experiments"
|
"github.com/go-task/task/v3/experiments"
|
||||||
"github.com/go-task/task/v3/internal/env"
|
"github.com/go-task/task/v3/internal/env"
|
||||||
"github.com/go-task/task/v3/internal/sort"
|
"github.com/go-task/task/v3/internal/sort"
|
||||||
"github.com/go-task/task/v3/taskfile"
|
|
||||||
"github.com/go-task/task/v3/taskfile/ast"
|
"github.com/go-task/task/v3/taskfile/ast"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -202,7 +201,7 @@ func Validate() error {
|
|||||||
|
|
||||||
// WithFlags is a special internal functional option that is used to pass flags
|
// WithFlags is a special internal functional option that is used to pass flags
|
||||||
// from the CLI into any constructor that accepts functional options.
|
// from the CLI into any constructor that accepts functional options.
|
||||||
func WithFlags() *flagsOption {
|
func WithFlags() task.ExecutorOption {
|
||||||
return &flagsOption{}
|
return &flagsOption{}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -229,8 +228,14 @@ func (o *flagsOption) ApplyToExecutor(e *task.Executor) {
|
|||||||
|
|
||||||
e.Options(
|
e.Options(
|
||||||
task.WithDir(dir),
|
task.WithDir(dir),
|
||||||
|
task.WithEntrypoint(Entrypoint),
|
||||||
task.WithForce(Force),
|
task.WithForce(Force),
|
||||||
task.WithForceAll(ForceAll),
|
task.WithForceAll(ForceAll),
|
||||||
|
task.WithInsecure(Insecure),
|
||||||
|
task.WithDownload(Download),
|
||||||
|
task.WithOffline(Offline),
|
||||||
|
task.WithTimeout(Timeout),
|
||||||
|
task.WithCacheExpiryDuration(CacheExpiryDuration),
|
||||||
task.WithWatch(Watch),
|
task.WithWatch(Watch),
|
||||||
task.WithVerbose(Verbose),
|
task.WithVerbose(Verbose),
|
||||||
task.WithSilent(Silent),
|
task.WithSilent(Silent),
|
||||||
@@ -246,12 +251,3 @@ func (o *flagsOption) ApplyToExecutor(e *task.Executor) {
|
|||||||
task.WithVersionCheck(true),
|
task.WithVersionCheck(true),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *flagsOption) ApplyToReader(r *taskfile.Reader) {
|
|
||||||
r.Options(
|
|
||||||
taskfile.WithInsecure(Insecure),
|
|
||||||
taskfile.WithDownload(Download),
|
|
||||||
taskfile.WithOffline(Offline),
|
|
||||||
taskfile.WithCacheExpiryDuration(CacheExpiryDuration),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import (
|
|||||||
_ "embed"
|
_ "embed"
|
||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/Masterminds/semver/v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -46,6 +48,10 @@ func getCommit(info *debug.BuildInfo) string {
|
|||||||
// However, it can also be overridden at build time using:
|
// However, it can also be overridden at build time using:
|
||||||
// -ldflags="-X 'github.com/go-task/task/v3/internal/version.version=vX.X.X'".
|
// -ldflags="-X 'github.com/go-task/task/v3/internal/version.version=vX.X.X'".
|
||||||
func GetVersion() string {
|
func GetVersion() string {
|
||||||
|
// If its a development version, we bump the minor version.
|
||||||
|
if commit != "" || dirty {
|
||||||
|
return semver.MustParse(version).IncMinor().String()
|
||||||
|
}
|
||||||
return version
|
return version
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,7 +67,7 @@ func GetVersionWithBuildInfo() string {
|
|||||||
buildMetadata = append(buildMetadata, "dirty")
|
buildMetadata = append(buildMetadata, "dirty")
|
||||||
}
|
}
|
||||||
if len(buildMetadata) > 0 {
|
if len(buildMetadata) > 0 {
|
||||||
return version + "+" + strings.Join(buildMetadata, ".")
|
return GetVersion() + "+" + strings.Join(buildMetadata, ".")
|
||||||
}
|
}
|
||||||
return version
|
return GetVersion()
|
||||||
}
|
}
|
||||||
|
|||||||
58
internal/version/version_test.go
Normal file
58
internal/version/version_test.go
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
package version
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestVersionTxt(t *testing.T) {
|
||||||
|
// Check that the version.txt is a valid semver version.
|
||||||
|
require.NotEmpty(t, GetVersion(), "version.txt is not semver compliant")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetVersion(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
version string
|
||||||
|
commit string
|
||||||
|
dirty bool
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{"1.2.3", "", false, "1.2.3"},
|
||||||
|
{"1.2.3", "", true, "1.3.0"},
|
||||||
|
{"1.2.3", "abcdefg", false, "1.3.0"},
|
||||||
|
{"1.2.3", "abcdefg", true, "1.3.0"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
version = tt.version
|
||||||
|
commit = tt.commit
|
||||||
|
dirty = tt.dirty
|
||||||
|
t.Run(tt.want, func(t *testing.T) {
|
||||||
|
require.Equal(t, tt.want, GetVersion())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetVersionWithBuildInfo(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
version string
|
||||||
|
commit string
|
||||||
|
dirty bool
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{"1.2.3", "", false, "1.2.3"},
|
||||||
|
{"1.2.3", "", true, "1.3.0+dirty"},
|
||||||
|
{"1.2.3", "abcdefg", false, "1.3.0+abcdefg"},
|
||||||
|
{"1.2.3", "abcdefg", true, "1.3.0+abcdefg.dirty"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
version = tt.version
|
||||||
|
commit = tt.commit
|
||||||
|
dirty = tt.dirty
|
||||||
|
t.Run(tt.want, func(t *testing.T) {
|
||||||
|
require.Equal(t, tt.want, GetVersionWithBuildInfo())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,13 +4,18 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"slices"
|
"slices"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/Masterminds/semver/v3"
|
"github.com/Masterminds/semver/v3"
|
||||||
"github.com/sajari/fuzzy"
|
"github.com/sajari/fuzzy"
|
||||||
|
|
||||||
"github.com/go-task/task/v3/errors"
|
"github.com/go-task/task/v3/errors"
|
||||||
|
"github.com/go-task/task/v3/internal/env"
|
||||||
|
"github.com/go-task/task/v3/internal/execext"
|
||||||
|
"github.com/go-task/task/v3/internal/filepathext"
|
||||||
"github.com/go-task/task/v3/internal/logger"
|
"github.com/go-task/task/v3/internal/logger"
|
||||||
"github.com/go-task/task/v3/internal/output"
|
"github.com/go-task/task/v3/internal/output"
|
||||||
"github.com/go-task/task/v3/internal/version"
|
"github.com/go-task/task/v3/internal/version"
|
||||||
@@ -18,8 +23,18 @@ import (
|
|||||||
"github.com/go-task/task/v3/taskfile/ast"
|
"github.com/go-task/task/v3/taskfile/ast"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (e *Executor) setup() error {
|
func (e *Executor) Setup() error {
|
||||||
e.setupLogger()
|
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
|
||||||
|
}
|
||||||
e.setupFuzzyModel()
|
e.setupFuzzyModel()
|
||||||
e.setupStdFiles()
|
e.setupStdFiles()
|
||||||
if err := e.setupOutput(); err != nil {
|
if err := e.setupOutput(); err != nil {
|
||||||
@@ -39,6 +54,46 @@ func (e *Executor) setup() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *Executor) getRootNode() (taskfile.Node, error) {
|
||||||
|
node, err := taskfile.NewRootNode(e.Entrypoint, e.Dir, e.Insecure, e.Timeout)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
e.Dir = node.Dir()
|
||||||
|
return node, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Executor) readTaskfile(node taskfile.Node) error {
|
||||||
|
ctx, cf := context.WithTimeout(context.Background(), e.Timeout)
|
||||||
|
defer cf()
|
||||||
|
debugFunc := func(s string) {
|
||||||
|
e.Logger.VerboseOutf(logger.Magenta, s)
|
||||||
|
}
|
||||||
|
promptFunc := func(s string) error {
|
||||||
|
return e.Logger.Prompt(logger.Yellow, s, "n", "y", "yes")
|
||||||
|
}
|
||||||
|
reader := taskfile.NewReader(
|
||||||
|
taskfile.WithInsecure(e.Insecure),
|
||||||
|
taskfile.WithDownload(e.Download),
|
||||||
|
taskfile.WithOffline(e.Offline),
|
||||||
|
taskfile.WithTempDir(e.TempDir.Remote),
|
||||||
|
taskfile.WithCacheExpiryDuration(e.CacheExpiryDuration),
|
||||||
|
taskfile.WithDebugFunc(debugFunc),
|
||||||
|
taskfile.WithPromptFunc(promptFunc),
|
||||||
|
)
|
||||||
|
graph, err := reader.Read(ctx, node)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, context.DeadlineExceeded) {
|
||||||
|
return &errors.TaskfileNetworkTimeoutError{URI: node.Location(), Timeout: e.Timeout}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if e.Taskfile, err = graph.Merge(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (e *Executor) setupFuzzyModel() {
|
func (e *Executor) setupFuzzyModel() {
|
||||||
if e.Taskfile == nil {
|
if e.Taskfile == nil {
|
||||||
return
|
return
|
||||||
@@ -60,6 +115,52 @@ func (e *Executor) setupFuzzyModel() {
|
|||||||
e.fuzzyModel = model
|
e.fuzzyModel = model
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *Executor) setupTempDir() error {
|
||||||
|
if e.TempDir != (TempDir{}) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
tempDir := env.GetTaskEnv("TEMP_DIR")
|
||||||
|
if tempDir == "" {
|
||||||
|
e.TempDir = TempDir{
|
||||||
|
Remote: filepathext.SmartJoin(e.Dir, ".task"),
|
||||||
|
Fingerprint: filepathext.SmartJoin(e.Dir, ".task"),
|
||||||
|
}
|
||||||
|
} else if filepath.IsAbs(tempDir) || strings.HasPrefix(tempDir, "~") {
|
||||||
|
tempDir, err := execext.ExpandLiteral(tempDir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
projectDir, _ := filepath.Abs(e.Dir)
|
||||||
|
projectName := filepath.Base(projectDir)
|
||||||
|
e.TempDir = TempDir{
|
||||||
|
Remote: tempDir,
|
||||||
|
Fingerprint: filepathext.SmartJoin(tempDir, projectName),
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
e.TempDir = TempDir{
|
||||||
|
Remote: filepathext.SmartJoin(e.Dir, tempDir),
|
||||||
|
Fingerprint: filepathext.SmartJoin(e.Dir, tempDir),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
remoteDir := env.GetTaskEnv("REMOTE_DIR")
|
||||||
|
if remoteDir != "" {
|
||||||
|
if filepath.IsAbs(remoteDir) || strings.HasPrefix(remoteDir, "~") {
|
||||||
|
remoteTempDir, err := execext.ExpandLiteral(remoteDir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
e.TempDir.Remote = remoteTempDir
|
||||||
|
} else {
|
||||||
|
e.TempDir.Remote = filepathext.SmartJoin(e.Dir, ".task")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (e *Executor) setupStdFiles() {
|
func (e *Executor) setupStdFiles() {
|
||||||
if e.Stdin == nil {
|
if e.Stdin == nil {
|
||||||
e.Stdin = os.Stdin
|
e.Stdin = os.Stdin
|
||||||
@@ -105,7 +206,7 @@ func (e *Executor) setupCompiler() error {
|
|||||||
|
|
||||||
e.Compiler = &Compiler{
|
e.Compiler = &Compiler{
|
||||||
Dir: e.Dir,
|
Dir: e.Dir,
|
||||||
Entrypoint: e.Taskfile.Location,
|
Entrypoint: e.Entrypoint,
|
||||||
UserWorkingDir: e.UserWorkingDir,
|
UserWorkingDir: e.UserWorkingDir,
|
||||||
TaskfileEnv: e.Taskfile.Env,
|
TaskfileEnv: e.Taskfile.Env,
|
||||||
TaskfileVars: e.Taskfile.Vars,
|
TaskfileVars: e.Taskfile.Vars,
|
||||||
4501
task_test.go
4501
task_test.go
File diff suppressed because it is too large
Load Diff
@@ -3,6 +3,7 @@ package taskfile
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
giturls "github.com/chainguard-dev/git-urls"
|
giturls "github.com/chainguard-dev/git-urls"
|
||||||
|
|
||||||
@@ -32,6 +33,7 @@ func NewRootNode(
|
|||||||
entrypoint string,
|
entrypoint string,
|
||||||
dir string,
|
dir string,
|
||||||
insecure bool,
|
insecure bool,
|
||||||
|
timeout time.Duration,
|
||||||
) (Node, error) {
|
) (Node, error) {
|
||||||
dir = fsext.DefaultDir(entrypoint, dir)
|
dir = fsext.DefaultDir(entrypoint, dir)
|
||||||
// If the entrypoint is "-", we read from stdin
|
// If the entrypoint is "-", we read from stdin
|
||||||
|
|||||||
78
temp_dir.go
78
temp_dir.go
@@ -1,78 +0,0 @@
|
|||||||
package task
|
|
||||||
|
|
||||||
import (
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/go-task/task/v3/internal/env"
|
|
||||||
"github.com/go-task/task/v3/internal/execext"
|
|
||||||
"github.com/go-task/task/v3/internal/filepathext"
|
|
||||||
)
|
|
||||||
|
|
||||||
type TempDir struct {
|
|
||||||
Remote string
|
|
||||||
Fingerprint string
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewTempDir(dir string) (*TempDir, error) {
|
|
||||||
tempDir, err := setupTempDirFingerprint(dir)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = setupTempDirRemote(dir, tempDir)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return tempDir, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func setupTempDirFingerprint(dir string) (*TempDir, error) {
|
|
||||||
tempDir := env.GetTaskEnv("TEMP_DIR")
|
|
||||||
|
|
||||||
if tempDir == "" {
|
|
||||||
return &TempDir{
|
|
||||||
Remote: filepathext.SmartJoin(dir, ".task"),
|
|
||||||
Fingerprint: filepathext.SmartJoin(dir, ".task"),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if filepath.IsAbs(tempDir) || strings.HasPrefix(tempDir, "~") {
|
|
||||||
tempDir, err := execext.ExpandLiteral(tempDir)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
projectDir, _ := filepath.Abs(dir)
|
|
||||||
projectName := filepath.Base(projectDir)
|
|
||||||
return &TempDir{
|
|
||||||
Remote: tempDir,
|
|
||||||
Fingerprint: filepathext.SmartJoin(tempDir, projectName),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return &TempDir{
|
|
||||||
Remote: filepathext.SmartJoin(dir, tempDir),
|
|
||||||
Fingerprint: filepathext.SmartJoin(dir, tempDir),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func setupTempDirRemote(dir string, tempDir *TempDir) error {
|
|
||||||
remoteDir := env.GetTaskEnv("REMOTE_DIR")
|
|
||||||
|
|
||||||
if remoteDir == "" {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if filepath.IsAbs(remoteDir) || strings.HasPrefix(remoteDir, "~") {
|
|
||||||
remoteTempDir, err := execext.ExpandLiteral(remoteDir)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
tempDir.Remote = remoteTempDir
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
tempDir.Remote = filepathext.SmartJoin(dir, ".task")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
Hello foo
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
Hello foo bar
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
I don't consume matches: []
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
Hello foo-bar
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
task: Task "no-match" does not exist
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
task: No tasks with description available. Try --list-all to list all tasks
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
Starting foo
|
|
||||||
Reference in New Issue
Block a user