2025-02-23 18:30:42 +00:00
|
|
|
package task
|
2018-02-17 16:12:41 -02:00
|
|
|
|
|
|
|
|
import (
|
2023-12-29 20:26:02 +00:00
|
|
|
"bytes"
|
|
|
|
|
"context"
|
|
|
|
|
"fmt"
|
2024-05-16 01:50:18 +01:00
|
|
|
"os"
|
2024-03-03 16:45:23 -06:00
|
|
|
"path/filepath"
|
2023-12-29 20:26:02 +00:00
|
|
|
"strings"
|
|
|
|
|
"sync"
|
|
|
|
|
|
2025-12-26 21:08:37 +01:00
|
|
|
"github.com/go-task/task/v3/experiments"
|
2025-01-02 21:07:25 +01:00
|
|
|
"github.com/go-task/task/v3/internal/env"
|
2023-12-29 20:26:02 +00:00
|
|
|
"github.com/go-task/task/v3/internal/execext"
|
|
|
|
|
"github.com/go-task/task/v3/internal/filepathext"
|
|
|
|
|
"github.com/go-task/task/v3/internal/logger"
|
|
|
|
|
"github.com/go-task/task/v3/internal/templater"
|
|
|
|
|
"github.com/go-task/task/v3/internal/version"
|
2023-12-29 20:32:03 +00:00
|
|
|
"github.com/go-task/task/v3/taskfile/ast"
|
2018-02-17 16:12:41 -02:00
|
|
|
)
|
|
|
|
|
|
2023-12-29 20:26:02 +00:00
|
|
|
type Compiler struct {
|
|
|
|
|
Dir string
|
2024-01-26 00:11:08 +00:00
|
|
|
Entrypoint string
|
2023-12-29 20:26:02 +00:00
|
|
|
UserWorkingDir string
|
|
|
|
|
|
2023-12-29 20:32:03 +00:00
|
|
|
TaskfileEnv *ast.Vars
|
|
|
|
|
TaskfileVars *ast.Vars
|
2025-12-29 16:45:41 +01:00
|
|
|
CLIVars *ast.Vars // CLI vars passed via command line (e.g., task foo VAR=value)
|
2025-12-26 21:08:37 +01:00
|
|
|
Graph *ast.TaskfileGraph
|
2023-12-29 20:26:02 +00:00
|
|
|
|
|
|
|
|
Logger *logger.Logger
|
|
|
|
|
|
|
|
|
|
dynamicCache map[string]string
|
|
|
|
|
muDynamicCache sync.Mutex
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-29 20:32:03 +00:00
|
|
|
func (c *Compiler) GetTaskfileVariables() (*ast.Vars, error) {
|
2023-12-29 20:26:02 +00:00
|
|
|
return c.getVariables(nil, nil, true)
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-23 18:30:42 +00:00
|
|
|
func (c *Compiler) GetVariables(t *ast.Task, call *Call) (*ast.Vars, error) {
|
2024-01-26 14:34:18 +00:00
|
|
|
return c.getVariables(t, call, true)
|
2023-12-29 20:26:02 +00:00
|
|
|
}
|
|
|
|
|
|
2025-02-23 18:30:42 +00:00
|
|
|
func (c *Compiler) FastGetVariables(t *ast.Task, call *Call) (*ast.Vars, error) {
|
2024-01-26 14:34:18 +00:00
|
|
|
return c.getVariables(t, call, false)
|
2023-12-29 20:26:02 +00:00
|
|
|
}
|
|
|
|
|
|
2026-01-14 19:34:53 +01:00
|
|
|
// isScopedMode returns true if scoped variable resolution should be used.
|
|
|
|
|
// Scoped mode requires the experiment to be enabled, a task with location info, and a graph.
|
|
|
|
|
func (c *Compiler) isScopedMode(t *ast.Task) bool {
|
|
|
|
|
return experiments.ScopedTaskfiles.Enabled() &&
|
|
|
|
|
t != nil &&
|
|
|
|
|
t.Location != nil &&
|
|
|
|
|
c.Graph != nil
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-23 18:30:42 +00:00
|
|
|
func (c *Compiler) getVariables(t *ast.Task, call *Call, evaluateShVars bool) (*ast.Vars, error) {
|
2026-01-14 19:34:53 +01:00
|
|
|
if c.isScopedMode(t) {
|
|
|
|
|
return c.getScopedVariables(t, call, evaluateShVars)
|
2025-12-29 16:31:51 +01:00
|
|
|
}
|
2026-01-14 19:34:53 +01:00
|
|
|
return c.getLegacyVariables(t, call, evaluateShVars)
|
|
|
|
|
}
|
2025-12-29 16:31:51 +01:00
|
|
|
|
2026-01-14 19:34:53 +01:00
|
|
|
// getScopedVariables resolves variables in scoped mode.
|
|
|
|
|
// In scoped mode:
|
|
|
|
|
// - OS env vars are in {{.env.XXX}} namespace, not at root
|
|
|
|
|
// - Variables from sibling includes are isolated
|
|
|
|
|
//
|
|
|
|
|
// Variable resolution order (lowest to highest priority):
|
|
|
|
|
// 1. Root Taskfile vars
|
|
|
|
|
// 2. Include Taskfile vars
|
|
|
|
|
// 3. Include passthrough vars (includes: name: vars:)
|
|
|
|
|
// 4. Task vars
|
|
|
|
|
// 5. Call vars
|
|
|
|
|
// 6. CLI vars
|
|
|
|
|
func (c *Compiler) getScopedVariables(t *ast.Task, call *Call, evaluateShVars bool) (*ast.Vars, error) {
|
|
|
|
|
result := ast.NewVars()
|
|
|
|
|
|
2024-10-09 03:14:23 -04:00
|
|
|
specialVars, err := c.getSpecialVars(t, call)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
for k, v := range specialVars {
|
|
|
|
|
result.Set(k, ast.Var{Value: v})
|
2023-12-29 20:26:02 +00:00
|
|
|
}
|
|
|
|
|
|
2023-12-29 20:32:03 +00:00
|
|
|
getRangeFunc := func(dir string) func(k string, v ast.Var) error {
|
|
|
|
|
return func(k string, v ast.Var) error {
|
2024-03-10 17:11:07 +00:00
|
|
|
cache := &templater.Cache{Vars: result}
|
|
|
|
|
newVar := templater.ReplaceVar(v, cache)
|
2023-12-29 20:26:02 +00:00
|
|
|
if !evaluateShVars && newVar.Value == nil {
|
2025-11-29 11:14:20 +01:00
|
|
|
result.Set(k, ast.Var{Value: "", Sh: newVar.Sh})
|
2023-12-29 20:26:02 +00:00
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
if !evaluateShVars {
|
2025-11-29 11:14:20 +01:00
|
|
|
result.Set(k, ast.Var{Value: newVar.Value, Sh: newVar.Sh})
|
2023-12-29 20:26:02 +00:00
|
|
|
return nil
|
|
|
|
|
}
|
2024-03-10 17:11:07 +00:00
|
|
|
if err := cache.Err(); err != nil {
|
2023-12-29 20:26:02 +00:00
|
|
|
return err
|
|
|
|
|
}
|
2025-03-30 20:21:02 +01:00
|
|
|
if newVar.Value != nil || newVar.Sh == nil {
|
2023-12-29 20:32:03 +00:00
|
|
|
result.Set(k, ast.Var{Value: newVar.Value})
|
2023-12-29 20:26:02 +00:00
|
|
|
return nil
|
|
|
|
|
}
|
2025-01-02 21:07:25 +01:00
|
|
|
static, err := c.HandleDynamicVar(newVar, dir, env.GetFromVars(result))
|
2023-12-29 20:26:02 +00:00
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2023-12-29 20:32:03 +00:00
|
|
|
result.Set(k, ast.Var{Value: static})
|
2023-12-29 20:26:02 +00:00
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
rangeFunc := getRangeFunc(c.Dir)
|
|
|
|
|
|
2023-12-29 20:32:03 +00:00
|
|
|
var taskRangeFunc func(k string, v ast.Var) error
|
2023-12-29 20:26:02 +00:00
|
|
|
if t != nil {
|
2024-03-10 17:11:07 +00:00
|
|
|
cache := &templater.Cache{Vars: result}
|
|
|
|
|
dir := templater.Replace(t.Dir, cache)
|
|
|
|
|
if err := cache.Err(); err != nil {
|
2023-12-29 20:26:02 +00:00
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
dir = filepathext.SmartJoin(c.Dir, dir)
|
|
|
|
|
taskRangeFunc = getRangeFunc(dir)
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-14 19:34:53 +01:00
|
|
|
rootVertex, err := c.Graph.Root()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2025-12-26 21:08:37 +01:00
|
|
|
|
2026-01-14 19:34:53 +01:00
|
|
|
envMap := make(map[string]any)
|
|
|
|
|
for _, e := range os.Environ() {
|
|
|
|
|
k, v, _ := strings.Cut(e, "=")
|
|
|
|
|
envMap[k] = v
|
|
|
|
|
}
|
2025-12-29 16:31:51 +01:00
|
|
|
|
2026-01-14 19:34:53 +01:00
|
|
|
resolveEnvToMap := func(k string, v ast.Var, dir string) error {
|
|
|
|
|
cache := &templater.Cache{Vars: result}
|
|
|
|
|
newVar := templater.ReplaceVar(v, cache)
|
|
|
|
|
if err := cache.Err(); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if newVar.Value != nil || newVar.Sh == nil {
|
|
|
|
|
if newVar.Value != nil {
|
|
|
|
|
envMap[k] = newVar.Value
|
2025-12-29 16:31:51 +01:00
|
|
|
}
|
2026-01-14 19:34:53 +01:00
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
if evaluateShVars {
|
|
|
|
|
envSlice := os.Environ()
|
|
|
|
|
for ek, ev := range envMap {
|
|
|
|
|
if s, ok := ev.(string); ok {
|
|
|
|
|
envSlice = append(envSlice, fmt.Sprintf("%s=%s", ek, s))
|
2025-12-29 16:31:51 +01:00
|
|
|
}
|
|
|
|
|
}
|
2026-01-14 19:34:53 +01:00
|
|
|
static, err := c.HandleDynamicVar(newVar, dir, envSlice)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
2025-12-29 16:31:51 +01:00
|
|
|
}
|
2026-01-14 19:34:53 +01:00
|
|
|
envMap[k] = static
|
2025-12-29 16:31:51 +01:00
|
|
|
}
|
2026-01-14 19:34:53 +01:00
|
|
|
return nil
|
|
|
|
|
}
|
2025-12-29 16:31:51 +01:00
|
|
|
|
2026-01-14 19:34:53 +01:00
|
|
|
for k, v := range rootVertex.Taskfile.Env.All() {
|
|
|
|
|
if err := resolveEnvToMap(k, v, c.Dir); err != nil {
|
|
|
|
|
return nil, err
|
2025-02-22 16:22:03 +00:00
|
|
|
}
|
2026-01-14 19:34:53 +01:00
|
|
|
}
|
2025-12-26 21:08:37 +01:00
|
|
|
|
2026-01-14 19:34:53 +01:00
|
|
|
for k, v := range rootVertex.Taskfile.Vars.All() {
|
|
|
|
|
if err := rangeFunc(k, v); err != nil {
|
|
|
|
|
return nil, err
|
2023-12-29 20:26:02 +00:00
|
|
|
}
|
2026-01-14 19:34:53 +01:00
|
|
|
}
|
2025-12-26 21:08:37 +01:00
|
|
|
|
2026-01-14 19:34:53 +01:00
|
|
|
if t.Location.Taskfile != rootVertex.URI {
|
|
|
|
|
predecessorMap, err := c.Graph.PredecessorMap()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var parentChain []*ast.TaskfileVertex
|
|
|
|
|
currentURI := t.Location.Taskfile
|
|
|
|
|
for {
|
|
|
|
|
edges := predecessorMap[currentURI]
|
|
|
|
|
if len(edges) == 0 {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
var parentURI string
|
|
|
|
|
for _, edge := range edges {
|
|
|
|
|
parentURI = edge.Source
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
if parentURI == rootVertex.URI {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
parentVertex, err := c.Graph.Vertex(parentURI)
|
2025-12-26 21:08:37 +01:00
|
|
|
if err != nil {
|
2025-02-22 16:22:03 +00:00
|
|
|
return nil, err
|
|
|
|
|
}
|
2026-01-14 19:34:53 +01:00
|
|
|
parentChain = append([]*ast.TaskfileVertex{parentVertex}, parentChain...)
|
|
|
|
|
currentURI = parentURI
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, parent := range parentChain {
|
|
|
|
|
parentDir := filepath.Dir(parent.URI)
|
|
|
|
|
for k, v := range parent.Taskfile.Env.All() {
|
|
|
|
|
if err := resolveEnvToMap(k, v, parentDir); err != nil {
|
2025-12-26 21:08:37 +01:00
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-01-14 19:34:53 +01:00
|
|
|
// Vars use the parent's directory too
|
|
|
|
|
parentRangeFunc := getRangeFunc(parentDir)
|
|
|
|
|
for k, v := range parent.Taskfile.Vars.All() {
|
|
|
|
|
if err := parentRangeFunc(k, v); err != nil {
|
2025-12-26 21:08:37 +01:00
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-14 19:34:53 +01:00
|
|
|
includeVertex, err := c.Graph.Vertex(t.Location.Taskfile)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
2025-12-26 21:08:37 +01:00
|
|
|
}
|
2026-01-14 19:34:53 +01:00
|
|
|
includeDir := filepath.Dir(includeVertex.URI)
|
|
|
|
|
for k, v := range includeVertex.Taskfile.Env.All() {
|
|
|
|
|
if err := resolveEnvToMap(k, v, includeDir); err != nil {
|
|
|
|
|
return nil, err
|
2025-12-29 16:45:41 +01:00
|
|
|
}
|
2026-01-14 19:34:53 +01:00
|
|
|
}
|
|
|
|
|
includeRangeFunc := getRangeFunc(includeDir)
|
|
|
|
|
for k, v := range includeVertex.Taskfile.Vars.All() {
|
|
|
|
|
if err := includeRangeFunc(k, v); err != nil {
|
|
|
|
|
return nil, err
|
2025-12-29 17:07:48 +01:00
|
|
|
}
|
2025-12-29 16:45:41 +01:00
|
|
|
}
|
2026-01-14 19:34:53 +01:00
|
|
|
}
|
2025-12-29 16:45:41 +01:00
|
|
|
|
2026-01-14 19:34:53 +01:00
|
|
|
if t.IncludeVars != nil {
|
|
|
|
|
for k, v := range t.IncludeVars.All() {
|
2025-12-26 21:08:37 +01:00
|
|
|
if err := rangeFunc(k, v); err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-01-14 19:34:53 +01:00
|
|
|
}
|
2025-12-29 16:45:41 +01:00
|
|
|
|
2026-01-14 19:34:53 +01:00
|
|
|
if call != nil {
|
|
|
|
|
for k, v := range t.Vars.All() {
|
|
|
|
|
if err := taskRangeFunc(k, v); err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for k, v := range call.Vars.All() {
|
|
|
|
|
if err := taskRangeFunc(k, v); err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-12-29 16:45:41 +01:00
|
|
|
|
2026-01-14 19:34:53 +01:00
|
|
|
for k, v := range c.CLIVars.All() {
|
|
|
|
|
if err := rangeFunc(k, v); err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2025-12-29 16:45:41 +01:00
|
|
|
}
|
|
|
|
|
|
2026-01-14 19:34:53 +01:00
|
|
|
result.Set("env", ast.Var{Value: envMap})
|
|
|
|
|
|
|
|
|
|
return result, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// getLegacyVariables resolves variables in legacy mode.
|
|
|
|
|
// In legacy mode, all variables (including OS env) are merged at root level.
|
|
|
|
|
func (c *Compiler) getLegacyVariables(t *ast.Task, call *Call, evaluateShVars bool) (*ast.Vars, error) {
|
|
|
|
|
result := env.GetEnviron()
|
|
|
|
|
|
|
|
|
|
specialVars, err := c.getSpecialVars(t, call)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
for k, v := range specialVars {
|
|
|
|
|
result.Set(k, ast.Var{Value: v})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
getRangeFunc := func(dir string) func(k string, v ast.Var) error {
|
|
|
|
|
return func(k string, v ast.Var) error {
|
|
|
|
|
cache := &templater.Cache{Vars: result}
|
|
|
|
|
newVar := templater.ReplaceVar(v, cache)
|
|
|
|
|
if !evaluateShVars && newVar.Value == nil {
|
|
|
|
|
result.Set(k, ast.Var{Value: "", Sh: newVar.Sh})
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
if !evaluateShVars {
|
|
|
|
|
result.Set(k, ast.Var{Value: newVar.Value, Sh: newVar.Sh})
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
if err := cache.Err(); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if newVar.Value != nil || newVar.Sh == nil {
|
|
|
|
|
result.Set(k, ast.Var{Value: newVar.Value})
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
static, err := c.HandleDynamicVar(newVar, dir, env.GetFromVars(result))
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
result.Set(k, ast.Var{Value: static})
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
rangeFunc := getRangeFunc(c.Dir)
|
|
|
|
|
|
|
|
|
|
var taskRangeFunc func(k string, v ast.Var) error
|
|
|
|
|
if t != nil {
|
|
|
|
|
cache := &templater.Cache{Vars: result}
|
|
|
|
|
dir := templater.Replace(t.Dir, cache)
|
|
|
|
|
if err := cache.Err(); err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
dir = filepathext.SmartJoin(c.Dir, dir)
|
|
|
|
|
taskRangeFunc = getRangeFunc(dir)
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-29 16:45:41 +01:00
|
|
|
for k, v := range c.TaskfileEnv.All() {
|
|
|
|
|
if err := rangeFunc(k, v); err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for k, v := range c.TaskfileVars.All() {
|
|
|
|
|
if err := rangeFunc(k, v); err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-01-14 19:34:53 +01:00
|
|
|
|
2025-12-29 16:45:41 +01:00
|
|
|
if t != nil {
|
|
|
|
|
for k, v := range t.IncludeVars.All() {
|
2025-12-26 21:08:37 +01:00
|
|
|
if err := rangeFunc(k, v); err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-12-29 16:45:41 +01:00
|
|
|
for k, v := range t.IncludedTaskfileVars.All() {
|
|
|
|
|
if err := taskRangeFunc(k, v); err != nil {
|
|
|
|
|
return nil, err
|
2025-12-26 21:08:37 +01:00
|
|
|
}
|
2023-12-29 20:26:02 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if t == nil || call == nil {
|
|
|
|
|
return result, nil
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-22 16:22:03 +00:00
|
|
|
for k, v := range call.Vars.All() {
|
|
|
|
|
if err := rangeFunc(k, v); err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2023-12-29 20:26:02 +00:00
|
|
|
}
|
2025-02-22 16:22:03 +00:00
|
|
|
for k, v := range t.Vars.All() {
|
|
|
|
|
if err := taskRangeFunc(k, v); err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2023-12-29 20:26:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result, nil
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-02 21:07:25 +01:00
|
|
|
func (c *Compiler) HandleDynamicVar(v ast.Var, dir string, e []string) (string, error) {
|
2023-12-29 20:26:02 +00:00
|
|
|
c.muDynamicCache.Lock()
|
|
|
|
|
defer c.muDynamicCache.Unlock()
|
|
|
|
|
|
2024-11-04 13:30:39 +00:00
|
|
|
if v.Sh == nil || *v.Sh == "" {
|
|
|
|
|
return "", nil
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-29 20:26:02 +00:00
|
|
|
if c.dynamicCache == nil {
|
|
|
|
|
c.dynamicCache = make(map[string]string, 30)
|
|
|
|
|
}
|
2024-11-04 13:30:39 +00:00
|
|
|
if result, ok := c.dynamicCache[*v.Sh]; ok {
|
2023-12-29 20:26:02 +00:00
|
|
|
return result, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// NOTE(@andreynering): If a var have a specific dir, use this instead
|
|
|
|
|
if v.Dir != "" {
|
|
|
|
|
dir = v.Dir
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var stdout bytes.Buffer
|
|
|
|
|
opts := &execext.RunCommandOptions{
|
2024-11-04 13:30:39 +00:00
|
|
|
Command: *v.Sh,
|
2023-12-29 20:26:02 +00:00
|
|
|
Dir: dir,
|
|
|
|
|
Stdout: &stdout,
|
|
|
|
|
Stderr: c.Logger.Stderr,
|
2025-01-02 21:07:25 +01:00
|
|
|
Env: e,
|
2023-12-29 20:26:02 +00:00
|
|
|
}
|
|
|
|
|
if err := execext.RunCommand(context.Background(), opts); err != nil {
|
|
|
|
|
return "", fmt.Errorf(`task: Command "%s" failed: %s`, opts.Command, err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Trim a single trailing newline from the result to make most command
|
|
|
|
|
// output easier to use in shell commands.
|
|
|
|
|
result := strings.TrimSuffix(stdout.String(), "\r\n")
|
|
|
|
|
result = strings.TrimSuffix(result, "\n")
|
|
|
|
|
|
2024-11-04 13:30:39 +00:00
|
|
|
c.dynamicCache[*v.Sh] = result
|
2024-12-07 16:05:53 +01:00
|
|
|
c.Logger.VerboseErrf(logger.Magenta, "task: dynamic variable: %q result: %q\n", *v.Sh, result)
|
2023-12-29 20:26:02 +00:00
|
|
|
|
|
|
|
|
return result, nil
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-07 18:25:59 +02:00
|
|
|
// ResetCache clear the dynamic variables cache
|
2023-12-29 20:26:02 +00:00
|
|
|
func (c *Compiler) ResetCache() {
|
|
|
|
|
c.muDynamicCache.Lock()
|
|
|
|
|
defer c.muDynamicCache.Unlock()
|
|
|
|
|
|
|
|
|
|
c.dynamicCache = nil
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-23 18:30:42 +00:00
|
|
|
func (c *Compiler) getSpecialVars(t *ast.Task, call *Call) (map[string]string, error) {
|
2024-10-09 03:14:23 -04:00
|
|
|
allVars := map[string]string{
|
2024-05-16 01:50:18 +01:00
|
|
|
"TASK_EXE": filepath.ToSlash(os.Args[0]),
|
2024-01-26 00:11:08 +00:00
|
|
|
"ROOT_TASKFILE": filepathext.SmartJoin(c.Dir, c.Entrypoint),
|
2023-12-29 20:26:02 +00:00
|
|
|
"ROOT_DIR": c.Dir,
|
|
|
|
|
"USER_WORKING_DIR": c.UserWorkingDir,
|
|
|
|
|
"TASK_VERSION": version.GetVersion(),
|
2024-10-09 03:14:23 -04:00
|
|
|
}
|
|
|
|
|
if t != nil {
|
2024-12-30 11:45:25 +01:00
|
|
|
allVars["TASK"] = t.Task
|
|
|
|
|
allVars["TASK_DIR"] = filepathext.SmartJoin(c.Dir, t.Dir)
|
|
|
|
|
allVars["TASKFILE"] = t.Location.Taskfile
|
|
|
|
|
allVars["TASKFILE_DIR"] = filepath.Dir(t.Location.Taskfile)
|
2025-03-10 11:46:07 +00:00
|
|
|
} else {
|
|
|
|
|
allVars["TASK"] = ""
|
|
|
|
|
allVars["TASK_DIR"] = ""
|
|
|
|
|
allVars["TASKFILE"] = ""
|
|
|
|
|
allVars["TASKFILE_DIR"] = ""
|
2024-10-09 03:14:23 -04:00
|
|
|
}
|
|
|
|
|
if call != nil {
|
2024-12-30 11:45:25 +01:00
|
|
|
allVars["ALIAS"] = call.Task
|
2025-03-10 11:46:07 +00:00
|
|
|
} else {
|
|
|
|
|
allVars["ALIAS"] = ""
|
2024-10-09 03:14:23 -04:00
|
|
|
}
|
2024-12-30 11:45:25 +01:00
|
|
|
|
2024-10-09 03:14:23 -04:00
|
|
|
return allVars, nil
|
2023-12-29 20:26:02 +00:00
|
|
|
}
|