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 } }