Skip to content
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

Command pre/post/task_separator hooks #1327

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 setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,7 @@ func (e *Executor) setupDefaults() {
func (e *Executor) setupConcurrencyState() {
e.executionHashes = make(map[string]context.Context)

e.taskSeparated = new(int32)
e.taskCallCount = make(map[string]*int32, e.Taskfile.Tasks.Len())
e.mkdirMutexMap = make(map[string]*sync.Mutex, e.Taskfile.Tasks.Len())
for _, k := range e.Taskfile.Tasks.Keys() {
Expand Down
57 changes: 55 additions & 2 deletions task.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ type Executor struct {

concurrencySemaphore chan struct{}
taskCallCount map[string]*int32
taskSeparated *int32
mkdirMutexMap map[string]*sync.Mutex
executionHashes map[string]context.Context
executionHashesMutex sync.Mutex
Expand Down Expand Up @@ -224,6 +225,9 @@ func (e *Executor) RunTask(ctx context.Context, call taskfile.Call) error {
e.Logger.Errf(logger.Red, "task: cannot make directory %q: %v\n", t.Dir, err)
}

if atomic.CompareAndSwapInt32(e.taskSeparated, 0, 1) {
e.runCommandHook(ctx, t, call, e.Taskfile.Cmds.TaskSeparator, "separator")
}
for i := range t.Cmds {
if t.Cmds[i].Defer {
defer e.runDeferred(t, call, i)
Expand All @@ -247,6 +251,9 @@ func (e *Executor) RunTask(ctx context.Context, call taskfile.Call) error {
return &errors.TaskRunError{TaskName: t.Task, Err: err}
}
}
if atomic.CompareAndSwapInt32(e.taskSeparated, 0, 1) {
e.runCommandHook(ctx, t, call, e.Taskfile.Cmds.TaskSeparator, "separator")
}
e.Logger.VerboseErrf(logger.Magenta, "task: %q finished\n", call.Task)
return nil
})
Expand Down Expand Up @@ -334,8 +341,10 @@ func (e *Executor) runCommand(ctx context.Context, t *taskfile.Task, call taskfi
if err != nil {
return fmt.Errorf("task: failed to get variables: %w", err)
}
stdOut, stdErr, close := outputWrapper.WrapWriter(e.Stdout, e.Stderr, t.Prefix, outputTemplater)
stdOut, stdErr, closer := outputWrapper.WrapWriter(e.Stdout, e.Stderr, t.Prefix, outputTemplater)

e.runCommandHook(ctx, t, call, e.Taskfile.Cmds.Pre, "pre")
atomic.StoreInt32(e.taskSeparated, 0)
err = execext.RunCommand(ctx, &execext.RunCommandOptions{
Command: cmd.Cmd,
Dir: t.Dir,
Expand All @@ -346,7 +355,9 @@ func (e *Executor) runCommand(ctx context.Context, t *taskfile.Task, call taskfi
Stdout: stdOut,
Stderr: stdErr,
})
if closeErr := close(err); closeErr != nil {
e.runCommandHook(ctx, t, call, e.Taskfile.Cmds.Post, "post")

if closeErr := closer(err); closeErr != nil {
e.Logger.Errf(logger.Red, "task: unable to close writer: %v\n", closeErr)
}
if execext.IsExitError(err) && cmd.IgnoreError {
Expand All @@ -359,6 +370,48 @@ func (e *Executor) runCommand(ctx context.Context, t *taskfile.Task, call taskfi
}
}

func (e *Executor) runCommandHook(ctx context.Context, t *taskfile.Task, call taskfile.Call, cmd string, kind string) {
if cmd == "" {
return
}

if e.Dry {
return
}

outputWrapper := e.Output
if t.Interactive {
outputWrapper = output.Interleaved{}
}
vars, err := e.Compiler.FastGetVariables(t, call)
outputTemplater := &templater.Templater{Vars: vars, RemoveNoValue: true}
if err != nil {
e.Logger.Errf(logger.Red, "task: failed to get variables for %s-command: %w", kind, err)
return
}
stdOut, stdErr, closer := outputWrapper.WrapWriter(e.Stdout, e.Stderr, t.Prefix, outputTemplater)

if err := execext.RunCommand(ctx, &execext.RunCommandOptions{
Command: cmd,
Dir: t.Dir,
Env: env.Get(t),
PosixOpts: slicesext.UniqueJoin(e.Taskfile.Set, t.Set),
BashOpts: slicesext.UniqueJoin(e.Taskfile.Shopt, t.Shopt),
Stdout: stdOut,
Stderr: stdErr,
}); err != nil {
e.Logger.Errf(logger.Red, "task: unable to run %s-command: %w", kind, err)
}

if closeErr := closer(err); closeErr != nil {
e.Logger.Errf(logger.Red, "task: unable to close writer: %v\n", closeErr)
}
if execext.IsExitError(err) {
e.Logger.VerboseErrf(logger.Yellow, "task: [%s] %s-command error ignored: %v\n", t.Name(), kind, err)
return
}
}

func (e *Executor) startExecution(ctx context.Context, t *taskfile.Task, execute func(ctx context.Context) error) error {
h, err := e.GetHash(t)
if err != nil {
Expand Down
7 changes: 7 additions & 0 deletions taskfile/cmds.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package taskfile

type Cmds struct {
Pre string
Post string
TaskSeparator string `yaml:"task_separator"`
}
3 changes: 3 additions & 0 deletions taskfile/taskfile.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ type Taskfile struct {
Shopt []string
Vars *Vars
Env *Vars
Cmds Cmds
Tasks Tasks
Silent bool
Dotenv []string
Expand All @@ -47,6 +48,7 @@ func (tf *Taskfile) UnmarshalYAML(node *yaml.Node) error {
Shopt []string
Vars *Vars
Env *Vars
Cmds Cmds
Tasks Tasks
Silent bool
Dotenv []string
Expand All @@ -66,6 +68,7 @@ func (tf *Taskfile) UnmarshalYAML(node *yaml.Node) error {
tf.Vars = taskfile.Vars
tf.Env = taskfile.Env
tf.Tasks = taskfile.Tasks
tf.Cmds = taskfile.Cmds
tf.Silent = taskfile.Silent
tf.Dotenv = taskfile.Dotenv
tf.Run = taskfile.Run
Expand Down