Skip to content

Commit 69e9eff

Browse files
committed
initial pass at deferred commands
1 parent 1c782c5 commit 69e9eff

File tree

6 files changed

+78
-0
lines changed

6 files changed

+78
-0
lines changed

task.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"os"
99
"sync"
1010
"sync/atomic"
11+
"time"
1112

1213
"github.com/go-task/task/v3/internal/compiler"
1314
compilerv2 "github.com/go-task/task/v3/internal/compiler/v2"
@@ -339,6 +340,11 @@ func (e *Executor) RunTask(ctx context.Context, call taskfile.Call) error {
339340
}
340341

341342
for i := range t.Cmds {
343+
if t.Cmds[i].Defer {
344+
defer e.runDeferred(t, call, i)
345+
continue
346+
}
347+
342348
if err := e.runCommand(ctx, t, call, i); err != nil {
343349
if err2 := e.statusOnError(t); err2 != nil {
344350
e.Logger.VerboseErrf(logger.Yellow, "task: error cleaning status on error: %v", err2)
@@ -395,6 +401,14 @@ func (e *Executor) runDeps(ctx context.Context, t *taskfile.Task) error {
395401
return g.Wait()
396402
}
397403

404+
func (e *Executor) runDeferred(t *taskfile.Task, call taskfile.Call, i int) {
405+
ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
406+
defer cancel()
407+
if err := e.runCommand(ctx, t, call, i); err != nil {
408+
e.Logger.VerboseErrf(logger.Yellow, `task: ignored error in deferred cmd: %s`, err.Error())
409+
}
410+
}
411+
398412
func (e *Executor) runCommand(ctx context.Context, t *taskfile.Task, call taskfile.Call, i int) error {
399413
cmd := t.Cmds[i]
400414

task_test.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1044,6 +1044,32 @@ func TestRunOnlyRunsJobsHashOnce(t *testing.T) {
10441044
tt.Run(t)
10451045
}
10461046

1047+
func TestDeferredCmds(t *testing.T) {
1048+
const dir = "testdata/deferred"
1049+
var buff bytes.Buffer
1050+
e := task.Executor{
1051+
Dir: dir,
1052+
Stdout: &buff,
1053+
Stderr: &buff,
1054+
}
1055+
assert.NoError(t, e.Setup())
1056+
1057+
expectedOutputOrder := strings.TrimSpace(`
1058+
task: [task-2] echo 'cmd ran'
1059+
cmd ran
1060+
task: [task-2] exit 1
1061+
task: [task-2] echo 'failing' && exit 2
1062+
failing
1063+
task: [task-2] echo 'echo ran'
1064+
echo ran
1065+
task: [task-1] echo 'task-1 ran'
1066+
task-1 ran
1067+
`)
1068+
assert.Error(t, e.Run(context.Background(), taskfile.Call{Task: "task-2"}))
1069+
fmt.Println(buff.String())
1070+
assert.Contains(t, buff.String(), expectedOutputOrder)
1071+
}
1072+
10471073
func TestIgnoreNilElements(t *testing.T) {
10481074
tests := []struct {
10491075
name string

taskfile/cmd.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ type Cmd struct {
77
Task string
88
Vars *Vars
99
IgnoreError bool
10+
Defer bool
1011
}
1112

1213
// Dep is a task dependency
@@ -33,6 +34,18 @@ func (c *Cmd) UnmarshalYAML(unmarshal func(interface{}) error) error {
3334
c.IgnoreError = cmdStruct.IgnoreError
3435
return nil
3536
}
37+
var deferredCmd struct {
38+
Defer string
39+
}
40+
if err := unmarshal(&deferredCmd); err == nil && deferredCmd.Defer != "" {
41+
c.Defer = true
42+
if strings.HasPrefix(deferredCmd.Defer, "^") {
43+
c.Task = strings.TrimPrefix(deferredCmd.Defer, "^")
44+
} else {
45+
c.Cmd = deferredCmd.Defer
46+
}
47+
return nil
48+
}
3649
var taskCall struct {
3750
Task string
3851
Vars *Vars

taskfile/taskfile_test.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ vars:
1919
PARAM1: VALUE1
2020
PARAM2: VALUE2
2121
`
22+
yamlDeferredTask = `defer: ^some_task`
23+
yamlDeferredCmd = `defer: echo 'test'`
2224
)
2325
tests := []struct {
2426
content string
@@ -41,6 +43,16 @@ vars:
4143
},
4244
}},
4345
},
46+
{
47+
yamlDeferredCmd,
48+
&taskfile.Cmd{},
49+
&taskfile.Cmd{Cmd: "echo 'test'", Defer: true},
50+
},
51+
{
52+
yamlDeferredTask,
53+
&taskfile.Cmd{},
54+
&taskfile.Cmd{Task: "some_task", Defer: true},
55+
},
4456
{
4557
yamlDep,
4658
&taskfile.Dep{},

testdata/deferred/Taskfile.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
version: "3"
2+
3+
tasks:
4+
task-1:
5+
- echo 'task-1 ran'
6+
7+
task-2:
8+
- defer: "^task-1"
9+
- defer: echo 'echo ran'
10+
- defer: echo 'failing' && exit 2
11+
- echo 'cmd ran'
12+
- exit 1

variables.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ func (e *Executor) compiledTask(call taskfile.Call, evaluateShVars bool) (*taskf
102102
Cmd: r.Replace(cmd.Cmd),
103103
Vars: r.ReplaceVars(cmd.Vars),
104104
IgnoreError: cmd.IgnoreError,
105+
Defer: cmd.Defer,
105106
})
106107
}
107108
}

0 commit comments

Comments
 (0)