1
0
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:
Carlos Alexandro Becker 2017-11-18 14:14:39 -02:00 committed by Carlos Alexandro Becker
parent adc2d7d4c5
commit 83093ac477
2 changed files with 153 additions and 20 deletions

View File

@ -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
}

View File

@ -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)
}