diff --git a/cmd/task/task.go b/cmd/task/task.go index b81e23dd..9274b247 100644 --- a/cmd/task/task.go +++ b/cmd/task/task.go @@ -174,6 +174,8 @@ func run() error { // Merge CLI variables first (e.g. FOO=bar) so they take priority over Taskfile defaults e.Taskfile.Vars.Merge(globals, nil) + // Store CLI vars for scoped mode where they need highest priority + e.Compiler.CLIVars = globals // Then ReverseMerge special variables so they're available for templating cliArgsPostDashQuoted, err := args.ToQuotedString(cliArgsPostDash) diff --git a/compiler.go b/compiler.go index f6a97814..1e24b3b3 100644 --- a/compiler.go +++ b/compiler.go @@ -26,6 +26,7 @@ type Compiler struct { TaskfileEnv *ast.Vars TaskfileVars *ast.Vars + CLIVars *ast.Vars // CLI vars passed via command line (e.g., task foo VAR=value) Graph *ast.TaskfileGraph Logger *logger.Logger @@ -210,38 +211,58 @@ func (c *Compiler) getVariables(t *ast.Task, call *Call, evaluateShVars bool) (* } } - // Inject env namespace into result - result.Set("env", ast.Var{Value: envMap}) - } else { - // Legacy behavior: use merged vars - 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 - } - } - if t != nil { - for k, v := range t.IncludeVars.All() { - if err := rangeFunc(k, v); err != nil { - return nil, err - } - } - for k, v := range t.IncludedTaskfileVars.All() { + // Apply task-level vars + if call != nil { + for k, v := range t.Vars.All() { if err := taskRangeFunc(k, v); err != nil { return nil, err } } } + + // CLI vars have highest priority - applied last to override everything + for k, v := range c.CLIVars.All() { + if err := rangeFunc(k, v); err != nil { + return nil, err + } + } + + // Inject env namespace into result + result.Set("env", ast.Var{Value: envMap}) + + return result, nil + } + + // === LEGACY MODE === + // Legacy behavior: use merged vars + 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 + } + } + if t != nil { + for k, v := range t.IncludeVars.All() { + if err := rangeFunc(k, v); err != nil { + return nil, err + } + } + for k, v := range t.IncludedTaskfileVars.All() { + if err := taskRangeFunc(k, v); err != nil { + return nil, err + } + } } if t == nil || call == nil { return result, nil } + // Legacy order: CLI vars, then task vars (task vars override CLI) for k, v := range call.Vars.All() { if err := rangeFunc(k, v); err != nil { return nil, err diff --git a/testdata/scoped_taskfiles/Taskfile.yml b/testdata/scoped_taskfiles/Taskfile.yml index 739cd481..77185977 100644 --- a/testdata/scoped_taskfiles/Taskfile.yml +++ b/testdata/scoped_taskfiles/Taskfile.yml @@ -36,3 +36,9 @@ tasks: # In scoped mode, {{.ROOT_ENV}} should be empty (env not at root) # In legacy mode, {{.ROOT_ENV}} would have the value - echo "ROOT_ENV_AT_ROOT={{.ROOT_ENV}}" + + prout: + vars: + LOL: prout_from_root + cmds: + - echo "{{.LOL}}"