1
0
mirror of https://github.com/go-task/task.git synced 2025-04-21 12:17:07 +02:00
task/executor.go

321 lines
8.8 KiB
Go
Raw Normal View History

package task
import (
"context"
"io"
"os"
"sync"
"time"
"github.com/puzpuzpuz/xsync/v3"
"github.com/sajari/fuzzy"
"github.com/go-task/task/v3/internal/logger"
"github.com/go-task/task/v3/internal/output"
"github.com/go-task/task/v3/internal/sort"
"github.com/go-task/task/v3/taskfile/ast"
)
type (
// An ExecutorOption is a functional option for an [Executor].
ExecutorOption func(*Executor)
// An Executor is used for processing Taskfile(s) and executing the task(s)
// within them.
Executor struct {
// Flags
Dir string
Entrypoint string
TempDir TempDir
Force bool
ForceAll bool
Insecure bool
Download bool
Offline bool
Timeout time.Duration
Watch bool
Verbose bool
Silent bool
AssumeYes bool
AssumeTerm bool // Used for testing
Dry bool
Summary bool
Parallel bool
Color bool
Concurrency int
Interval time.Duration
// I/O
Stdin io.Reader
Stdout io.Writer
Stderr io.Writer
// Internal
Taskfile *ast.Taskfile
Logger *logger.Logger
Compiler *Compiler
Output output.Output
OutputStyle ast.Output
TaskSorter sort.Sorter
UserWorkingDir string
EnableVersionCheck bool
fuzzyModel *fuzzy.Model
concurrencySemaphore chan struct{}
taskCallCount map[string]*int32
mkdirMutexMap map[string]*sync.Mutex
executionHashes map[string]context.Context
executionHashesMutex sync.Mutex
watchedDirs *xsync.MapOf[string, bool]
}
TempDir struct {
Remote string
Fingerprint string
}
)
// NewExecutor creates a new [Executor] and applies the given functional options
// to it.
func NewExecutor(opts ...ExecutorOption) *Executor {
e := &Executor{
Timeout: time.Second * 10,
Stdin: os.Stdin,
Stdout: os.Stdout,
Stderr: os.Stderr,
Logger: nil,
Compiler: nil,
Output: nil,
OutputStyle: ast.Output{},
TaskSorter: sort.AlphaNumericWithRootTasksFirst,
UserWorkingDir: "",
fuzzyModel: nil,
concurrencySemaphore: nil,
taskCallCount: map[string]*int32{},
mkdirMutexMap: map[string]*sync.Mutex{},
executionHashes: map[string]context.Context{},
executionHashesMutex: sync.Mutex{},
}
e.Options(opts...)
return e
}
// Options loops through the given [ExecutorOption] functions and applies them
// to the [Executor].
func (e *Executor) Options(opts ...ExecutorOption) {
for _, opt := range opts {
opt(e)
}
}
// ExecutorWithDir sets the working directory of the [Executor]. By default, the
// directory is set to the user's current working directory.
func ExecutorWithDir(dir string) ExecutorOption {
return func(e *Executor) {
e.Dir = dir
}
}
// ExecutorWithEntrypoint sets the entrypoint (main Taskfile) of the [Executor].
// By default, Task will search for one of the default Taskfiles in the given
// directory.
func ExecutorWithEntrypoint(entrypoint string) ExecutorOption {
return func(e *Executor) {
e.Entrypoint = entrypoint
}
}
// ExecutorWithTempDir sets the temporary directory that will be used by
// [Executor] for storing temporary files like checksums and cached remote
// files. By default, the temporary directory is set to the user's temporary
// directory.
func ExecutorWithTempDir(tempDir TempDir) ExecutorOption {
return func(e *Executor) {
e.TempDir = tempDir
}
}
// ExecutorWithForce ensures that the [Executor] always runs a task, even when
// fingerprinting or prompts would normally stop it.
func ExecutorWithForce(force bool) ExecutorOption {
return func(e *Executor) {
e.Force = force
}
}
// ExecutorWithForceAll ensures that the [Executor] always runs all tasks
// (including subtasks), even when fingerprinting or prompts would normally stop
// them.
func ExecutorWithForceAll(forceAll bool) ExecutorOption {
return func(e *Executor) {
e.ForceAll = forceAll
}
}
// ExecutorWithInsecure allows the [Executor] to make insecure connections when
// reading remote taskfiles. By default, insecure connections are rejected.
func ExecutorWithInsecure(insecure bool) ExecutorOption {
return func(e *Executor) {
e.Insecure = insecure
}
}
// ExecutorWithDownload forces the [Executor] to download a fresh copy of the
// taskfile from the remote source.
func ExecutorWithDownload(download bool) ExecutorOption {
return func(e *Executor) {
e.Download = download
}
}
// ExecutorWithOffline 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 ExecutorWithOffline(offline bool) ExecutorOption {
return func(e *Executor) {
e.Offline = offline
}
}
// ExecutorWithTimeout sets the [Executor]'s timeout for fetching remote
// taskfiles. By default, the timeout is set to 10 seconds.
func ExecutorWithTimeout(timeout time.Duration) ExecutorOption {
return func(e *Executor) {
e.Timeout = timeout
}
}
// ExecutorWithWatch 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 detected, a new task run is triggered.
func ExecutorWithWatch(watch bool) ExecutorOption {
return func(e *Executor) {
e.Watch = watch
}
}
// ExecutorWithVerbose tells the [Executor] to output more information about the
// tasks that are run.
func ExecutorWithVerbose(verbose bool) ExecutorOption {
return func(e *Executor) {
e.Verbose = verbose
}
}
// ExecutorWithSilent tells the [Executor] to suppress all output except for the
// output of the tasks that are run.
func ExecutorWithSilent(silent bool) ExecutorOption {
return func(e *Executor) {
e.Silent = silent
}
}
// ExecutorWithAssumeYes tells the [Executor] to assume "yes" for all prompts.
func ExecutorWithAssumeYes(assumeYes bool) ExecutorOption {
return func(e *Executor) {
e.AssumeYes = assumeYes
}
}
// WithAssumeTerm is used for testing purposes to simulate a terminal.
func ExecutorWithDry(dry bool) ExecutorOption {
return func(e *Executor) {
e.Dry = dry
}
}
// ExecutorWithSummary tells the [Executor] to output a summary of the given
// tasks instead of running them.
func ExecutorWithSummary(summary bool) ExecutorOption {
return func(e *Executor) {
e.Summary = summary
}
}
// ExecutorWithParallel tells the [Executor] to run tasks given in the same call
// in parallel.
func ExecutorWithParallel(parallel bool) ExecutorOption {
return func(e *Executor) {
e.Parallel = parallel
}
}
// ExecutorWithColor tells the [Executor] whether or not to output using
// colorized strings.
func ExecutorWithColor(color bool) ExecutorOption {
return func(e *Executor) {
e.Color = color
}
}
// ExecutorWithConcurrency sets the maximum number of tasks that the [Executor]
// can run in parallel.
func ExecutorWithConcurrency(concurrency int) ExecutorOption {
return func(e *Executor) {
e.Concurrency = concurrency
}
}
// ExecutorWithInterval sets the interval at which the [Executor] will wait for
// duplicated events before running a task.
func ExecutorWithInterval(interval time.Duration) ExecutorOption {
return func(e *Executor) {
e.Interval = interval
}
}
// ExecutorWithOutputStyle sets the output style of the [Executor]. By default,
// the output style is set to the style defined in the Taskfile.
func ExecutorWithOutputStyle(outputStyle ast.Output) ExecutorOption {
return func(e *Executor) {
e.OutputStyle = outputStyle
}
}
// ExecutorWithTaskSorter sets the sorter that the [Executor] will use to sort
// tasks. By default, the sorter is set to sort tasks alphabetically, but with
// tasks with no namespace (in the root Taskfile) first.
func ExecutorWithTaskSorter(sorter sort.Sorter) ExecutorOption {
return func(e *Executor) {
e.TaskSorter = sorter
}
}
// ExecutorWithStdin sets the [Executor]'s standard input [io.Reader].
func ExecutorWithStdin(stdin io.Reader) ExecutorOption {
return func(e *Executor) {
e.Stdin = stdin
}
}
// ExecutorWithStdout sets the [Executor]'s standard output [io.Writer].
func ExecutorWithStdout(stdout io.Writer) ExecutorOption {
return func(e *Executor) {
e.Stdout = stdout
}
}
// ExecutorWithStderr sets the [Executor]'s standard error [io.Writer].
func ExecutorWithStderr(stderr io.Writer) ExecutorOption {
return func(e *Executor) {
e.Stderr = stderr
}
}
// ExecutorWithIO sets the [Executor]'s standard input, output, and error to the
// same [io.ReadWriter].
func ExecutorWithIO(rw io.ReadWriter) ExecutorOption {
return func(e *Executor) {
e.Stdin = rw
e.Stdout = rw
e.Stderr = rw
}
}
// ExecutorWithVersionCheck tells the [Executor] whether or not to check the
// version of
func ExecutorWithVersionCheck(enableVersionCheck bool) ExecutorOption {
return func(e *Executor) {
e.EnableVersionCheck = enableVersionCheck
}
}