Skip to content

Defer tasks until a later named point. #2346

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions call.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ type Call struct {
Vars *ast.Vars
Silent bool
Indirect bool // True if the task was called by another task
When string
}
7 changes: 7 additions & 0 deletions executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,17 @@ type (
executionHashes map[string]context.Context
executionHashesMutex sync.Mutex
watchedDirs *xsync.MapOf[string, bool]
whenTasks map[string][]WhenTaskCall
}
TempDir struct {
Remote string
Fingerprint string
}
WhenTaskCall struct {
Task *ast.Task
Call *Call
Index int
}
)

// NewExecutor creates a new [Executor] and applies the given functional options
Expand All @@ -98,6 +104,7 @@ func NewExecutor(opts ...ExecutorOption) *Executor {
mkdirMutexMap: map[string]*sync.Mutex{},
executionHashes: map[string]context.Context{},
executionHashesMutex: sync.Mutex{},
whenTasks: map[string][]WhenTaskCall{},
}
e.Options(opts...)
return e
Expand Down
19 changes: 17 additions & 2 deletions task.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,14 @@ func (e *Executor) Run(ctx context.Context, calls ...*Call) error {
return e.watchTasks(watchCalls...)
}

if whenList, ok := e.whenTasks["exit"]; ok {
for _, w := range whenList {
if err := e.runCommand(ctx, w.Task, w.Call, w.Index); err != nil {
e.Logger.Errf(logger.Red, "task: error running when[%v] task call", "exit", err)
}
}
delete(e.whenTasks, "exit")
}
return nil
}

Expand Down Expand Up @@ -211,7 +219,14 @@ func (e *Executor) RunTask(ctx context.Context, call *Call) error {

for i := range t.Cmds {
if t.Cmds[i].Defer {
defer e.runDeferred(t, call, i, &deferredExitCode)
if len(call.When) > 0 {
if _, ok := e.whenTasks[call.When]; !ok {
e.whenTasks[call.When] = []WhenTaskCall{}
}
e.whenTasks[call.When] = append(e.whenTasks[call.When], WhenTaskCall{Task: t, Call: call, Index: i})
} else {
defer e.runDeferred(t, call, i, &deferredExitCode)
}
continue
}

Expand Down Expand Up @@ -313,7 +328,7 @@ func (e *Executor) runCommand(ctx context.Context, t *ast.Task, call *Call, i in
reacquire := e.releaseConcurrencyLimit()
defer reacquire()

err := e.RunTask(ctx, &Call{Task: cmd.Task, Vars: cmd.Vars, Silent: cmd.Silent, Indirect: true})
err := e.RunTask(ctx, &Call{Task: cmd.Task, Vars: cmd.Vars, Silent: cmd.Silent, Indirect: true, When: cmd.When})
if err != nil {
return err
}
Expand Down
2 changes: 2 additions & 0 deletions taskfile/ast/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ type Cmd struct {
IgnoreError bool
Defer bool
Platforms []*Platform
When string
}

func (c *Cmd) DeepCopy() *Cmd {
Expand Down Expand Up @@ -73,6 +74,7 @@ func (c *Cmd) UnmarshalYAML(node *yaml.Node) error {
c.Defer = true
c.Cmd = cmdStruct.Defer.Cmd
c.Silent = cmdStruct.Silent
c.When = cmdStruct.Defer.When
return nil
}

Expand Down
3 changes: 3 additions & 0 deletions taskfile/ast/defer.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ type Defer struct {
Task string
Vars *Vars
Silent bool
When string
}

func (d *Defer) UnmarshalYAML(node *yaml.Node) error {
Expand All @@ -30,6 +31,7 @@ func (d *Defer) UnmarshalYAML(node *yaml.Node) error {
Task string
Vars *Vars
Silent bool
When string
}
if err := node.Decode(&deferStruct); err != nil {
return errors.NewTaskfileDecodeError(err, node)
Expand All @@ -38,6 +40,7 @@ func (d *Defer) UnmarshalYAML(node *yaml.Node) error {
d.Task = deferStruct.Task
d.Vars = deferStruct.Vars
d.Silent = deferStruct.Silent
d.When = deferStruct.When
return nil
}

Expand Down
4 changes: 3 additions & 1 deletion website/docs/reference/schema.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ tasks:
| `silent` | `bool` | `false` | Skips some output for this command. Note that STDOUT and STDERR of the commands will still be redirected. |
| `vars` | [`map[string]Variable`](#variable) | | Optional additional variables to be passed to the referenced task. Only relevant when setting `task` instead of `cmd`. |
| `ignore_error` | `bool` | `false` | Continue execution if errors happen while executing the command. |
| `defer` | [`Defer`](#defer) | | Alternative to `cmd`, but schedules the command or a task to be executed at the end of this task instead of immediately. This cannot be used together with `cmd`. |
| `defer` | [`Defer`](#defer) | | Alternative to `cmd`, but schedules the command or a task to be executed at the end of this task, or a named point (e.g. `exit`), instead of immediately. This cannot be used together with `cmd`. |
| `platforms` | `[]string` | All platforms | Specifies which platforms the command should be run on. [Valid GOOS and GOARCH values allowed](https://github.com/golang/go/blob/master/src/internal/syslist/syslist.go). Command will be skipped otherwise. |
| `set` | `[]string` | | Specify options for the [`set` builtin](https://www.gnu.org/software/bash/manual/html_node/The-Set-Builtin.html). |
| `shopt` | `[]string` | | Specify option for the [`shopt` builtin](https://www.gnu.org/software/bash/manual/html_node/The-Shopt-Builtin.html). |
Expand Down Expand Up @@ -184,13 +184,15 @@ tasks:
### Defer

The `defer` parameter defines a shell command to run, or a task to trigger, at the end of the current task instead of immediately.
A task may also be deffered to run at a later named point.
If defined as a string this is a shell command, otherwise it is a map defining a task to call:

| Attribute | Type | Default | Description |
| --------- | ---------------------------------- | ------- | ----------------------------------------------------------------- |
| `task` | `string` | | The deferred task to trigger. |
| `vars` | [`map[string]Variable`](#variable) | | Optional additional variables to be passed to the deferred task. |
| `silent` | `bool` | `false` | Hides task name and command from output. The command's output will still be redirected to `STDOUT` and `STDERR`. |
| `when` | `string` | | Defer the task to a later named point (e.g. `exit`). |

### For

Expand Down
15 changes: 15 additions & 0 deletions website/docs/usage.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -1804,6 +1804,21 @@ tasks:
cleanup: rm -rf tmpdir/
```

Additionally the cleanup task can be deferred until task exits:

```yaml
version: '3'

tasks:
default:
cmds:
- mkdir -p tmpdir/
- defer: { task: cleanup, when: exit }
- echo 'Do work on tmpdir/'

cleanup: rm -rf tmpdir/
```

:::info

Due to the nature of how the
Expand Down
8 changes: 8 additions & 0 deletions website/static/next-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,10 @@
"silent": {
"description": "Hides task name and command from output. The command's output will still be redirected to `STDOUT` and `STDERR`.",
"type": "boolean"
},
"when": {
"description": "Defer the task or command to a later named point (e.g. `exit`).",
"type": "string"
}
},
"additionalProperties": false,
Expand Down Expand Up @@ -384,6 +388,10 @@
"silent": {
"description": "Hides task name and command from output. The command's output will still be redirected to `STDOUT` and `STDERR`.",
"type": "boolean"
},
"when": {
"description": "Defer the task or command to a later named point (e.g. `exit`).",
"type": "string"
}
},
"additionalProperties": false,
Expand Down
Loading