This repository was archived by the owner on Mar 4, 2026. It is now read-only.
forked from go-task/task
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathexecutor.go
More file actions
636 lines (515 loc) · 15.4 KB
/
executor.go
File metadata and controls
636 lines (515 loc) · 15.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
package task
import (
"context"
"io"
"os"
"sync"
"time"
"github.com/puzpuzpuz/xsync/v4"
"github.com/sajari/fuzzy"
"github.com/go-task/task/v3/internal/logger"
"github.com/go-task/task/v3/internal/metrics"
"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 any type that can apply a configuration to an
// [Executor].
ExecutorOption interface {
ApplyToExecutor(*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
TrustedHosts []string
Timeout time.Duration
CacheExpiryDuration time.Duration
RemoteCacheDir string
CACert string
Cert string
CertKey string
Watch bool
Verbose bool
Silent bool
DisableFuzzy bool
AssumeYes bool
AssumeTerm bool // Used for testing
Interactive bool
Dry bool
Summary bool
Parallel bool
Color bool
Concurrency int
Interval time.Duration
Failfast bool
TimingJSONPath string
// 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
fuzzyModelOnce sync.Once
promptedVars *ast.Vars // vars collected via interactive prompts
concurrencySemaphore chan struct{}
taskCallCount map[string]*int32
mkdirMutexMap map[string]*sync.Mutex
executionHashes map[string]context.Context
executionHashesMutex sync.Mutex
watchedDirs *xsync.Map[string, bool]
metricsCollector *metrics.Collector
}
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.ApplyToExecutor(e)
}
}
// WithDir sets the working directory of the [Executor]. By default, the
// directory is set to the user's current working directory.
func WithDir(dir string) ExecutorOption {
return &dirOption{dir}
}
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 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
// 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 WithForce(force bool) ExecutorOption {
return &forceOption{force}
}
type forceOption struct {
force bool
}
func (o *forceOption) ApplyToExecutor(e *Executor) {
e.Force = o.force
}
// 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}
}
type forceAllOption struct {
forceAll bool
}
func (o *forceAllOption) ApplyToExecutor(e *Executor) {
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
}
// WithTrustedHosts configures the [Executor] with a list of trusted hosts for remote
// Taskfiles. Hosts in this list will not prompt for user confirmation.
func WithTrustedHosts(trustedHosts []string) ExecutorOption {
return &trustedHostsOption{trustedHosts}
}
type trustedHostsOption struct {
trustedHosts []string
}
func (o *trustedHostsOption) ApplyToExecutor(e *Executor) {
e.TrustedHosts = o.trustedHosts
}
// 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 0 (disabled).
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
}
// WithRemoteCacheDir sets the directory where remote taskfiles are cached.
func WithRemoteCacheDir(dir string) ExecutorOption {
return &remoteCacheDirOption{dir: dir}
}
type remoteCacheDirOption struct {
dir string
}
func (o *remoteCacheDirOption) ApplyToExecutor(e *Executor) {
e.RemoteCacheDir = o.dir
}
// WithCACert sets the path to a custom CA certificate for TLS connections.
func WithCACert(caCert string) ExecutorOption {
return &caCertOption{caCert: caCert}
}
type caCertOption struct {
caCert string
}
func (o *caCertOption) ApplyToExecutor(e *Executor) {
e.CACert = o.caCert
}
// WithCert sets the path to a client certificate for TLS connections.
func WithCert(cert string) ExecutorOption {
return &certOption{cert: cert}
}
type certOption struct {
cert string
}
func (o *certOption) ApplyToExecutor(e *Executor) {
e.Cert = o.cert
}
// WithCertKey sets the path to a client certificate key for TLS connections.
func WithCertKey(certKey string) ExecutorOption {
return &certKeyOption{certKey: certKey}
}
type certKeyOption struct {
certKey string
}
func (o *certKeyOption) ApplyToExecutor(e *Executor) {
e.CertKey = o.certKey
}
// 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
}
// WithDisableFuzzy tells the [Executor] to disable fuzzy matching for task names.
func WithDisableFuzzy(disableFuzzy bool) ExecutorOption {
return &disableFuzzyOption{disableFuzzy}
}
type disableFuzzyOption struct {
disableFuzzy bool
}
func (o *disableFuzzyOption) ApplyToExecutor(e *Executor) {
e.DisableFuzzy = o.disableFuzzy
}
// 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 WithAssumeTerm(assumeTerm bool) ExecutorOption {
return &assumeTermOption{assumeTerm}
}
type assumeTermOption struct {
assumeTerm bool
}
func (o *assumeTermOption) ApplyToExecutor(e *Executor) {
e.AssumeTerm = o.assumeTerm
}
// WithInteractive tells the [Executor] to prompt for missing required variables.
func WithInteractive(interactive bool) ExecutorOption {
return &interactiveOption{interactive}
}
type interactiveOption struct {
interactive bool
}
func (o *interactiveOption) ApplyToExecutor(e *Executor) {
e.Interactive = o.interactive
}
// 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 ¶llelOption{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 WithConcurrency(concurrency int) ExecutorOption {
return &concurrencyOption{concurrency}
}
type concurrencyOption struct {
concurrency int
}
func (o *concurrencyOption) ApplyToExecutor(e *Executor) {
e.Concurrency = o.concurrency
}
// WithInterval sets the interval at which the [Executor] will wait for
// duplicated events before running a task.
func WithInterval(interval time.Duration) ExecutorOption {
return &intervalOption{interval}
}
type intervalOption struct {
interval time.Duration
}
func (o *intervalOption) ApplyToExecutor(e *Executor) {
e.Interval = o.interval
}
// 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}
}
type outputStyleOption struct {
outputStyle ast.Output
}
func (o *outputStyleOption) ApplyToExecutor(e *Executor) {
e.OutputStyle = o.outputStyle
}
// 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}
}
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
}
// WithFailfast tells the [Executor] whether or not to check the version of
func WithFailfast(failfast bool) ExecutorOption {
return &failfastOption{failfast}
}
type failfastOption struct {
failfast bool
}
func (o *failfastOption) ApplyToExecutor(e *Executor) {
e.Failfast = o.failfast
}
// WithTimingJSONPath sets the file path for writing task timing data as JSON.
// When set, task execution timing will be collected and written to this file.
func WithTimingJSONPath(path string) ExecutorOption {
return &timingJSONPathOption{path}
}
type timingJSONPathOption struct {
path string
}
func (o *timingJSONPathOption) ApplyToExecutor(e *Executor) {
e.TimingJSONPath = o.path
}