diff --git a/internal/builders/golang/build.go b/internal/builders/golang/build.go index e522b3374c0b..1f4a248af190 100644 --- a/internal/builders/golang/build.go +++ b/internal/builders/golang/build.go @@ -7,6 +7,7 @@ import ( "go/token" "os" "os/exec" + "path/filepath" "strings" "github.com/apex/log" @@ -32,6 +33,9 @@ type Builder struct{} // WithDefaults sets the defaults for a golang build and returns it func (*Builder) WithDefaults(build config.Build) config.Build { + if build.Dir == "" { + build.Dir = "." + } if build.Main == "" { build.Main = "." } @@ -111,7 +115,7 @@ func (*Builder) Build(ctx *context.Context, build config.Build, options api.Opti cmd = append(cmd, processedLdFlags) cmd = append(cmd, "-o", options.Path, build.Main) - if err := run(ctx, cmd, env); err != nil { + if err := run(ctx, cmd, env, build.Dir); err != nil { return errors.Wrapf(err, "failed to build for %s", options.Target) } ctx.Artifacts.Add(artifact) @@ -138,11 +142,12 @@ func joinLdFlags(flags []string) string { return ldflagString.String() } -func run(ctx *context.Context, command, env []string) error { +func run(ctx *context.Context, command, env []string, dir string) error { /* #nosec */ var cmd = exec.CommandContext(ctx, command[0], command[1:]...) var log = log.WithField("env", env).WithField("cmd", command) cmd.Env = env + cmd.Dir = dir log.Debug("running") if out, err := cmd.CombinedOutput(); err != nil { log.WithError(err).Debug("failed") @@ -182,6 +187,9 @@ func checkMain(build config.Build) error { if main == "" { main = "." } + if build.Dir != "" { + main = filepath.Join(build.Dir, main) + } stat, ferr := os.Stat(main) if ferr != nil { return ferr diff --git a/internal/builders/golang/build_test.go b/internal/builders/golang/build_test.go index 3841cd0282bc..d6ed2af01792 100644 --- a/internal/builders/golang/build_test.go +++ b/internal/builders/golang/build_test.go @@ -3,6 +3,7 @@ package golang import ( "fmt" "io/ioutil" + "os" "path/filepath" "runtime" "strings" @@ -184,6 +185,38 @@ func TestBuild(t *testing.T) { }) } +func TestBuildCodeInSubdir(t *testing.T) { + folder, back := testlib.Mktmp(t) + defer back() + subdir := filepath.Join(folder, "bar") + err := os.Mkdir(subdir, 0755) + assert.NoError(t, err) + writeGoodMain(t, subdir) + var config = config.Project{ + Builds: []config.Build{ + { + ID: "foo", + Env: []string{"GO111MODULE=off"}, + Dir: "bar", + Binary: "foo", + Targets: []string{ + runtimeTarget, + }, + }, + }, + } + var ctx = context.New(config) + ctx.Git.CurrentTag = "5.6.7" + var build = ctx.Config.Builds[0] + err = Default.Build(ctx, build, api.Options{ + Target: runtimeTarget, + Name: build.Binary, + Path: filepath.Join(folder, "dist", runtimeTarget, build.Binary), + Ext: "", + }) + assert.NoError(t, err) +} + func TestBuildFailed(t *testing.T) { folder, back := testlib.Mktmp(t) defer back() diff --git a/internal/pipe/build/build.go b/internal/pipe/build/build.go index 3f8163c23b4e..22914818c57e 100644 --- a/internal/pipe/build/build.go +++ b/internal/pipe/build/build.go @@ -113,11 +113,16 @@ func doBuild(ctx *context.Context, build config.Build, target string) error { build.Binary = binary var name = build.Binary + ext - var path = filepath.Join( - ctx.Config.Dist, - fmt.Sprintf("%s_%s", build.ID, target), - name, + path, err := filepath.Abs( + filepath.Join( + ctx.Config.Dist, + fmt.Sprintf("%s_%s", build.ID, target), + name, + ), ) + if err != nil { + return err + } log.WithField("binary", path).Info("building") return builders.For(build.Lang).Build(ctx, build, builders.Options{ Target: target, diff --git a/internal/pipe/build/build_test.go b/internal/pipe/build/build_test.go index dd985bf7493f..165a66f6aa6d 100644 --- a/internal/pipe/build/build_test.go +++ b/internal/pipe/build/build_test.go @@ -231,6 +231,7 @@ func TestDefaultEmptyBuild(t *testing.T) { var build = ctx.Config.Builds[0] assert.Equal(t, ctx.Config.ProjectName, build.ID) assert.Equal(t, ctx.Config.ProjectName, build.Binary) + assert.Equal(t, ".", build.Dir) assert.Equal(t, ".", build.Main) assert.Equal(t, []string{"linux", "darwin"}, build.Goos) assert.Equal(t, []string{"amd64", "386"}, build.Goarch) @@ -289,6 +290,7 @@ func TestDefaultPartialBuilds(t *testing.T) { { ID: "build2", Binary: "foo", + Dir: "baz", Ldflags: []string{"-s -w"}, Goarch: []string{"386"}, }, @@ -299,6 +301,7 @@ func TestDefaultPartialBuilds(t *testing.T) { t.Run("build0", func(t *testing.T) { var build = ctx.Config.Builds[0] assert.Equal(t, "bar", build.Binary) + assert.Equal(t, ".", build.Dir) assert.Equal(t, "./cmd/main.go", build.Main) assert.Equal(t, []string{"linux"}, build.Goos) assert.Equal(t, []string{"amd64", "386"}, build.Goarch) @@ -310,6 +313,7 @@ func TestDefaultPartialBuilds(t *testing.T) { var build = ctx.Config.Builds[1] assert.Equal(t, "foo", build.Binary) assert.Equal(t, ".", build.Main) + assert.Equal(t, "baz", build.Dir) assert.Equal(t, []string{"linux", "darwin"}, build.Goos) assert.Equal(t, []string{"386"}, build.Goarch) assert.Equal(t, []string{"6"}, build.Goarm) diff --git a/pkg/config/config.go b/pkg/config/config.go index da0323bdce3d..dc37491c167d 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -144,6 +144,7 @@ type Build struct { Goarm []string `yaml:",omitempty"` Targets []string `yaml:",omitempty"` Ignore []IgnoredBuild `yaml:",omitempty"` + Dir string `yaml:",omitempty"` Main string `yaml:",omitempty"` Ldflags StringArray `yaml:",omitempty"` Flags FlagArray `yaml:",omitempty"` diff --git a/www/content/build.md b/www/content/build.md index f92987e8038f..bc60d0be457e 100644 --- a/www/content/build.md +++ b/www/content/build.md @@ -21,6 +21,10 @@ builds: # Defaults to the project name. id: "my-build" + # Path to project's (sub)directory containing Go code. + # Default is `.`. + dir: go + # Path to main.go file or main package. # Default is `.`. main: ./cmd/main.go