mirror of
https://github.com/goreleaser/goreleaser.git
synced 2025-03-11 14:39:28 +02:00
feat: add hooks to universal binaries (#2684)
* add hooks for universal binaries * task fmt
This commit is contained in:
parent
611313571e
commit
f9b693edf0
@ -13,7 +13,6 @@ import (
|
||||
type Skipper interface {
|
||||
// Skip returns true if the Piper should be skipped.
|
||||
Skip(ctx *context.Context) bool
|
||||
|
||||
fmt.Stringer
|
||||
}
|
||||
|
||||
|
@ -24,7 +24,6 @@ import (
|
||||
// Announcer should be implemented by pipes that want to announce releases.
|
||||
type Announcer interface {
|
||||
fmt.Stringer
|
||||
|
||||
Announce(ctx *context.Context) error
|
||||
}
|
||||
|
||||
|
@ -3,20 +3,16 @@
|
||||
package build
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/apex/log"
|
||||
"github.com/caarlos0/go-shellwords"
|
||||
"github.com/goreleaser/goreleaser/internal/gio"
|
||||
"github.com/goreleaser/goreleaser/internal/ids"
|
||||
"github.com/goreleaser/goreleaser/internal/logext"
|
||||
"github.com/goreleaser/goreleaser/internal/semerrgroup"
|
||||
"github.com/goreleaser/goreleaser/internal/shell"
|
||||
"github.com/goreleaser/goreleaser/internal/tmpl"
|
||||
builders "github.com/goreleaser/goreleaser/pkg/build"
|
||||
"github.com/goreleaser/goreleaser/pkg/config"
|
||||
@ -151,7 +147,7 @@ func runHook(ctx *context.Context, opts builders.Options, buildEnv []string, hoo
|
||||
return err
|
||||
}
|
||||
|
||||
if err := run(ctx, dir, cmd, env); err != nil {
|
||||
if err := shell.Run(ctx, dir, cmd, env); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@ -230,26 +226,3 @@ func extFor(target string, flags config.FlagArray) string {
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func run(ctx *context.Context, dir string, command, env []string) error {
|
||||
fields := log.Fields{
|
||||
"cmd": command,
|
||||
"env": env,
|
||||
}
|
||||
/* #nosec */
|
||||
cmd := exec.CommandContext(ctx, command[0], command[1:]...)
|
||||
cmd.Env = env
|
||||
var b bytes.Buffer
|
||||
w := gio.Safe(&b)
|
||||
cmd.Stderr = io.MultiWriter(logext.NewWriter(fields, logext.Error), w)
|
||||
cmd.Stdout = io.MultiWriter(logext.NewWriter(fields, logext.Info), w)
|
||||
if dir != "" {
|
||||
cmd.Dir = dir
|
||||
}
|
||||
log.WithFields(fields).Debug("running")
|
||||
if err := cmd.Run(); err != nil {
|
||||
log.WithFields(fields).WithError(err).Debug("failed")
|
||||
return fmt.Errorf("%q: %w", b.String(), err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -9,10 +9,12 @@ import (
|
||||
"path/filepath"
|
||||
|
||||
"github.com/apex/log"
|
||||
"github.com/caarlos0/go-shellwords"
|
||||
"github.com/goreleaser/goreleaser/internal/artifact"
|
||||
"github.com/goreleaser/goreleaser/internal/ids"
|
||||
"github.com/goreleaser/goreleaser/internal/pipe"
|
||||
"github.com/goreleaser/goreleaser/internal/semerrgroup"
|
||||
"github.com/goreleaser/goreleaser/internal/shell"
|
||||
"github.com/goreleaser/goreleaser/internal/tmpl"
|
||||
"github.com/goreleaser/goreleaser/pkg/config"
|
||||
"github.com/goreleaser/goreleaser/pkg/context"
|
||||
@ -46,9 +48,15 @@ func (Pipe) Run(ctx *context.Context) error {
|
||||
for _, unibin := range ctx.Config.UniversalBinaries {
|
||||
unibin := unibin
|
||||
g.Go(func() error {
|
||||
if err := runHook(ctx, unibin.Hooks.Pre); err != nil {
|
||||
return fmt.Errorf("pre hook failed: %w", err)
|
||||
}
|
||||
if err := makeUniversalBinary(ctx, unibin); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := runHook(ctx, unibin.Hooks.Post); err != nil {
|
||||
return fmt.Errorf("post hook failed: %w", err)
|
||||
}
|
||||
if !unibin.Replace {
|
||||
return nil
|
||||
}
|
||||
@ -58,6 +66,47 @@ func (Pipe) Run(ctx *context.Context) error {
|
||||
return g.Wait()
|
||||
}
|
||||
|
||||
func runHook(ctx *context.Context, hooks config.Hooks) error {
|
||||
if len(hooks) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, hook := range hooks {
|
||||
var envs []string
|
||||
envs = append(envs, ctx.Env.Strings()...)
|
||||
|
||||
for _, rawEnv := range hook.Env {
|
||||
env, err := tmpl.New(ctx).Apply(rawEnv)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
envs = append(envs, env)
|
||||
}
|
||||
|
||||
dir, err := tmpl.New(ctx).Apply(hook.Dir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sh, err := tmpl.New(ctx).WithEnvS(envs).Apply(hook.Cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.WithField("hook", sh).Info("running hook")
|
||||
cmd, err := shellwords.Parse(sh)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := shell.Run(ctx, dir, cmd, envs); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type input struct {
|
||||
data []byte
|
||||
cpu uint32
|
||||
|
@ -104,7 +104,9 @@ func TestRun(t *testing.T) {
|
||||
"arm64": filepath.Join(dist, "fake_darwin_arm64/fake"),
|
||||
}
|
||||
|
||||
ctx1 := context.New(config.Project{
|
||||
pre := filepath.Join(dist, "pre")
|
||||
post := filepath.Join(dist, "post")
|
||||
cfg := config.Project{
|
||||
Dist: dist,
|
||||
UniversalBinaries: []config.UniversalBinary{
|
||||
{
|
||||
@ -113,7 +115,8 @@ func TestRun(t *testing.T) {
|
||||
Replace: true,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
ctx1 := context.New(cfg)
|
||||
|
||||
ctx2 := context.New(config.Project{
|
||||
Dist: dist,
|
||||
@ -145,6 +148,24 @@ func TestRun(t *testing.T) {
|
||||
},
|
||||
})
|
||||
|
||||
ctx5 := context.New(config.Project{
|
||||
Dist: dist,
|
||||
UniversalBinaries: []config.UniversalBinary{
|
||||
{
|
||||
ID: "foo",
|
||||
NameTemplate: "foo",
|
||||
Hooks: config.BuildHookConfig{
|
||||
Pre: []config.Hook{
|
||||
{Cmd: "touch " + pre},
|
||||
},
|
||||
Post: []config.Hook{
|
||||
{Cmd: "touch " + post},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
for arch, path := range paths {
|
||||
cmd := exec.Command("go", "build", "-o", path, src)
|
||||
cmd.Env = append(os.Environ(), "GOOS=darwin", "GOARCH="+arch)
|
||||
@ -168,6 +189,7 @@ func TestRun(t *testing.T) {
|
||||
}
|
||||
ctx1.Artifacts.Add(&art)
|
||||
ctx2.Artifacts.Add(&art)
|
||||
ctx5.Artifacts.Add(&art)
|
||||
ctx4.Artifacts.Add(&artifact.Artifact{
|
||||
Name: "fake",
|
||||
Path: path + "wrong",
|
||||
@ -212,6 +234,25 @@ func TestRun(t *testing.T) {
|
||||
t.Run("fail to open", func(t *testing.T) {
|
||||
require.ErrorIs(t, Pipe{}.Run(ctx4), os.ErrNotExist)
|
||||
})
|
||||
|
||||
t.Run("hooks", func(t *testing.T) {
|
||||
require.NoError(t, Pipe{}.Run(ctx5))
|
||||
require.FileExists(t, pre)
|
||||
require.FileExists(t, post)
|
||||
})
|
||||
|
||||
t.Run("failing pre-hook", func(t *testing.T) {
|
||||
ctx := ctx5
|
||||
ctx.Config.UniversalBinaries[0].Hooks.Pre = []config.Hook{{Cmd: "exit 1"}}
|
||||
ctx.Config.UniversalBinaries[0].Hooks.Post = []config.Hook{{Cmd: "echo post"}}
|
||||
require.EqualError(t, Pipe{}.Run(ctx), `pre hook failed: "": exec: "exit": executable file not found in $PATH`)
|
||||
})
|
||||
t.Run("failing post-hook", func(t *testing.T) {
|
||||
ctx := ctx5
|
||||
ctx.Config.UniversalBinaries[0].Hooks.Pre = []config.Hook{{Cmd: "echo pre"}}
|
||||
ctx.Config.UniversalBinaries[0].Hooks.Post = []config.Hook{{Cmd: "exit 1"}}
|
||||
require.EqualError(t, Pipe{}.Run(ctx), `post hook failed: "": exec: "exit": executable file not found in $PATH`)
|
||||
})
|
||||
}
|
||||
|
||||
func checkUniversalBinary(tb testing.TB, unibin *artifact.Artifact) {
|
||||
|
44
internal/shell/shell.go
Normal file
44
internal/shell/shell.go
Normal file
@ -0,0 +1,44 @@
|
||||
package shell
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"os/exec"
|
||||
|
||||
"github.com/apex/log"
|
||||
|
||||
"github.com/goreleaser/goreleaser/internal/gio"
|
||||
"github.com/goreleaser/goreleaser/internal/logext"
|
||||
"github.com/goreleaser/goreleaser/pkg/context"
|
||||
)
|
||||
|
||||
// Run a shell command with given arguments and envs
|
||||
func Run(ctx *context.Context, dir string, command, env []string) error {
|
||||
fields := log.Fields{
|
||||
"cmd": command,
|
||||
"env": env,
|
||||
}
|
||||
|
||||
/* #nosec */
|
||||
cmd := exec.CommandContext(ctx, command[0], command[1:]...)
|
||||
cmd.Env = env
|
||||
|
||||
var b bytes.Buffer
|
||||
w := gio.Safe(&b)
|
||||
|
||||
cmd.Stderr = io.MultiWriter(logext.NewWriter(fields, logext.Error), w)
|
||||
cmd.Stdout = io.MultiWriter(logext.NewWriter(fields, logext.Info), w)
|
||||
|
||||
if dir != "" {
|
||||
cmd.Dir = dir
|
||||
}
|
||||
|
||||
log.WithFields(fields).Debug("running")
|
||||
if err := cmd.Run(); err != nil {
|
||||
log.WithFields(fields).WithError(err).Debug("failed")
|
||||
return fmt.Errorf("%q: %w", b.String(), err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
31
internal/shell/shell_test.go
Normal file
31
internal/shell/shell_test.go
Normal file
@ -0,0 +1,31 @@
|
||||
package shell_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/goreleaser/goreleaser/internal/shell"
|
||||
"github.com/goreleaser/goreleaser/pkg/config"
|
||||
"github.com/goreleaser/goreleaser/pkg/context"
|
||||
)
|
||||
|
||||
func TestRunAValidCommand(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
ctx := context.New(config.Project{})
|
||||
|
||||
err := shell.Run(ctx, "", []string{"echo", "test"}, []string{})
|
||||
assert.NoError(err)
|
||||
}
|
||||
|
||||
func TestRunAnInValidCommand(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
ctx := context.New(config.Project{})
|
||||
|
||||
err := shell.Run(ctx, "", []string{"invalid", "command"}, []string{})
|
||||
|
||||
assert.Error(err)
|
||||
assert.Contains(err.Error(), "executable file not found")
|
||||
}
|
@ -431,9 +431,10 @@ func (f File) JSONSchemaType() *jsonschema.Type {
|
||||
|
||||
// UniversalBinary setups macos universal binaries.
|
||||
type UniversalBinary struct {
|
||||
ID string `yaml:"id,omitempty"`
|
||||
NameTemplate string `yaml:"name_template,omitempty"`
|
||||
Replace bool `yaml:"replace,omitempty"`
|
||||
ID string `yaml:"id,omitempty"`
|
||||
NameTemplate string `yaml:"name_template,omitempty"`
|
||||
Replace bool `yaml:"replace,omitempty"`
|
||||
Hooks BuildHookConfig `yaml:"hooks,omitempty"`
|
||||
}
|
||||
|
||||
// Archive config used for the archive.
|
||||
|
Loading…
x
Reference in New Issue
Block a user