1
0
mirror of https://github.com/go-task/task.git synced 2025-11-23 22:24:45 +02:00

feat: better functional options (#2147)

This commit is contained in:
Pete Davison
2025-03-31 21:49:00 +01:00
committed by GitHub
parent 4736bc2734
commit 2a92b70bc2
6 changed files with 710 additions and 546 deletions

View File

@@ -18,7 +18,9 @@ import (
type (
// An ExecutorOption is a functional option for an [Executor].
ExecutorOption func(*Executor)
ExecutorOption interface {
ApplyToExecutor(*Executor)
}
// An Executor is used for processing Taskfile(s) and executing the task(s)
// within them.
Executor struct {
@@ -103,226 +105,384 @@ func NewExecutor(opts ...ExecutorOption) *Executor {
// to the [Executor].
func (e *Executor) Options(opts ...ExecutorOption) {
for _, opt := range opts {
opt(e)
opt.ApplyToExecutor(e)
}
}
// ExecutorWithDir sets the working directory of the [Executor]. By default, the
// WithDir 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
}
func WithDir(dir string) ExecutorOption {
return dirOption{dir}
}
// ExecutorWithEntrypoint sets the entrypoint (main Taskfile) of the [Executor].
// By default, Task will search for one of the default Taskfiles in the given
type dirOption struct {
dir string
}
func (o dirOption) ApplyToExecutor(e *Executor) {
e.Dir = o.dir
}
// WithEntrypoint 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
}
func WithEntrypoint(entrypoint string) ExecutorOption {
return entrypointOption{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
}
type entrypointOption struct {
entrypoint string
}
// ExecutorWithForce ensures that the [Executor] always runs a task, even when
func (o entrypointOption) ApplyToExecutor(e *Executor) {
e.Entrypoint = o.entrypoint
}
// WithTempDir 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 WithTempDir(tempDir TempDir) ExecutorOption {
return tempDirOption{tempDir}
}
type tempDirOption struct {
tempDir TempDir
}
func (o tempDirOption) ApplyToExecutor(e *Executor) {
e.TempDir = o.tempDir
}
// WithForce 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
}
func WithForce(force bool) ExecutorOption {
return forceOption{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
}
type forceOption struct {
force bool
}
// 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
}
func (o forceOption) ApplyToExecutor(e *Executor) {
e.Force = o.force
}
// 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
}
// WithForceAll ensures that the [Executor] always runs all tasks (including
// subtasks), even when fingerprinting or prompts would normally stop them.
func WithForceAll(forceAll bool) ExecutorOption {
return forceAllOption{forceAll}
}
// 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
}
type forceAllOption struct {
forceAll bool
}
// 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
}
func (o forceAllOption) ApplyToExecutor(e *Executor) {
e.ForceAll = o.forceAll
}
// 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
}
// 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}
}
// 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
}
type insecureOption struct {
insecure bool
}
// 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
}
func (o insecureOption) ApplyToExecutor(e *Executor) {
e.Insecure = o.insecure
}
// ExecutorWithAssumeYes tells the [Executor] to assume "yes" for all prompts.
func ExecutorWithAssumeYes(assumeYes bool) ExecutorOption {
return func(e *Executor) {
e.AssumeYes = assumeYes
}
// 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
}
// 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
// detected, a new task run is triggered.
func WithWatch(watch bool) ExecutorOption {
return watchOption{watch}
}
type watchOption struct {
watch bool
}
func (o watchOption) ApplyToExecutor(e *Executor) {
e.Watch = o.watch
}
// WithVerbose tells the [Executor] to output more information about the tasks
// that are run.
func WithVerbose(verbose bool) ExecutorOption {
return verboseOption{verbose}
}
type verboseOption struct {
verbose bool
}
func (o verboseOption) ApplyToExecutor(e *Executor) {
e.Verbose = o.verbose
}
// WithSilent tells the [Executor] to suppress all output except for the output
// of the tasks that are run.
func WithSilent(silent bool) ExecutorOption {
return silentOption{silent}
}
type silentOption struct {
silent bool
}
func (o silentOption) ApplyToExecutor(e *Executor) {
e.Silent = o.silent
}
// WithAssumeYes tells the [Executor] to assume "yes" for all prompts.
func WithAssumeYes(assumeYes bool) ExecutorOption {
return assumeYesOption{assumeYes}
}
type assumeYesOption struct {
assumeYes bool
}
func (o assumeYesOption) ApplyToExecutor(e *Executor) {
e.AssumeYes = o.assumeYes
}
// WithAssumeTerm is used for testing purposes to simulate a terminal.
func ExecutorWithAssumeTerm(assumeTerm bool) ExecutorOption {
return func(e *Executor) {
e.AssumeTerm = assumeTerm
}
func WithAssumeTerm(assumeTerm bool) ExecutorOption {
return assumeTermOption{assumeTerm}
}
// ExecutorWithDry tells the [Executor] to output the commands that would be run
// without actually running them.
func ExecutorWithDry(dry bool) ExecutorOption {
return func(e *Executor) {
e.Dry = dry
}
type assumeTermOption struct {
assumeTerm bool
}
// 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
}
func (o assumeTermOption) ApplyToExecutor(e *Executor) {
e.AssumeTerm = o.assumeTerm
}
// ExecutorWithParallel tells the [Executor] to run tasks given in the same call
// WithDry tells the [Executor] to output the commands that would be run without
// actually running them.
func WithDry(dry bool) ExecutorOption {
return dryOption{dry}
}
type dryOption struct {
dry bool
}
func (o dryOption) ApplyToExecutor(e *Executor) {
e.Dry = o.dry
}
// WithSummary tells the [Executor] to output a summary of the given tasks
// instead of running them.
func WithSummary(summary bool) ExecutorOption {
return summaryOption{summary}
}
type summaryOption struct {
summary bool
}
func (o summaryOption) ApplyToExecutor(e *Executor) {
e.Summary = o.summary
}
// WithParallel tells the [Executor] to run tasks given in the same call in
// parallel.
func WithParallel(parallel bool) ExecutorOption {
return parallelOption{parallel}
}
type parallelOption struct {
parallel bool
}
func (o parallelOption) ApplyToExecutor(e *Executor) {
e.Parallel = o.parallel
}
// WithColor tells the [Executor] whether or not to output using colorized
// strings.
func WithColor(color bool) ExecutorOption {
return colorOption{color}
}
type colorOption struct {
color bool
}
func (o colorOption) ApplyToExecutor(e *Executor) {
e.Color = o.color
}
// WithConcurrency sets the maximum number of tasks that the [Executor] can run
// in parallel.
func ExecutorWithParallel(parallel bool) ExecutorOption {
return func(e *Executor) {
e.Parallel = parallel
}
func WithConcurrency(concurrency int) ExecutorOption {
return concurrencyOption{concurrency}
}
// ExecutorWithColor tells the [Executor] whether or not to output using
// colorized strings.
func ExecutorWithColor(color bool) ExecutorOption {
return func(e *Executor) {
e.Color = color
}
type concurrencyOption struct {
concurrency int
}
// 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
}
func (o concurrencyOption) ApplyToExecutor(e *Executor) {
e.Concurrency = o.concurrency
}
// ExecutorWithInterval sets the interval at which the [Executor] will wait for
// WithInterval 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
}
func WithInterval(interval time.Duration) ExecutorOption {
return intervalOption{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
}
type intervalOption struct {
interval time.Duration
}
// 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
}
func (o intervalOption) ApplyToExecutor(e *Executor) {
e.Interval = o.interval
}
// ExecutorWithStdin sets the [Executor]'s standard input [io.Reader].
func ExecutorWithStdin(stdin io.Reader) ExecutorOption {
return func(e *Executor) {
e.Stdin = stdin
}
// WithOutputStyle sets the output style of the [Executor]. By default, the
// output style is set to the style defined in the Taskfile.
func WithOutputStyle(outputStyle ast.Output) ExecutorOption {
return outputStyleOption{outputStyle}
}
// ExecutorWithStdout sets the [Executor]'s standard output [io.Writer].
func ExecutorWithStdout(stdout io.Writer) ExecutorOption {
return func(e *Executor) {
e.Stdout = stdout
}
type outputStyleOption struct {
outputStyle ast.Output
}
// ExecutorWithStderr sets the [Executor]'s standard error [io.Writer].
func ExecutorWithStderr(stderr io.Writer) ExecutorOption {
return func(e *Executor) {
e.Stderr = stderr
}
func (o outputStyleOption) ApplyToExecutor(e *Executor) {
e.OutputStyle = o.outputStyle
}
// 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
}
// WithTaskSorter 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 WithTaskSorter(sorter sort.Sorter) ExecutorOption {
return taskSorterOption{sorter}
}
// ExecutorWithVersionCheck tells the [Executor] whether or not to check the
// version of
func ExecutorWithVersionCheck(enableVersionCheck bool) ExecutorOption {
return func(e *Executor) {
e.EnableVersionCheck = enableVersionCheck
}
type taskSorterOption struct {
sorter sort.Sorter
}
func (o taskSorterOption) ApplyToExecutor(e *Executor) {
e.TaskSorter = o.sorter
}
// WithStdin sets the [Executor]'s standard input [io.Reader].
func WithStdin(stdin io.Reader) ExecutorOption {
return stdinOption{stdin}
}
type stdinOption struct {
stdin io.Reader
}
func (o stdinOption) ApplyToExecutor(e *Executor) {
e.Stdin = o.stdin
}
// WithStdout sets the [Executor]'s standard output [io.Writer].
func WithStdout(stdout io.Writer) ExecutorOption {
return stdoutOption{stdout}
}
type stdoutOption struct {
stdout io.Writer
}
func (o stdoutOption) ApplyToExecutor(e *Executor) {
e.Stdout = o.stdout
}
// WithStderr sets the [Executor]'s standard error [io.Writer].
func WithStderr(stderr io.Writer) ExecutorOption {
return stderrOption{stderr}
}
type stderrOption struct {
stderr io.Writer
}
func (o stderrOption) ApplyToExecutor(e *Executor) {
e.Stderr = o.stderr
}
// WithIO sets the [Executor]'s standard input, output, and error to the same
// [io.ReadWriter].
func WithIO(rw io.ReadWriter) ExecutorOption {
return ioOption{rw}
}
type ioOption struct {
rw io.ReadWriter
}
func (o ioOption) ApplyToExecutor(e *Executor) {
e.Stdin = o.rw
e.Stdout = o.rw
e.Stderr = o.rw
}
// WithVersionCheck tells the [Executor] whether or not to check the version of
func WithVersionCheck(enableVersionCheck bool) ExecutorOption {
return versionCheckOption{enableVersionCheck}
}
type versionCheckOption struct {
enableVersionCheck bool
}
func (o versionCheckOption) ApplyToExecutor(e *Executor) {
e.EnableVersionCheck = o.enableVersionCheck
}