1
0
mirror of https://github.com/goreleaser/goreleaser.git synced 2025-11-23 22:36:11 +02:00

feat: make goreleaser timeoutable

This commit is contained in:
Carlos Alexandro Becker
2017-12-29 17:07:06 -02:00
parent b61aec7a1c
commit a84148c620
11 changed files with 85 additions and 45 deletions

View File

@@ -10,6 +10,7 @@ import (
ctx "context"
"os"
"strings"
"time"
"github.com/goreleaser/goreleaser/config"
"github.com/goreleaser/goreleaser/internal/artifact"
@@ -41,8 +42,18 @@ type Context struct {
// New context
func New(config config.Project) *Context {
return wrap(ctx.Background(), config)
}
// NewWithTimeout new context with the given timeout
func NewWithTimeout(config config.Project, timeout time.Duration) (*Context, ctx.CancelFunc) {
ctx, cancel := ctx.WithTimeout(ctx.Background(), timeout)
return wrap(ctx, config), cancel
}
func wrap(ctx ctx.Context, config config.Project) *Context {
return &Context{
Context: ctx.Background(),
Context: ctx,
Config: config,
Env: splitEnv(os.Environ()),
Parallelism: 4,

View File

@@ -4,7 +4,10 @@ import (
"fmt"
"io/ioutil"
"os"
"os/signal"
"strings"
"syscall"
"time"
"github.com/apex/log"
"github.com/apex/log/handlers/cli"
@@ -65,6 +68,7 @@ type Flags interface {
String(s string) string
Int(s string) int
Bool(s string) bool
Duration(s string) time.Duration
}
// Release runs the release process with the given flags
@@ -84,7 +88,8 @@ func Release(flags Flags) error {
}
log.WithField("file", file).Warn("could not load config, using defaults")
}
var ctx = context.New(cfg)
ctx, cancel := context.NewWithTimeout(cfg, flags.Duration("timeout"))
defer cancel()
ctx.Parallelism = flags.Int("parallelism")
ctx.Debug = flags.Bool("debug")
log.Debugf("parallelism: %v", ctx.Parallelism)
@@ -105,16 +110,36 @@ func Release(flags Flags) error {
ctx.Publish = false
}
ctx.RmDist = flags.Bool("rm-dist")
for _, pipe := range pipes {
cli.Default.Padding = normalPadding
log.Infof("\033[1m%s\033[0m", strings.ToUpper(pipe.String()))
cli.Default.Padding = increasedPadding
if err := handle(pipe.Run(ctx)); err != nil {
return err
var errs = make(chan error)
go func() {
for _, pipe := range pipes {
restoreOutputPadding()
log.Infof("\033[1m%s\033[0m", strings.ToUpper(pipe.String()))
cli.Default.Padding = increasedPadding
if err := handle(pipe.Run(ctx)); err != nil {
errs <- err
return
}
}
errs <- nil
}()
defer restoreOutputPadding()
var signals = make(chan os.Signal)
signal.Notify(signals, os.Interrupt, syscall.SIGTERM)
select {
case err := <-errs:
return err
case <-ctx.Done():
return ctx.Err()
case sig := <-signals:
restoreOutputPadding()
log.Infof("stopping: %s", sig)
return nil
}
}
func restoreOutputPadding() {
cli.Default.Padding = normalPadding
return nil
}
func handle(err error) error {

View File

@@ -63,6 +63,11 @@ func main() {
Name: "debug",
Usage: "Enable debug mode",
},
cli.DurationFlag{
Name: "timeout",
Usage: "How much time the entire release process is allowed to take",
Value: 30 * time.Minute,
},
}
app.Action = func(c *cli.Context) error {
start := time.Now()

View File

@@ -74,7 +74,7 @@ func buildWithDefaults(ctx *context.Context, build config.Build) config.Build {
}
func runPipeOnBuild(ctx *context.Context, build config.Build) error {
if err := runHook(build.Env, build.Hooks.Pre); err != nil {
if err := runHook(ctx, build.Env, build.Hooks.Pre); err != nil {
return errors.Wrap(err, "pre hook failed")
}
sem := make(chan bool, ctx.Parallelism)
@@ -93,16 +93,16 @@ func runPipeOnBuild(ctx *context.Context, build config.Build) error {
if err := g.Wait(); err != nil {
return err
}
return errors.Wrap(runHook(build.Env, build.Hooks.Post), "post hook failed")
return errors.Wrap(runHook(ctx, build.Env, build.Hooks.Post), "post hook failed")
}
func runHook(env []string, hook string) error {
func runHook(ctx *context.Context, env []string, hook string) error {
if hook == "" {
return nil
}
log.WithField("hook", hook).Info("running hook")
cmd := strings.Fields(hook)
return run(buildtarget.Runtime, cmd, env)
return run(ctx, buildtarget.Runtime, cmd, env)
}
func doBuild(ctx *context.Context, build config.Build, target buildtarget.Target) error {
@@ -119,7 +119,7 @@ func doBuild(ctx *context.Context, build config.Build, target buildtarget.Target
return err
}
cmd = append(cmd, "-ldflags="+flags, "-o", binary, build.Main)
if err := run(target, cmd, build.Env); err != nil {
if err := run(ctx, target, cmd, build.Env); err != nil {
return errors.Wrapf(err, "failed to build for %s", target)
}
ctx.Artifacts.Add(artifact.Artifact{
@@ -137,9 +137,9 @@ func doBuild(ctx *context.Context, build config.Build, target buildtarget.Target
return nil
}
func run(target buildtarget.Target, command, env []string) error {
func run(ctx *context.Context, target buildtarget.Target, command, env []string) error {
/* #nosec */
var cmd = exec.Command(command[0], command[1:]...)
var cmd = exec.CommandContext(ctx, command[0], command[1:]...)
env = append(env, target.Env()...)
var log = log.WithField("target", target.PrettyString()).
WithField("env", env).

View File

@@ -141,11 +141,11 @@ func process(ctx *context.Context, docker config.Docker, artifact artifact.Artif
return errors.Wrapf(err, "failed to link extra file '%s'", file)
}
}
if err := dockerBuild(root, dockerfile, image); err != nil {
if err := dockerBuild(ctx, root, dockerfile, image); err != nil {
return err
}
if docker.Latest {
if err := dockerTag(image, latest); err != nil {
if err := dockerTag(ctx, image, latest); err != nil {
return err
}
}
@@ -187,16 +187,16 @@ func publish(ctx *context.Context, docker config.Docker, image, latest string) e
if !docker.Latest {
return nil
}
if err := dockerTag(image, latest); err != nil {
if err := dockerTag(ctx, image, latest); err != nil {
return err
}
return dockerPush(ctx, docker, latest)
}
func dockerBuild(root, dockerfile, image string) error {
func dockerBuild(ctx *context.Context, root, dockerfile, image string) error {
log.WithField("image", image).Info("building docker image")
/* #nosec */
var cmd = exec.Command("docker", "build", "-f", dockerfile, "-t", image, root)
var cmd = exec.CommandContext(ctx, "docker", "build", "-f", dockerfile, "-t", image, root)
log.WithField("cmd", cmd).Debug("executing")
out, err := cmd.CombinedOutput()
if err != nil {
@@ -206,10 +206,10 @@ func dockerBuild(root, dockerfile, image string) error {
return nil
}
func dockerTag(image, tag string) error {
func dockerTag(ctx *context.Context, image, tag string) error {
log.WithField("image", image).WithField("tag", tag).Info("tagging docker image")
/* #nosec */
var cmd = exec.Command("docker", "tag", image, tag)
var cmd = exec.CommandContext(ctx, "docker", "tag", image, tag)
log.WithField("cmd", cmd).Debug("executing")
out, err := cmd.CombinedOutput()
if err != nil {
@@ -222,7 +222,7 @@ func dockerTag(image, tag string) error {
func dockerPush(ctx *context.Context, docker config.Docker, image string) error {
log.WithField("image", image).Info("pushing docker image")
/* #nosec */
var cmd = exec.Command("docker", "push", image)
var cmd = exec.CommandContext(ctx, "docker", "push", image)
log.WithField("cmd", cmd).Debug("executing")
out, err := cmd.CombinedOutput()
if err != nil {

View File

@@ -128,7 +128,7 @@ func create(ctx *context.Context, format, arch string, binaries []artifact.Artif
}
log.WithField("args", options).Debug("creating fpm package")
if out, err := cmd(options).CombinedOutput(); err != nil {
if out, err := cmd(ctx, options).CombinedOutput(); err != nil {
return errors.Wrap(err, string(out))
}
ctx.Artifacts.Add(artifact.Artifact{
@@ -142,9 +142,9 @@ func create(ctx *context.Context, format, arch string, binaries []artifact.Artif
return nil
}
func cmd(options []string) *exec.Cmd {
func cmd(ctx *context.Context, options []string) *exec.Cmd {
/* #nosec */
var cmd = exec.Command("fpm", options...)
var cmd = exec.CommandContext(ctx, "fpm", options...)
cmd.Env = []string{fmt.Sprintf("PATH=%s:%s", gnuTarPath, os.Getenv("PATH"))}
for _, env := range os.Environ() {
if strings.HasPrefix(env, "PATH=") {

View File

@@ -96,7 +96,7 @@ func TestInvalidNameTemplate(t *testing.T) {
Config: config.Project{
FPM: config.FPM{
NameTemplate: "{{.Foo}",
Formats: []string{"deb"},
Formats: []string{"deb"},
},
},
}
@@ -109,7 +109,6 @@ func TestInvalidNameTemplate(t *testing.T) {
assert.Contains(t, Pipe{}.Run(ctx).Error(), `template: {{.Foo}:1: unexpected "}" in operand`)
}
func TestCreateFileDoesntExist(t *testing.T) {
folder, err := ioutil.TempDir("", "archivetest")
assert.NoError(t, err)
@@ -141,7 +140,7 @@ func TestCreateFileDoesntExist(t *testing.T) {
}
func TestCmd(t *testing.T) {
cmd := cmd([]string{"--help"})
cmd := cmd(context.New(config.Project{}), []string{"--help"})
assert.NotEmpty(t, cmd.Env)
assert.Contains(t, cmd.Env[0], gnuTarPath)
}
@@ -161,7 +160,7 @@ func TestDefaultSet(t *testing.T) {
var ctx = &context.Context{
Config: config.Project{
FPM: config.FPM{
Bindir: "/bin",
Bindir: "/bin",
NameTemplate: "foo",
},
},

View File

@@ -25,7 +25,7 @@ Built with {{ .GoVersion }}`
func describeBody(ctx *context.Context) (bytes.Buffer, error) {
/* #nosec */
bts, err := exec.Command("go", "version").CombinedOutput()
bts, err := exec.CommandContext(ctx, "go", "version").CombinedOutput()
if err != nil {
return bytes.Buffer{}, err
}

View File

@@ -92,7 +92,7 @@ func signone(ctx *context.Context, artifact artifact.Artifact) (string, error) {
// However, this works as intended. The nosec annotation
// tells the scanner to ignore this.
// #nosec
cmd := exec.Command(cfg.Cmd, args...)
cmd := exec.CommandContext(ctx, cfg.Cmd, args...)
output, err := cmd.CombinedOutput()
if err != nil {
return "", fmt.Errorf("sign: %s failed with %q", cfg.Cmd, string(output))

View File

@@ -163,7 +163,7 @@ func create(ctx *context.Context, arch string, binaries []artifact.Artifact) err
var snap = filepath.Join(ctx.Config.Dist, folder+".snap")
/* #nosec */
var cmd = exec.Command("snapcraft", "snap", primeDir, "--output", snap)
var cmd = exec.CommandContext(ctx, "snapcraft", "snap", primeDir, "--output", snap)
if out, err = cmd.CombinedOutput(); err != nil {
return fmt.Errorf("failed to generate snap package: %s", string(out))
}

View File

@@ -54,8 +54,8 @@ func TestRunPipe(t *testing.T) {
Dist: dist,
Snapcraft: config.Snapcraft{
NameTemplate: "foo_{{.Arch}}",
Summary: "test summary",
Description: "test description",
Summary: "test summary",
Description: "test description",
},
},
}
@@ -77,8 +77,8 @@ func TestRunPipeInvalidNameTemplate(t *testing.T) {
Dist: dist,
Snapcraft: config.Snapcraft{
NameTemplate: "foo_{{.Arch}",
Summary: "test summary",
Description: "test description",
Summary: "test summary",
Description: "test description",
},
},
}
@@ -100,9 +100,9 @@ func TestRunPipeWithName(t *testing.T) {
Dist: dist,
Snapcraft: config.Snapcraft{
NameTemplate: "foo_{{.Arch}}",
Name: "testsnapname",
Summary: "test summary",
Description: "test description",
Name: "testsnapname",
Summary: "test summary",
Description: "test description",
},
},
}
@@ -130,8 +130,8 @@ func TestRunPipeWithPlugsAndDaemon(t *testing.T) {
Dist: dist,
Snapcraft: config.Snapcraft{
NameTemplate: "foo_{{.Arch}}",
Summary: "test summary",
Description: "test description",
Summary: "test summary",
Description: "test description",
Apps: map[string]config.SnapcraftAppMetadata{
"mybin": {
Plugs: []string{"home", "network"},
@@ -171,7 +171,7 @@ func TestNoSnapcraftInPath(t *testing.T) {
func TestDefault(t *testing.T) {
var ctx = context.New(config.Project{})
assert.NoError(t,Pipe{}.Default(ctx))
assert.NoError(t, Pipe{}.Default(ctx))
assert.Equal(t, defaultNameTemplate, ctx.Config.Snapcraft.NameTemplate)
}
@@ -181,7 +181,7 @@ func TestDefaultSet(t *testing.T) {
NameTemplate: "foo",
},
})
assert.NoError(t,Pipe{}.Default(ctx))
assert.NoError(t, Pipe{}.Default(ctx))
assert.Equal(t, "foo", ctx.Config.Snapcraft.NameTemplate)
}