mirror of
https://github.com/goreleaser/goreleaser.git
synced 2025-01-10 03:47:03 +02:00
feat: break if main package doesnt have a main func
Breaks the release if the package being built doesnt have a main function. Also improved error handling in the build pipe. Closes #415
This commit is contained in:
parent
adc2d7d4c5
commit
83093ac477
@ -4,6 +4,7 @@ package build
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
@ -15,6 +16,8 @@ import (
|
||||
"github.com/goreleaser/goreleaser/internal/buildtarget"
|
||||
"github.com/goreleaser/goreleaser/internal/ext"
|
||||
"github.com/goreleaser/goreleaser/internal/name"
|
||||
zglob "github.com/mattn/go-zglob"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
@ -30,6 +33,9 @@ func (Pipe) Description() string {
|
||||
func (Pipe) Run(ctx *context.Context) error {
|
||||
for _, build := range ctx.Config.Builds {
|
||||
log.WithField("build", build).Debug("building")
|
||||
if err := checkMain(ctx, build); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := runPipeOnBuild(ctx, build); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -37,9 +43,32 @@ func (Pipe) Run(ctx *context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkMain(ctx *context.Context, build config.Build) error {
|
||||
var glob = build.Main
|
||||
if !strings.HasSuffix(glob, "main.go") {
|
||||
glob = glob + "/" + "*.go"
|
||||
}
|
||||
log.Debugf("glob is %s", glob)
|
||||
files, err := zglob.Glob(glob)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "glob %s is not valid, please file a bug", glob)
|
||||
}
|
||||
log.Debugf("files %v", files)
|
||||
for _, file := range files {
|
||||
bts, err := ioutil.ReadFile(file)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to read main file %s", file)
|
||||
}
|
||||
if strings.Contains(string(bts), "func main() {") {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("build for %s does not contain a main function", build.Binary)
|
||||
}
|
||||
|
||||
func runPipeOnBuild(ctx *context.Context, build config.Build) error {
|
||||
if err := runHook(build.Env, build.Hooks.Pre); err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "pre hook failed")
|
||||
}
|
||||
sem := make(chan bool, ctx.Parallelism)
|
||||
var g errgroup.Group
|
||||
@ -57,7 +86,10 @@ func runPipeOnBuild(ctx *context.Context, build config.Build) error {
|
||||
if err := g.Wait(); err != nil {
|
||||
return err
|
||||
}
|
||||
return runHook(build.Env, build.Hooks.Post)
|
||||
if err := runHook(build.Env, build.Hooks.Post); err != nil {
|
||||
return errors.Wrap(err, "post hook failed")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func runHook(env []string, hook string) error {
|
||||
@ -95,7 +127,10 @@ func doBuild(ctx *context.Context, build config.Build, target buildtarget.Target
|
||||
return err
|
||||
}
|
||||
cmd = append(cmd, "-ldflags="+flags, "-o", binary, build.Main)
|
||||
return run(target, cmd, build.Env)
|
||||
if err := run(target, cmd, build.Env); err != nil {
|
||||
return errors.Wrapf(err, "failed to build for %s", target)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func run(target buildtarget.Target, command, env []string) error {
|
||||
@ -109,7 +144,7 @@ func run(target buildtarget.Target, command, env []string) error {
|
||||
log.Debug("running")
|
||||
if out, err := cmd.CombinedOutput(); err != nil {
|
||||
log.WithError(err).Debug("failed")
|
||||
return fmt.Errorf("build failed for %s:\n%v", target.PrettyString(), string(out))
|
||||
return errors.New(string(out))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ import (
|
||||
"github.com/goreleaser/goreleaser/config"
|
||||
"github.com/goreleaser/goreleaser/context"
|
||||
"github.com/goreleaser/goreleaser/internal/buildtarget"
|
||||
"github.com/goreleaser/goreleaser/internal/testlib"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@ -42,8 +43,9 @@ func TestBuild(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRunFullPipe(t *testing.T) {
|
||||
folder, err := ioutil.TempDir("", "goreleasertest")
|
||||
assert.NoError(t, err)
|
||||
folder, back := testlib.Mktmp(t)
|
||||
defer back()
|
||||
writeGoodMain(t, folder)
|
||||
var binary = filepath.Join(folder, "testing")
|
||||
var pre = filepath.Join(folder, "pre")
|
||||
var post = filepath.Join(folder, "post")
|
||||
@ -51,6 +53,7 @@ func TestRunFullPipe(t *testing.T) {
|
||||
Dist: folder,
|
||||
Builds: []config.Build{
|
||||
{
|
||||
Main: ".",
|
||||
Binary: "testing",
|
||||
Flags: "-v",
|
||||
Ldflags: "-X main.test=testing",
|
||||
@ -74,14 +77,16 @@ func TestRunFullPipe(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRunPipeFormatBinary(t *testing.T) {
|
||||
folder, err := ioutil.TempDir("", "goreleasertest")
|
||||
assert.NoError(t, err)
|
||||
folder, back := testlib.Mktmp(t)
|
||||
defer back()
|
||||
writeGoodMain(t, folder)
|
||||
var binary = filepath.Join(folder, "binary-testing")
|
||||
var config = config.Project{
|
||||
ProjectName: "testing",
|
||||
Dist: folder,
|
||||
Builds: []config.Build{
|
||||
{
|
||||
Main: ".",
|
||||
Binary: "testing",
|
||||
Goos: []string{
|
||||
runtime.GOOS,
|
||||
@ -101,13 +106,15 @@ func TestRunPipeFormatBinary(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRunPipeArmBuilds(t *testing.T) {
|
||||
folder, err := ioutil.TempDir("", "goreleasertest")
|
||||
assert.NoError(t, err)
|
||||
folder, back := testlib.Mktmp(t)
|
||||
defer back()
|
||||
writeGoodMain(t, folder)
|
||||
var binary = filepath.Join(folder, "armtesting")
|
||||
var config = config.Project{
|
||||
Dist: folder,
|
||||
Builds: []config.Build{
|
||||
{
|
||||
Main: ".",
|
||||
Binary: "armtesting",
|
||||
Flags: "-v",
|
||||
Ldflags: "-X main.test=armtesting",
|
||||
@ -129,9 +136,14 @@ func TestRunPipeArmBuilds(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestBuildFailed(t *testing.T) {
|
||||
folder, back := testlib.Mktmp(t)
|
||||
defer back()
|
||||
writeGoodMain(t, folder)
|
||||
var config = config.Project{
|
||||
Dist: folder,
|
||||
Builds: []config.Build{
|
||||
{
|
||||
Main: ".",
|
||||
Flags: "-flag-that-dont-exists-to-force-failure",
|
||||
Goos: []string{
|
||||
runtime.GOOS,
|
||||
@ -142,13 +154,18 @@ func TestBuildFailed(t *testing.T) {
|
||||
},
|
||||
},
|
||||
}
|
||||
assert.Error(t, Pipe{}.Run(context.New(config)))
|
||||
assertContainsError(t, Pipe{}.Run(context.New(config)), `flag provided but not defined: -flag-that-dont-exists-to-force-failure`)
|
||||
}
|
||||
|
||||
func TestRunPipeWithInvalidOS(t *testing.T) {
|
||||
folder, back := testlib.Mktmp(t)
|
||||
defer back()
|
||||
writeGoodMain(t, folder)
|
||||
var config = config.Project{
|
||||
Dist: folder,
|
||||
Builds: []config.Build{
|
||||
{
|
||||
Main: ".",
|
||||
Flags: "-v",
|
||||
Goos: []string{
|
||||
"windows",
|
||||
@ -163,11 +180,16 @@ func TestRunPipeWithInvalidOS(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRunInvalidNametemplate(t *testing.T) {
|
||||
folder, back := testlib.Mktmp(t)
|
||||
defer back()
|
||||
writeGoodMain(t, folder)
|
||||
for _, format := range []string{"tar.gz", "zip", "binary"} {
|
||||
var config = config.Project{
|
||||
Dist: folder,
|
||||
ProjectName: "nameeeee",
|
||||
Builds: []config.Build{
|
||||
{
|
||||
Main: ".",
|
||||
Binary: "namet{{.est}",
|
||||
Flags: "-v",
|
||||
Goos: []string{
|
||||
@ -183,14 +205,19 @@ func TestRunInvalidNametemplate(t *testing.T) {
|
||||
NameTemplate: "{{.Binary}",
|
||||
},
|
||||
}
|
||||
assert.Error(t, Pipe{}.Run(context.New(config)))
|
||||
assert.EqualError(t, Pipe{}.Run(context.New(config)), `template: nameeeee:1: unexpected "}" in operand`)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunInvalidLdflags(t *testing.T) {
|
||||
folder, back := testlib.Mktmp(t)
|
||||
defer back()
|
||||
writeGoodMain(t, folder)
|
||||
var config = config.Project{
|
||||
Dist: folder,
|
||||
Builds: []config.Build{
|
||||
{
|
||||
Main: ".",
|
||||
Binary: "nametest",
|
||||
Flags: "-v",
|
||||
Ldflags: "-s -w -X main.version={{.Version}",
|
||||
@ -203,14 +230,54 @@ func TestRunInvalidLdflags(t *testing.T) {
|
||||
},
|
||||
},
|
||||
}
|
||||
assert.Error(t, Pipe{}.Run(context.New(config)))
|
||||
assert.EqualError(t, Pipe{}.Run(context.New(config)), `template: ldflags:1: unexpected "}" in operand`)
|
||||
}
|
||||
|
||||
func TestRunPipeFailingHooks(t *testing.T) {
|
||||
prepare := func() *context.Context {
|
||||
folder, back := testlib.Mktmp(t)
|
||||
defer back()
|
||||
writeGoodMain(t, folder)
|
||||
var config = config.Project{
|
||||
Dist: folder,
|
||||
Builds: []config.Build{
|
||||
{
|
||||
Main: ".",
|
||||
Binary: "hooks",
|
||||
Hooks: config.Hooks{},
|
||||
Goos: []string{
|
||||
runtime.GOOS,
|
||||
},
|
||||
Goarch: []string{
|
||||
runtime.GOARCH,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
return context.New(config)
|
||||
}
|
||||
t.Run("pre-hook", func(t *testing.T) {
|
||||
var ctx = prepare()
|
||||
ctx.Config.Builds[0].Hooks.Pre = "exit 1"
|
||||
assert.EqualError(t, Pipe{}.Run(ctx), `pre hook failed: `)
|
||||
})
|
||||
t.Run("post-hook", func(t *testing.T) {
|
||||
var ctx = prepare()
|
||||
ctx.Config.Builds[0].Hooks.Post = "exit 1"
|
||||
assert.EqualError(t, Pipe{}.Run(ctx), `post hook failed: `)
|
||||
})
|
||||
}
|
||||
|
||||
func TestRunPipeWithouMainFunc(t *testing.T) {
|
||||
folder, back := testlib.Mktmp(t)
|
||||
defer back()
|
||||
writeMainWithoutMainFunc(t, folder)
|
||||
var config = config.Project{
|
||||
Dist: folder,
|
||||
Builds: []config.Build{
|
||||
{
|
||||
Hooks: config.Hooks{},
|
||||
Binary: "no-main",
|
||||
Hooks: config.Hooks{},
|
||||
Goos: []string{
|
||||
runtime.GOOS,
|
||||
},
|
||||
@ -221,13 +288,13 @@ func TestRunPipeFailingHooks(t *testing.T) {
|
||||
},
|
||||
}
|
||||
var ctx = context.New(config)
|
||||
t.Run("pre-hook", func(t *testing.T) {
|
||||
ctx.Config.Builds[0].Hooks.Pre = "exit 1"
|
||||
assert.Error(t, Pipe{}.Run(ctx))
|
||||
t.Run("glob", func(t *testing.T) {
|
||||
ctx.Config.Builds[0].Main = "."
|
||||
assert.EqualError(t, Pipe{}.Run(ctx), `build for no-main does not contain a main function`)
|
||||
})
|
||||
t.Run("post-hook", func(t *testing.T) {
|
||||
ctx.Config.Builds[0].Hooks.Post = "exit 1"
|
||||
assert.Error(t, Pipe{}.Run(ctx))
|
||||
t.Run("fixed main.go", func(t *testing.T) {
|
||||
ctx.Config.Builds[0].Main = "main.go"
|
||||
assert.EqualError(t, Pipe{}.Run(ctx), `build for no-main does not contain a main function`)
|
||||
})
|
||||
}
|
||||
|
||||
@ -235,3 +302,34 @@ func exists(file string) bool {
|
||||
_, err := os.Stat(file)
|
||||
return !os.IsNotExist(err)
|
||||
}
|
||||
|
||||
func writeMainWithoutMainFunc(t *testing.T, folder string) {
|
||||
main := `package main
|
||||
|
||||
func foo() {
|
||||
println("foo")
|
||||
}
|
||||
`
|
||||
writeFile(t, folder, main)
|
||||
}
|
||||
|
||||
func writeGoodMain(t *testing.T, folder string) {
|
||||
main := `package main
|
||||
|
||||
func main() {
|
||||
println("hi")
|
||||
}
|
||||
`
|
||||
writeFile(t, folder, main)
|
||||
}
|
||||
|
||||
func writeFile(t *testing.T, folder, content string) {
|
||||
assert.NoError(t, ioutil.WriteFile(
|
||||
filepath.Join(folder, "main.go"), []byte(content), 0644),
|
||||
)
|
||||
}
|
||||
|
||||
func assertContainsError(t *testing.T, err error, s string) {
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), s)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user