Describe the feature
This is a proposal to simplify the execution of multiple commands, where an error should stop this chain. Basically the behaviour of make when executing multiple commands for a single target.
What problem does this feature address?
AFAIK this is the way to execute multiple commands in a "build" target:
I think it's a good idea to make running shell commands as direct as possible.
Additional context
This is the way I implemented it locally. It does add the dependency on golang.org/x/sync/errgroup, so the code cannot be used as-is. I think it's relatively simple to restructure the code to avoid this dependency. Before doing that, I'd rather discuss the overall idea first, though.
//go:build mage
package main
import (
"context"
"github.com/magefile/mage/sh"
"golang.org/x/sync/errgroup"
)
// Runner allows running a group of commands sequentially, stopping at the first
// failure.
type Runner struct {
group *errgroup.Group
ctx context.Context
}
// NewRunner constructs a new runner that's bound to the given context. If the
// context is done, no new command will be executed. It does NOT abort an
// already-running command.
func NewRunner(ctx context.Context) *Runner {
group, groupctx := errgroup.WithContext(ctx)
group.SetLimit(1)
return &Runner{
group: group,
ctx: groupctx,
}
}
// Run the given command.
// This only runs a command if no previous command has failed yet.
func (r *Runner) Run(cmd string, args ...string) {
r.group.Go(func() error {
if err := r.ctx.Err(); err != nil {
return err
}
return sh.Run(cmd, args...)
})
}
// Wait for the commands to finish running, and return any error.
func (r *Runner) Wait() error {
return r.group.Wait()
}
Describe the feature This is a proposal to simplify the execution of multiple commands, where an error should stop this chain. Basically the behaviour of
make
when executing multiple commands for a single target.What problem does this feature address? AFAIK this is the way to execute multiple commands in a "build" target:
This means that for every command to execute there are typically three lines of code necessary. My proposal is to introduce something like this:
I think it's a good idea to make running shell commands as direct as possible.
Additional context This is the way I implemented it locally. It does add the dependency on
golang.org/x/sync/errgroup
, so the code cannot be used as-is. I think it's relatively simple to restructure the code to avoid this dependency. Before doing that, I'd rather discuss the overall idea first, though.