Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: allow a --output flag on goreleaser build #2701

Merged
merged 3 commits into from Feb 5, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
27 changes: 26 additions & 1 deletion cmd/build.go
Expand Up @@ -9,6 +9,8 @@ import (
"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 +36,7 @@ type buildOpts struct {
parallelism int
timeout time.Duration
singleTarget bool
output string
}

func newBuildCmd() *buildCmd {
Expand Down Expand Up @@ -81,6 +84,7 @@ Finally, it allows you to generate a local build for your current machine only u
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 @@ -98,7 +102,7 @@ func buildProject(options buildOpts) (*context.Context, error) {
return nil, err
}
return ctx, ctrlc.Default.Run(ctx, func() error {
for _, pipe := range pipeline.BuildPipeline {
for _, pipe := range setupPipeline(ctx, options) {
if err := skip.Maybe(
pipe,
logging.Log(
Expand All @@ -114,6 +118,13 @@ func buildProject(options buildOpts) (*context.Context, error) {
})
}

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

func setupBuildContext(ctx *context.Context, options buildOpts) error {
ctx.Parallelism = runtime.NumCPU()
if options.parallelism > 0 {
Expand Down Expand Up @@ -182,3 +193,17 @@ 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
return gio.Copy(path, w.output)
}
57 changes: 57 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,62 @@ 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.BuildPipeline, setupPipeline(context.New(config.Project{}), buildOpts{}))
})

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

t.Run("single-target and id", func(t *testing.T) {
require.Equal(t, pipeline.BuildPipeline, 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, pipeline.BuildPipeline, 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.BuildPipeline, 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.BuildPipeline, 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