mirror of
https://github.com/go-task/task.git
synced 2026-06-19 13:51:40 +00:00
Compare commits
2 Commits
v3.48.0
...
feat/comma
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9a96a47c99 | ||
|
|
93cdccefce |
@@ -1,9 +1,7 @@
|
||||
# Changelog
|
||||
|
||||
## v3.48.0 - 2026-01-26
|
||||
## Unreleased
|
||||
|
||||
- Fixed `if:` conditions when using to check dynamic variables. Also, skip
|
||||
variable prompt if task would be skipped by `if:` (#2658, #2660 by @vmaerten).
|
||||
- Fixed `ROOT_TASKFILE` variable pointing to directory instead of the actual
|
||||
Taskfile path when no explicit `-t` flag is provided (#2635, #1706 by
|
||||
@trulede).
|
||||
|
||||
@@ -1160,10 +1160,6 @@ func TestIf(t *testing.T) {
|
||||
|
||||
// For loop with if
|
||||
{name: "if-in-for-loop", task: "if-in-for-loop", verbose: true},
|
||||
|
||||
// Task-level if with dynamic variable
|
||||
{name: "task-if-dynamic-true", task: "task-if-dynamic-true"},
|
||||
{name: "task-if-dynamic-false", task: "task-if-dynamic-false", verbose: true},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
|
||||
@@ -1 +1 @@
|
||||
3.48.0
|
||||
3.47.0
|
||||
|
||||
37
task.go
37
task.go
@@ -148,20 +148,6 @@ func (e *Executor) RunTask(ctx context.Context, call *Call) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Check required vars early (before template compilation) if we can't prompt.
|
||||
// This gives a clear "missing required variables" error instead of a template error.
|
||||
if !e.canPrompt() {
|
||||
if err := e.areTaskRequiredVarsSet(t); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
t, err = e.CompiledTask(call)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Check if condition after CompiledTask so dynamic variables are resolved
|
||||
if strings.TrimSpace(t.If) != "" {
|
||||
if err := execext.RunCommand(ctx, &execext.RunCommandOptions{
|
||||
Command: t.If,
|
||||
@@ -173,7 +159,7 @@ func (e *Executor) RunTask(ctx context.Context, call *Call) error {
|
||||
}
|
||||
}
|
||||
|
||||
// Prompt for missing required vars after if check (avoid prompting if task won't run)
|
||||
// Prompt for missing required vars (just-in-time for sequential task calls)
|
||||
prompted, err := e.promptTaskVars(t, call)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -190,6 +176,11 @@ func (e *Executor) RunTask(ctx context.Context, call *Call) error {
|
||||
return err
|
||||
}
|
||||
|
||||
t, err = e.CompiledTask(call)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := e.areTaskRequiredVarsAllowedValuesSet(t); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -374,6 +365,22 @@ func (e *Executor) runCommand(ctx context.Context, t *ast.Task, call *Call, i in
|
||||
}
|
||||
}
|
||||
|
||||
// Handle ask attached to command (y/n confirmation)
|
||||
if cmd.Ask != "" && !e.Dry {
|
||||
if e.AssumeYes {
|
||||
e.Logger.VerboseOutf(logger.Yellow, "task: [%s] %s [assuming yes]\n", t.Name(), cmd.Ask)
|
||||
} else {
|
||||
if err := e.Logger.Prompt(logger.Yellow, cmd.Ask, "n", "y", "yes"); errors.Is(err, logger.ErrNoTerminal) {
|
||||
return &errors.TaskCancelledNoTerminalError{TaskName: call.Task}
|
||||
} else if errors.Is(err, logger.ErrPromptCancelled) {
|
||||
e.Logger.VerboseOutf(logger.Yellow, "task: [%s] ask declined - skipped\n", t.Name())
|
||||
return nil
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch {
|
||||
case cmd.Task != "":
|
||||
reacquire := e.releaseConcurrencyLimit()
|
||||
|
||||
@@ -20,6 +20,7 @@ type Cmd struct {
|
||||
IgnoreError bool
|
||||
Defer bool
|
||||
Platforms []*Platform
|
||||
Ask string
|
||||
}
|
||||
|
||||
func (c *Cmd) DeepCopy() *Cmd {
|
||||
@@ -38,6 +39,7 @@ func (c *Cmd) DeepCopy() *Cmd {
|
||||
IgnoreError: c.IgnoreError,
|
||||
Defer: c.Defer,
|
||||
Platforms: deepcopy.Slice(c.Platforms),
|
||||
Ask: c.Ask,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,6 +67,7 @@ func (c *Cmd) UnmarshalYAML(node *yaml.Node) error {
|
||||
IgnoreError bool `yaml:"ignore_error"`
|
||||
Defer *Defer
|
||||
Platforms []*Platform
|
||||
Ask string
|
||||
}
|
||||
if err := node.Decode(&cmdStruct); err != nil {
|
||||
return errors.NewTaskfileDecodeError(err, node)
|
||||
@@ -98,6 +101,7 @@ func (c *Cmd) UnmarshalYAML(node *yaml.Node) error {
|
||||
c.If = cmdStruct.If
|
||||
c.Silent = cmdStruct.Silent
|
||||
c.IgnoreError = cmdStruct.IgnoreError
|
||||
c.Ask = cmdStruct.Ask
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -111,6 +115,7 @@ func (c *Cmd) UnmarshalYAML(node *yaml.Node) error {
|
||||
c.Shopt = cmdStruct.Shopt
|
||||
c.IgnoreError = cmdStruct.IgnoreError
|
||||
c.Platforms = cmdStruct.Platforms
|
||||
c.Ask = cmdStruct.Ask
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
18
testdata/if/Taskfile.yml
vendored
18
testdata/if/Taskfile.yml
vendored
@@ -158,21 +158,3 @@ tasks:
|
||||
if: '{{ eq .ENV "dev" }}'
|
||||
cmds:
|
||||
- echo "should not appear"
|
||||
|
||||
# Task-level if with dynamic variable (condition met)
|
||||
task-if-dynamic-true:
|
||||
vars:
|
||||
ENABLE_FEATURE:
|
||||
sh: 'echo "true"'
|
||||
if: '{{ eq .ENABLE_FEATURE "true" }}'
|
||||
cmds:
|
||||
- echo "dynamic feature enabled"
|
||||
|
||||
# Task-level if with dynamic variable (condition not met)
|
||||
task-if-dynamic-false:
|
||||
vars:
|
||||
ENABLE_FEATURE:
|
||||
sh: 'echo "false"'
|
||||
if: '{{ eq .ENABLE_FEATURE "true" }}'
|
||||
cmds:
|
||||
- echo "should not appear"
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
task: dynamic variable: "echo \"false\"" result: "false"
|
||||
task: if condition not met - skipped: "task-if-dynamic-false"
|
||||
@@ -1 +0,0 @@
|
||||
dynamic feature enabled
|
||||
@@ -7,20 +7,6 @@ outline: deep
|
||||
|
||||
::: v-pre
|
||||
|
||||
## v3.48.0 - 2026-01-26
|
||||
|
||||
- Fixed `if:` conditions when using to check dynamic variables. Also, skip
|
||||
variable prompt if task would be skipped by `if:` (#2658, #2660 by @vmaerten).
|
||||
- Fixed `ROOT_TASKFILE` variable pointing to directory instead of the actual
|
||||
Taskfile path when no explicit `-t` flag is provided (#2635, #1706 by
|
||||
@trulede).
|
||||
- Included Taskfiles with `silent: true` now properly propagate silence to their
|
||||
tasks, while still allowing individual tasks to override with `silent: false`
|
||||
(#2640, #1319 by @trulede).
|
||||
- Added TLS certificate options for Remote Taskfiles: use `--cacert` for
|
||||
self-signed certificates and `--cert`/`--cert-key` for mTLS authentication
|
||||
(#2537, #2242 by @vmaerten).
|
||||
|
||||
## v3.47.0 - 2026-01-24
|
||||
|
||||
- Fixed remote git Taskfiles: cloning now works without explicit ref, and
|
||||
|
||||
@@ -741,6 +741,8 @@ tasks:
|
||||
platforms: [linux, darwin]
|
||||
set: [errexit]
|
||||
shopt: [globstar]
|
||||
if: '[ "$CI" = "true" ]'
|
||||
ask: "Run this command?"
|
||||
```
|
||||
|
||||
### Task References
|
||||
@@ -857,6 +859,36 @@ tasks:
|
||||
if: '[ "{{.ITEM}}" != "b" ]'
|
||||
```
|
||||
|
||||
### Command Confirmations
|
||||
|
||||
Use `ask` to request user confirmation before executing a command. If the
|
||||
user declines (answers "n" or "no"), the command is skipped but the task
|
||||
continues.
|
||||
|
||||
```yaml
|
||||
tasks:
|
||||
deploy:
|
||||
cmds:
|
||||
- cmd: echo "Deploying to production..."
|
||||
ask: "Deploy to production?"
|
||||
- cmd: echo "Updating database..."
|
||||
ask: "Run database migrations?"
|
||||
- echo "Done!" # No ask, always runs
|
||||
```
|
||||
|
||||
| Flag | Behavior |
|
||||
|------|----------|
|
||||
| (none) | Asks user for y/n confirmation |
|
||||
| `--yes` | Auto-confirms all asks |
|
||||
| `--dry` | Shows commands without asking |
|
||||
|
||||
:::note
|
||||
|
||||
This is different from the task-level `prompt:` which cancels the entire task
|
||||
if declined. Command-level `ask:` only skips the individual command.
|
||||
|
||||
:::
|
||||
|
||||
## Shell Options
|
||||
|
||||
### Set Options
|
||||
|
||||
@@ -340,6 +340,10 @@
|
||||
"if": {
|
||||
"description": "A shell command to evaluate. If the exit code is non-zero, the command is skipped.",
|
||||
"type": "string"
|
||||
},
|
||||
"ask": {
|
||||
"description": "A y/n confirmation shown before executing this task call. If declined, the task call is skipped.",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
@@ -381,6 +385,10 @@
|
||||
"if": {
|
||||
"description": "A shell command to evaluate. If the exit code is non-zero, the command is skipped.",
|
||||
"type": "string"
|
||||
},
|
||||
"ask": {
|
||||
"description": "A y/n confirmation shown before executing this command. If declined, the command is skipped.",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
|
||||
Reference in New Issue
Block a user