diff --git a/compiler.go b/compiler.go index 348a072898..6ecea4cdc6 100644 --- a/compiler.go +++ b/compiler.go @@ -33,18 +33,18 @@ type Compiler struct { } func (c *Compiler) GetTaskfileVariables() (*ast.Vars, error) { - return c.getVariables(nil, nil, true) + return c.getVariables(nil, nil, true, map[string]string{}) } func (c *Compiler) GetVariables(t *ast.Task, call *Call) (*ast.Vars, error) { - return c.getVariables(t, call, true) + return c.getVariables(t, call, true, map[string]string{}) } -func (c *Compiler) FastGetVariables(t *ast.Task, call *Call) (*ast.Vars, error) { - return c.getVariables(t, call, false) +func (c *Compiler) FastGetVariables(t *ast.Task, call *Call, overrideVars map[string]string) (*ast.Vars, error) { + return c.getVariables(t, call, false, overrideVars) } -func (c *Compiler) getVariables(t *ast.Task, call *Call, evaluateShVars bool) (*ast.Vars, error) { +func (c *Compiler) getVariables(t *ast.Task, call *Call, evaluateShVars bool, overrideVars map[string]string) (*ast.Vars, error) { result := env.GetEnviron() specialVars, err := c.getSpecialVars(t, call) if err != nil { @@ -56,6 +56,12 @@ func (c *Compiler) getVariables(t *ast.Task, call *Call, evaluateShVars bool) (* getRangeFunc := func(dir string) func(k string, v ast.Var) error { return func(k string, v ast.Var) error { + val, ok := overrideVars[k] + + if ok { + v = ast.Var{Value: val} + } + cache := &templater.Cache{Vars: result} // Replace values newVar := templater.ReplaceVar(v, cache) diff --git a/executor.go b/executor.go index 8f9233ef88..2164dbe6ab 100644 --- a/executor.go +++ b/executor.go @@ -38,6 +38,7 @@ type ( CacheExpiryDuration time.Duration Watch bool Verbose bool + Vars map[string]string Silent bool AssumeYes bool AssumeTerm bool // Used for testing @@ -274,14 +275,26 @@ func WithVerbose(verbose bool) ExecutorOption { return &verboseOption{verbose} } +func WithVars(vars map[string]string) ExecutorOption { + return &varsOption{vars} +} + type verboseOption struct { verbose bool } +type varsOption struct { + vars map[string]string +} + func (o *verboseOption) ApplyToExecutor(e *Executor) { e.Verbose = o.verbose } +func (o *varsOption) ApplyToExecutor(e *Executor) { + e.Vars = o.vars +} + // WithSilent tells the [Executor] to suppress all output except for the output // of the tasks that are run. func WithSilent(silent bool) ExecutorOption { diff --git a/executor_test.go b/executor_test.go index ddf735c98b..2f2aa67134 100644 --- a/executor_test.go +++ b/executor_test.go @@ -265,6 +265,17 @@ func TestVars(t *testing.T) { ) } +func TestVarOverride(t *testing.T) { + t.Parallel() + NewExecutorTest(t, + WithExecutorOptions( + task.WithDir("testdata/var_override"), + task.WithSilent(true), + task.WithVars(map[string]string{"MYVAR": "bar"}), + ), + ) +} + func TestRequires(t *testing.T) { t.Parallel() NewExecutorTest(t, diff --git a/internal/flags/flags.go b/internal/flags/flags.go index dab9fdf8f4..9478535aa1 100644 --- a/internal/flags/flags.go +++ b/internal/flags/flags.go @@ -56,6 +56,7 @@ var ( ForceAll bool Watch bool Verbose bool + Vars map[string]string Silent bool AssumeYes bool Dry bool @@ -121,6 +122,7 @@ func init() { pflag.BoolVar(&Insecure, "insecure", false, "Forces Task to download Taskfiles over insecure connections.") pflag.BoolVarP(&Watch, "watch", "w", false, "Enables watch of the given task.") pflag.BoolVarP(&Verbose, "verbose", "v", false, "Enables verbose mode.") + pflag.StringToStringVarP(&Vars, "var", "V", map[string]string{}, "Provide variable overrides") pflag.BoolVarP(&Silent, "silent", "s", false, "Disables echoing.") pflag.BoolVarP(&AssumeYes, "yes", "y", false, "Assume \"yes\" as answer to all prompts.") pflag.BoolVarP(&Parallel, "parallel", "p", false, "Executes tasks provided on command line in parallel.") @@ -238,6 +240,7 @@ func (o *flagsOption) ApplyToExecutor(e *task.Executor) { task.WithCacheExpiryDuration(CacheExpiryDuration), task.WithWatch(Watch), task.WithVerbose(Verbose), + task.WithVars(Vars), task.WithSilent(Silent), task.WithAssumeYes(AssumeYes), task.WithDry(Dry || Status), diff --git a/task.go b/task.go index fc3f17662d..3f356c17ad 100644 --- a/task.go +++ b/task.go @@ -336,7 +336,7 @@ func (e *Executor) runCommand(ctx context.Context, t *ast.Task, call *Call, i in if t.Interactive { outputWrapper = output.Interleaved{} } - vars, err := e.Compiler.FastGetVariables(t, call) + vars, err := e.Compiler.FastGetVariables(t, call, map[string]string{}) outputTemplater := &templater.Cache{Vars: vars} if err != nil { return fmt.Errorf("task: failed to get variables: %w", err) diff --git a/testdata/var_override/Taskfile.yml b/testdata/var_override/Taskfile.yml new file mode 100644 index 0000000000..855b67ce6e --- /dev/null +++ b/testdata/var_override/Taskfile.yml @@ -0,0 +1,9 @@ +version: '3' + +vars: + MYVAR: foo + +tasks: + default: + cmds: + - echo "{{.MYVAR}}" diff --git a/testdata/var_override/testdata/TestVarOverride.golden b/testdata/var_override/testdata/TestVarOverride.golden new file mode 100644 index 0000000000..5716ca5987 --- /dev/null +++ b/testdata/var_override/testdata/TestVarOverride.golden @@ -0,0 +1 @@ +bar diff --git a/variables.go b/variables.go index 261de59b7e..f49868ae5a 100644 --- a/variables.go +++ b/variables.go @@ -37,9 +37,9 @@ func (e *Executor) compiledTask(call *Call, evaluateShVars bool) (*ast.Task, err var vars *ast.Vars if evaluateShVars { - vars, err = e.Compiler.GetVariables(origTask, call) + vars, err = e.Compiler.getVariables(origTask, call, true, e.Vars) } else { - vars, err = e.Compiler.FastGetVariables(origTask, call) + vars, err = e.Compiler.FastGetVariables(origTask, call, e.Vars) } if err != nil { return nil, err diff --git a/website/src/docs/reference/cli.md b/website/src/docs/reference/cli.md index eb1c701919..e88568d221 100644 --- a/website/src/docs/reference/cli.md +++ b/website/src/docs/reference/cli.md @@ -95,6 +95,14 @@ Disable command echoing. task deploy --silent ``` +#### `-V, --var =` + +Provide variable overrides to override variables defined in the Taskfile. + +```bash +task build --var FOO=bar +``` + ### Execution Control #### `-f, --force`