Skip to content

Commit

Permalink
feat: allow a --output flag on goreleaser build (#2701)
Browse files Browse the repository at this point in the history
* feat: allow a --output flag on goreleaser build

Signed-off-by: Carlos A Becker <caarlos0@gmail.com>

* fix: single build always to copies to root

Signed-off-by: Carlos A Becker <caarlos0@gmail.com>
  • Loading branch information
caarlos0 committed Feb 5, 2022
1 parent 750d520 commit c42a2fd
Show file tree
Hide file tree
Showing 2 changed files with 109 additions and 1 deletion.
32 changes: 31 additions & 1 deletion cmd/build.go
Expand Up @@ -3,12 +3,15 @@ package cmd
import (
"fmt"
"os"
"path/filepath"
"runtime"
"time"

"github.com/apex/log"
"github.com/caarlos0/ctrlc"
"github.com/fatih/color"
"github.com/goreleaser/goreleaser/internal/artifact"
"github.com/goreleaser/goreleaser/internal/gio"
"github.com/goreleaser/goreleaser/internal/middleware/errhandler"
"github.com/goreleaser/goreleaser/internal/middleware/logging"
"github.com/goreleaser/goreleaser/internal/middleware/skip"
Expand All @@ -34,6 +37,7 @@ type buildOpts struct {
parallelism int
timeout time.Duration
singleTarget bool
output string
}

func newBuildCmd() *buildCmd {
Expand Down Expand Up @@ -90,6 +94,7 @@ defaulting to the current's machine target if not set.
cmd.Flags().BoolVar(&root.opts.singleTarget, "single-target", false, "Builds only for current GOOS and GOARCH")
cmd.Flags().StringVar(&root.opts.id, "id", "", "Builds only the specified build id")
cmd.Flags().BoolVar(&root.opts.deprecated, "deprecated", false, "Force print the deprecation message - tests only")
cmd.Flags().StringVarP(&root.opts.output, "output", "o", "", "Path to the binary, defaults to the distribution folder according to configs. Only taked into account when using --single-target and a single id (either with --id or if config only has one build)")
_ = cmd.Flags().MarkHidden("deprecated")

root.cmd = cmd
Expand All @@ -107,7 +112,7 @@ func buildProject(options buildOpts) (*context.Context, error) {
return nil, err
}
return ctx, ctrlc.Default.Run(ctx, func() error {
for _, pipe := range pipeline.BuildCmdPipeline {
for _, pipe := range setupPipeline(ctx, options) {
if err := skip.Maybe(
pipe,
logging.Log(
Expand All @@ -123,6 +128,13 @@ func buildProject(options buildOpts) (*context.Context, error) {
})
}

func setupPipeline(ctx *context.Context, options buildOpts) []pipeline.Piper {
if options.singleTarget && (options.id != "" || len(ctx.Config.Builds) == 1) {
return append(pipeline.BuildCmdPipeline, withOutputPipe{options.output})
}
return pipeline.BuildCmdPipeline
}

func setupBuildContext(ctx *context.Context, options buildOpts) error {
ctx.Parallelism = runtime.NumCPU()
if options.parallelism > 0 {
Expand Down Expand Up @@ -191,3 +203,21 @@ func setupBuildID(ctx *context.Context, id string) error {
ctx.Config.Builds = keep
return nil
}

// withOutputPipe copies the binary from dist to the specified output path.
type withOutputPipe struct {
output string
}

func (w withOutputPipe) String() string {
return fmt.Sprintf("copying binary to %q", w.output)
}

func (w withOutputPipe) Run(ctx *context.Context) error {
path := ctx.Artifacts.Filter(artifact.ByType(artifact.Binary)).List()[0].Path
out := w.output
if out == "" {
out = filepath.Base(path)
}
return gio.Copy(path, out)
}
78 changes: 78 additions & 0 deletions cmd/build_test.go
Expand Up @@ -5,6 +5,7 @@ import (
"runtime"
"testing"

"github.com/goreleaser/goreleaser/internal/pipeline"
"github.com/goreleaser/goreleaser/pkg/config"
"github.com/goreleaser/goreleaser/pkg/context"
"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -40,6 +41,83 @@ func TestBuildBrokenProject(t *testing.T) {
require.EqualError(t, cmd.cmd.Execute(), "failed to parse dir: .: main.go:1:1: expected 'package', found not")
}

func TestSetupPipeline(t *testing.T) {
t.Run("regular", func(t *testing.T) {
require.Equal(
t,
pipeline.BuildCmdPipeline,
setupPipeline(context.New(config.Project{}), buildOpts{}),
)
})

t.Run("single-target", func(t *testing.T) {
require.Equal(
t,
pipeline.BuildCmdPipeline,
setupPipeline(context.New(config.Project{}), buildOpts{
singleTarget: true,
}),
)
})

t.Run("single-target and id", func(t *testing.T) {
require.Equal(
t,
append(pipeline.BuildCmdPipeline, withOutputPipe{""}),
setupPipeline(context.New(config.Project{}), buildOpts{
singleTarget: true,
id: "foo",
}),
)
})

t.Run("single-target and single build on config", func(t *testing.T) {
require.Equal(
t,
append(pipeline.BuildCmdPipeline, withOutputPipe{""}),
setupPipeline(
context.New(config.Project{
Builds: []config.Build{{}},
}),
buildOpts{
singleTarget: true,
},
),
)
})

t.Run("single-target, id and output", func(t *testing.T) {
require.Equal(
t,
append(pipeline.BuildCmdPipeline, withOutputPipe{"foobar"}),
setupPipeline(
context.New(config.Project{}),
buildOpts{
singleTarget: true,
id: "foo",
output: "foobar",
},
),
)
})

t.Run("single-target, single build on config and output", func(t *testing.T) {
require.Equal(
t,
append(pipeline.BuildCmdPipeline, withOutputPipe{"zaz"}),
setupPipeline(
context.New(config.Project{
Builds: []config.Build{{}},
}),
buildOpts{
singleTarget: true,
output: "zaz",
},
),
)
})
}

func TestBuildFlags(t *testing.T) {
setup := func(opts buildOpts) *context.Context {
ctx := context.New(config.Project{})
Expand Down

0 comments on commit c42a2fd

Please sign in to comment.