2022-07-08 20:16:04 +02:00
|
|
|
package task
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
"strings"
|
|
|
|
"sync"
|
|
|
|
|
2023-02-08 12:21:43 +02:00
|
|
|
"github.com/Masterminds/semver/v3"
|
|
|
|
"github.com/sajari/fuzzy"
|
|
|
|
|
2024-01-12 00:30:02 +02:00
|
|
|
"github.com/go-task/task/v3/errors"
|
2023-12-29 22:26:02 +02:00
|
|
|
"github.com/go-task/task/v3/internal/compiler"
|
2022-07-08 20:16:04 +02:00
|
|
|
"github.com/go-task/task/v3/internal/execext"
|
2022-08-06 23:19:07 +02:00
|
|
|
"github.com/go-task/task/v3/internal/filepathext"
|
2022-07-08 20:16:04 +02:00
|
|
|
"github.com/go-task/task/v3/internal/logger"
|
|
|
|
"github.com/go-task/task/v3/internal/output"
|
2024-01-12 00:30:02 +02:00
|
|
|
"github.com/go-task/task/v3/internal/version"
|
2022-07-08 20:16:04 +02:00
|
|
|
"github.com/go-task/task/v3/taskfile"
|
2023-12-29 22:32:03 +02:00
|
|
|
"github.com/go-task/task/v3/taskfile/ast"
|
2022-07-08 20:16:04 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
func (e *Executor) Setup() error {
|
2023-09-12 23:42:54 +02:00
|
|
|
e.setupLogger()
|
2022-09-03 23:14:54 +02:00
|
|
|
if err := e.setCurrentDir(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2023-09-14 23:57:46 +02:00
|
|
|
if err := e.setupTempDir(); err != nil {
|
2022-07-08 20:16:04 +02:00
|
|
|
return err
|
|
|
|
}
|
2023-09-14 23:57:46 +02:00
|
|
|
if err := e.readTaskfile(); err != nil {
|
2022-07-08 20:16:04 +02:00
|
|
|
return err
|
|
|
|
}
|
2023-09-12 23:42:54 +02:00
|
|
|
e.setupFuzzyModel()
|
2022-07-08 20:16:04 +02:00
|
|
|
e.setupStdFiles()
|
|
|
|
if err := e.setupOutput(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2023-02-08 12:21:43 +02:00
|
|
|
if err := e.setupCompiler(); err != nil {
|
2022-07-08 20:16:04 +02:00
|
|
|
return err
|
|
|
|
}
|
2023-02-08 12:21:43 +02:00
|
|
|
if err := e.readDotEnvFiles(); err != nil {
|
2022-07-08 20:16:04 +02:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2023-02-08 12:21:43 +02:00
|
|
|
if err := e.doVersionChecks(); err != nil {
|
2022-07-08 20:16:04 +02:00
|
|
|
return err
|
|
|
|
}
|
2023-02-08 12:21:43 +02:00
|
|
|
e.setupDefaults()
|
2022-07-08 20:16:04 +02:00
|
|
|
e.setupConcurrencyState()
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-09-03 23:14:54 +02:00
|
|
|
func (e *Executor) setCurrentDir() error {
|
2023-09-15 00:15:54 +02:00
|
|
|
// If the entrypoint is already set, we don't need to do anything
|
|
|
|
if e.Entrypoint != "" {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-09-14 23:57:46 +02:00
|
|
|
// Default the directory to the current working directory
|
2022-09-03 23:14:54 +02:00
|
|
|
if e.Dir == "" {
|
|
|
|
wd, err := os.Getwd()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
e.Dir = wd
|
2023-09-14 23:57:46 +02:00
|
|
|
}
|
|
|
|
|
2023-09-15 00:15:54 +02:00
|
|
|
// Search for a taskfile
|
2023-12-29 22:32:03 +02:00
|
|
|
root, err := taskfile.ExistsWalk(e.Dir)
|
2023-09-14 23:57:46 +02:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2023-09-15 00:15:54 +02:00
|
|
|
e.Dir = filepath.Dir(root)
|
|
|
|
e.Entrypoint = filepath.Base(root)
|
2023-09-14 23:57:46 +02:00
|
|
|
|
2022-09-03 23:14:54 +02:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-07-08 20:16:04 +02:00
|
|
|
func (e *Executor) readTaskfile() error {
|
2023-09-12 23:42:54 +02:00
|
|
|
uri := filepath.Join(e.Dir, e.Entrypoint)
|
2023-12-29 22:32:03 +02:00
|
|
|
node, err := taskfile.NewNode(uri, e.Insecure)
|
2023-09-12 23:42:54 +02:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2023-12-29 22:32:03 +02:00
|
|
|
e.Taskfile, err = taskfile.Read(
|
2023-09-12 23:42:54 +02:00
|
|
|
node,
|
|
|
|
e.Insecure,
|
|
|
|
e.Download,
|
|
|
|
e.Offline,
|
2023-11-17 22:51:10 +02:00
|
|
|
e.Timeout,
|
2023-09-12 23:42:54 +02:00
|
|
|
e.TempDir,
|
|
|
|
e.Logger,
|
|
|
|
)
|
2023-09-02 22:24:01 +02:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
2022-07-08 20:16:04 +02:00
|
|
|
}
|
|
|
|
|
2022-10-02 17:49:38 +02:00
|
|
|
func (e *Executor) setupFuzzyModel() {
|
|
|
|
if e.Taskfile != nil {
|
2022-11-02 15:23:19 +02:00
|
|
|
return
|
|
|
|
}
|
2022-10-02 17:49:38 +02:00
|
|
|
|
2022-11-02 15:23:19 +02:00
|
|
|
model := fuzzy.NewModel()
|
|
|
|
model.SetThreshold(1) // because we want to build grammar based on every task name
|
2022-10-15 01:08:00 +02:00
|
|
|
|
2022-11-02 15:23:19 +02:00
|
|
|
var words []string
|
2023-04-06 13:07:57 +02:00
|
|
|
for _, taskName := range e.Taskfile.Tasks.Keys() {
|
2022-11-02 15:23:19 +02:00
|
|
|
words = append(words, taskName)
|
2022-10-02 17:49:38 +02:00
|
|
|
|
2023-04-06 13:07:57 +02:00
|
|
|
for _, task := range e.Taskfile.Tasks.Values() {
|
2022-11-02 15:23:19 +02:00
|
|
|
words = append(words, task.Aliases...)
|
|
|
|
}
|
2022-10-02 17:49:38 +02:00
|
|
|
}
|
2022-11-02 15:23:19 +02:00
|
|
|
|
|
|
|
model.Train(words)
|
|
|
|
e.fuzzyModel = model
|
2022-10-02 17:49:38 +02:00
|
|
|
}
|
|
|
|
|
2022-07-08 20:16:04 +02:00
|
|
|
func (e *Executor) setupTempDir() error {
|
|
|
|
if e.TempDir != "" {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
if os.Getenv("TASK_TEMP_DIR") == "" {
|
2022-08-06 23:19:07 +02:00
|
|
|
e.TempDir = filepathext.SmartJoin(e.Dir, ".task")
|
2022-07-08 20:16:04 +02:00
|
|
|
} else if filepath.IsAbs(os.Getenv("TASK_TEMP_DIR")) || strings.HasPrefix(os.Getenv("TASK_TEMP_DIR"), "~") {
|
|
|
|
tempDir, err := execext.Expand(os.Getenv("TASK_TEMP_DIR"))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
projectDir, _ := filepath.Abs(e.Dir)
|
|
|
|
projectName := filepath.Base(projectDir)
|
2022-08-06 23:19:07 +02:00
|
|
|
e.TempDir = filepathext.SmartJoin(tempDir, projectName)
|
2022-07-08 20:16:04 +02:00
|
|
|
} else {
|
2022-08-06 23:19:07 +02:00
|
|
|
e.TempDir = filepathext.SmartJoin(e.Dir, os.Getenv("TASK_TEMP_DIR"))
|
2022-07-08 20:16:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *Executor) setupStdFiles() {
|
|
|
|
if e.Stdin == nil {
|
|
|
|
e.Stdin = os.Stdin
|
|
|
|
}
|
|
|
|
if e.Stdout == nil {
|
|
|
|
e.Stdout = os.Stdout
|
|
|
|
}
|
|
|
|
if e.Stderr == nil {
|
|
|
|
e.Stderr = os.Stderr
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *Executor) setupLogger() {
|
|
|
|
e.Logger = &logger.Logger{
|
2023-10-07 23:55:43 +02:00
|
|
|
Stdin: e.Stdin,
|
|
|
|
Stdout: e.Stdout,
|
|
|
|
Stderr: e.Stderr,
|
|
|
|
Verbose: e.Verbose,
|
|
|
|
Color: e.Color,
|
|
|
|
AssumeYes: e.AssumeYes,
|
|
|
|
AssumeTerm: e.AssumeTerm,
|
2022-07-08 20:16:04 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *Executor) setupOutput() error {
|
|
|
|
if !e.OutputStyle.IsSet() {
|
|
|
|
e.OutputStyle = e.Taskfile.Output
|
|
|
|
}
|
|
|
|
|
|
|
|
var err error
|
|
|
|
e.Output, err = output.BuildFor(&e.OutputStyle)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2023-02-08 12:21:43 +02:00
|
|
|
func (e *Executor) setupCompiler() error {
|
2023-12-29 22:26:02 +02:00
|
|
|
if e.UserWorkingDir == "" {
|
2022-07-08 20:16:04 +02:00
|
|
|
var err error
|
2023-12-29 22:26:02 +02:00
|
|
|
e.UserWorkingDir, err = os.Getwd()
|
2022-07-08 20:16:04 +02:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-12-29 22:26:02 +02:00
|
|
|
e.Compiler = &compiler.Compiler{
|
|
|
|
Dir: e.Dir,
|
|
|
|
UserWorkingDir: e.UserWorkingDir,
|
|
|
|
TaskfileEnv: e.Taskfile.Env,
|
|
|
|
TaskfileVars: e.Taskfile.Vars,
|
|
|
|
Logger: e.Logger,
|
|
|
|
}
|
2022-07-08 20:16:04 +02:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-02-08 12:21:43 +02:00
|
|
|
func (e *Executor) readDotEnvFiles() error {
|
2023-12-29 22:32:03 +02:00
|
|
|
if e.Taskfile.Version.LessThan(ast.V3) {
|
2022-07-08 20:16:04 +02:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-12-29 22:32:03 +02:00
|
|
|
env, err := taskfile.Dotenv(e.Compiler, e.Taskfile, e.Dir)
|
2022-07-08 20:16:04 +02:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2023-12-29 22:32:03 +02:00
|
|
|
err = env.Range(func(key string, value ast.Var) error {
|
2023-04-06 13:07:57 +02:00
|
|
|
if ok := e.Taskfile.Env.Exists(key); !ok {
|
2022-07-08 20:16:04 +02:00
|
|
|
e.Taskfile.Env.Set(key, value)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2023-02-08 12:21:43 +02:00
|
|
|
func (e *Executor) setupDefaults() {
|
2022-07-08 20:16:04 +02:00
|
|
|
if e.Taskfile.Method == "" {
|
2023-12-29 22:26:02 +02:00
|
|
|
e.Taskfile.Method = "checksum"
|
2022-07-08 20:16:04 +02:00
|
|
|
}
|
|
|
|
if e.Taskfile.Run == "" {
|
|
|
|
e.Taskfile.Run = "always"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *Executor) setupConcurrencyState() {
|
|
|
|
e.executionHashes = make(map[string]context.Context)
|
|
|
|
|
2023-04-06 13:07:57 +02:00
|
|
|
e.taskCallCount = make(map[string]*int32, e.Taskfile.Tasks.Len())
|
|
|
|
e.mkdirMutexMap = make(map[string]*sync.Mutex, e.Taskfile.Tasks.Len())
|
|
|
|
for _, k := range e.Taskfile.Tasks.Keys() {
|
2022-07-08 20:16:04 +02:00
|
|
|
e.taskCallCount[k] = new(int32)
|
|
|
|
e.mkdirMutexMap[k] = &sync.Mutex{}
|
|
|
|
}
|
|
|
|
|
|
|
|
if e.Concurrency > 0 {
|
|
|
|
e.concurrencySemaphore = make(chan struct{}, e.Concurrency)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-08 12:21:43 +02:00
|
|
|
func (e *Executor) doVersionChecks() error {
|
|
|
|
// Copy the version to avoid modifying the original
|
2024-01-12 00:30:02 +02:00
|
|
|
schemaVersion := &semver.Version{}
|
|
|
|
*schemaVersion = *e.Taskfile.Version
|
|
|
|
|
|
|
|
// Error if the Taskfile uses a schema version below v3
|
|
|
|
if schemaVersion.LessThan(ast.V3) {
|
|
|
|
return &errors.TaskfileVersionCheckError{
|
|
|
|
URI: e.Taskfile.Location,
|
|
|
|
SchemaVersion: schemaVersion,
|
|
|
|
Message: `no longer supported. Please use v3 or above`,
|
|
|
|
}
|
2022-07-08 20:16:04 +02:00
|
|
|
}
|
|
|
|
|
2024-01-12 00:30:02 +02:00
|
|
|
// Get the current version of Task
|
|
|
|
// If we can't parse the version (e.g. when its "devel"), then ignore the current version checks
|
|
|
|
currentVersion, err := semver.NewVersion(version.GetVersion())
|
|
|
|
if err != nil {
|
|
|
|
return nil
|
2022-07-08 20:16:04 +02:00
|
|
|
}
|
|
|
|
|
2024-01-12 00:30:02 +02:00
|
|
|
// Error if the Taskfile uses a schema version above the current version of Task
|
|
|
|
if schemaVersion.GreaterThan(currentVersion) {
|
|
|
|
return &errors.TaskfileVersionCheckError{
|
|
|
|
URI: e.Taskfile.Location,
|
|
|
|
SchemaVersion: schemaVersion,
|
|
|
|
Message: fmt.Sprintf(`is greater than the current version of Task (%s)`, currentVersion.String()),
|
2022-07-08 20:16:04 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|