1
0
mirror of https://github.com/goreleaser/goreleaser.git synced 2024-12-31 01:53:50 +02:00

feat: Add build command (#1520)

* feat: Add build command

* feat(cmd/build): Add skip-post-hooks flag

* Update internal/pipeline/pipeline.go

Co-authored-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com>

Co-authored-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com>
This commit is contained in:
Radek Simko 2020-05-15 15:19:20 +01:00 committed by GitHub
parent 0cd40a42da
commit 983f342ab8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 249 additions and 37 deletions

107
cmd/build.go Normal file
View File

@ -0,0 +1,107 @@
package cmd
import (
"time"
"github.com/apex/log"
"github.com/caarlos0/ctrlc"
"github.com/fatih/color"
"github.com/goreleaser/goreleaser/internal/middleware"
"github.com/goreleaser/goreleaser/internal/pipeline"
"github.com/goreleaser/goreleaser/pkg/context"
"github.com/spf13/cobra"
)
type buildCmd struct {
cmd *cobra.Command
opts buildOpts
}
type buildOpts struct {
config string
snapshot bool
skipValidate bool
skipPostHooks bool
rmDist bool
deprecated bool
parallelism int
timeout time.Duration
}
func newBuildCmd() *buildCmd {
var root = &buildCmd{}
// nolint: dupl
var cmd = &cobra.Command{
Use: "build",
Aliases: []string{"b"},
Short: "Builds the current project",
SilenceUsage: true,
SilenceErrors: true,
RunE: func(cmd *cobra.Command, args []string) error {
start := time.Now()
log.Infof(color.New(color.Bold).Sprint("building..."))
ctx, err := buildProject(root.opts)
if err != nil {
return wrapError(err, color.New(color.Bold).Sprintf("build failed after %0.2fs", time.Since(start).Seconds()))
}
if ctx.Deprecated {
log.Warn(color.New(color.Bold).Sprintf("your config is using deprecated properties, check logs above for details"))
}
log.Infof(color.New(color.Bold).Sprintf("build succeeded after %0.2fs", time.Since(start).Seconds()))
return nil
},
}
cmd.Flags().StringVarP(&root.opts.config, "config", "f", "", "Load configuration from file")
cmd.Flags().BoolVar(&root.opts.snapshot, "snapshot", false, "Generate an unversioned snapshot build, skipping all validations and without publishing any artifacts")
cmd.Flags().BoolVar(&root.opts.skipValidate, "skip-validate", false, "Skips several sanity checks")
cmd.Flags().BoolVar(&root.opts.skipPostHooks, "skip-post-hooks", false, "Skips all post-build hooks")
cmd.Flags().BoolVar(&root.opts.rmDist, "rm-dist", false, "Remove the dist folder before building")
cmd.Flags().IntVarP(&root.opts.parallelism, "parallelism", "p", 4, "Amount tasks to run concurrently")
cmd.Flags().DurationVar(&root.opts.timeout, "timeout", 30*time.Minute, "Timeout to the entire build process")
cmd.Flags().BoolVar(&root.opts.deprecated, "deprecated", false, "Force print the deprecation message - tests only")
_ = cmd.Flags().MarkHidden("deprecated")
root.cmd = cmd
return root
}
func buildProject(options buildOpts) (*context.Context, error) {
cfg, err := loadConfig(options.config)
if err != nil {
return nil, err
}
ctx, cancel := context.NewWithTimeout(cfg, options.timeout)
defer cancel()
setupBuildContext(ctx, options)
return ctx, ctrlc.Default.Run(ctx, func() error {
for _, pipe := range pipeline.BuildPipeline {
if err := middleware.Logging(
pipe.String(),
middleware.ErrHandler(pipe.Run),
middleware.DefaultInitialPadding,
)(ctx); err != nil {
return err
}
}
return nil
})
}
func setupBuildContext(ctx *context.Context, options buildOpts) *context.Context {
ctx.Parallelism = options.parallelism
log.Debugf("parallelism: %v", ctx.Parallelism)
ctx.Snapshot = options.snapshot
ctx.SkipValidate = ctx.Snapshot || options.skipValidate
ctx.SkipPostBuildHooks = options.skipPostHooks
ctx.RmDist = options.rmDist
ctx.SkipTokenCheck = true
// test only
ctx.Deprecated = options.deprecated
return ctx
}

72
cmd/build_test.go Normal file
View File

@ -0,0 +1,72 @@
package cmd
import (
"testing"
"github.com/goreleaser/goreleaser/pkg/config"
"github.com/goreleaser/goreleaser/pkg/context"
"github.com/stretchr/testify/require"
)
func TestBuild(t *testing.T) {
_, back := setup(t)
defer back()
var cmd = newBuildCmd()
cmd.cmd.SetArgs([]string{"--snapshot", "--timeout=1m", "--parallelism=2", "--deprecated"})
require.NoError(t, cmd.cmd.Execute())
}
func TestBuildInvalidConfig(t *testing.T) {
_, back := setup(t)
defer back()
createFile(t, "goreleaser.yml", "foo: bar")
var cmd = newBuildCmd()
cmd.cmd.SetArgs([]string{"--snapshot", "--timeout=1m", "--parallelism=2", "--deprecated"})
require.EqualError(t, cmd.cmd.Execute(), "yaml: unmarshal errors:\n line 1: field foo not found in type config.Project")
}
func TestBuildBrokenProject(t *testing.T) {
_, back := setup(t)
defer back()
createFile(t, "main.go", "not a valid go file")
var cmd = newBuildCmd()
cmd.cmd.SetArgs([]string{"--snapshot", "--timeout=1m", "--parallelism=2"})
require.EqualError(t, cmd.cmd.Execute(), "failed to parse dir: .: main.go:1:1: expected 'package', found not")
}
func TestBuildFlags(t *testing.T) {
var setup = func(opts buildOpts) *context.Context {
return setupBuildContext(context.New(config.Project{}), opts)
}
t.Run("snapshot", func(t *testing.T) {
var ctx = setup(buildOpts{
snapshot: true,
})
require.True(t, ctx.Snapshot)
require.True(t, ctx.SkipValidate)
require.True(t, ctx.SkipTokenCheck)
})
t.Run("skips", func(t *testing.T) {
var ctx = setup(buildOpts{
skipValidate: true,
skipPostHooks: true,
})
require.True(t, ctx.SkipValidate)
require.True(t, ctx.SkipPostBuildHooks)
require.True(t, ctx.SkipTokenCheck)
})
t.Run("parallelism", func(t *testing.T) {
require.Equal(t, 1, setup(buildOpts{
parallelism: 1,
}).Parallelism)
})
t.Run("rm dist", func(t *testing.T) {
require.True(t, setup(buildOpts{
rmDist: true,
}).RmDist)
})
}

View File

@ -34,6 +34,7 @@ type releaseOpts struct {
func newReleaseCmd() *releaseCmd {
var root = &releaseCmd{}
// nolint: dupl
var cmd = &cobra.Command{
Use: "release",
Aliases: []string{"r"},
@ -84,7 +85,7 @@ func releaseProject(options releaseOpts) (*context.Context, error) {
}
ctx, cancel := context.NewWithTimeout(cfg, options.timeout)
defer cancel()
setupContext(ctx, options)
setupReleaseContext(ctx, options)
return ctx, ctrlc.Default.Run(ctx, func() error {
for _, pipe := range pipeline.Pipeline {
if err := middleware.Logging(
@ -99,7 +100,7 @@ func releaseProject(options releaseOpts) (*context.Context, error) {
})
}
func setupContext(ctx *context.Context, options releaseOpts) *context.Context {
func setupReleaseContext(ctx *context.Context, options releaseOpts) *context.Context {
ctx.Parallelism = options.parallelism
log.Debugf("parallelism: %v", ctx.Parallelism)
ctx.ReleaseNotes = options.releaseNotes

View File

@ -36,7 +36,7 @@ func TestReleaseBrokenProject(t *testing.T) {
func TestReleaseFlags(t *testing.T) {
var setup = func(opts releaseOpts) *context.Context {
return setupContext(context.New(config.Project{}), opts)
return setupReleaseContext(context.New(config.Project{}), opts)
}
t.Run("snapshot", func(t *testing.T) {

View File

@ -70,6 +70,7 @@ func newRootCmd(version string, exit func(int)) *rootCmd {
cmd.PersistentFlags().BoolVar(&root.debug, "debug", false, "Enable debug mode")
cmd.AddCommand(
newBuildCmd().cmd,
newReleaseCmd().cmd,
newCheckCmd().cmd,
newInitCmd().cmd,

View File

@ -93,8 +93,10 @@ func runPipeOnBuild(ctx *context.Context, build config.Build) error {
if err := doBuild(ctx, build, *opts); err != nil {
return err
}
if err := runHook(ctx, *opts, build.Env, build.Hooks.Post); err != nil {
return errors.Wrap(err, "post hook failed")
if !ctx.SkipPostBuildHooks {
if err := runHook(ctx, *opts, build.Env, build.Hooks.Post); err != nil {
return errors.Wrap(err, "post hook failed")
}
}
return nil
})

View File

@ -89,7 +89,7 @@ func (Pipe) Run(ctx *context.Context) error {
}
func checkErrors(ctx *context.Context, noTokens, noTokenErrs bool, gitlabTokenErr, githubTokenErr, giteaTokenErr error) error {
if ctx.SkipPublish || ctx.Config.Release.Disable {
if ctx.SkipTokenCheck || ctx.SkipPublish || ctx.Config.Release.Disable {
return nil
}

View File

@ -34,9 +34,9 @@ type Piper interface {
Run(ctx *context.Context) error
}
// Pipeline contains all pipe implementations in order
// nolint: gochecknoglobals
var Pipeline = []Piper{
// BuildPipeline contains all build-related pipe implementations in order
// nolint:gochecknoglobals
var BuildPipeline = []Piper{
before.Pipe{}, // run global hooks before build
env.Pipe{}, // load and validate environment variables
git.Pipe{}, // get and validate git repo state
@ -47,12 +47,18 @@ var Pipeline = []Piper{
effectiveconfig.Pipe{}, // writes the actual config (with defaults et al set) to dist
changelog.Pipe{}, // builds the release changelog
build.Pipe{}, // build
archive.Pipe{}, // archive in tar.gz, zip or binary (which does no archiving at all)
sourcearchive.Pipe{}, // archive the source code using git-archive
nfpm.Pipe{}, // archive via fpm (deb, rpm) using "native" go impl
snapcraft.Pipe{}, // archive via snapcraft (snap)
checksums.Pipe{}, // checksums of the files
sign.Pipe{}, // sign artifacts
docker.Pipe{}, // create and push docker images
publish.Pipe{}, // publishes artifacts
}
// Pipeline contains all pipe implementations in order
// nolint: gochecknoglobals
var Pipeline = append(
BuildPipeline,
archive.Pipe{}, // archive in tar.gz, zip or binary (which does no archiving at all)
sourcearchive.Pipe{}, // archive the source code using git-archive
nfpm.Pipe{}, // archive via fpm (deb, rpm) using "native" go impl
snapcraft.Pipe{}, // archive via snapcraft (snap)
checksums.Pipe{}, // checksums of the files
sign.Pipe{}, // sign artifacts
docker.Pipe{}, // create and push docker images
publish.Pipe{}, // publishes artifacts
)

View File

@ -62,25 +62,27 @@ const (
// Context carries along some data through the pipes
type Context struct {
ctx.Context
Config config.Project
Env Env
Token string
TokenType TokenType
Git GitInfo
Artifacts artifact.Artifacts
ReleaseNotes string
ReleaseHeader string
ReleaseFooter string
Version string
Snapshot bool
SkipPublish bool
SkipSign bool
SkipValidate bool
RmDist bool
PreRelease bool
Deprecated bool
Parallelism int
Semver Semver
Config config.Project
Env Env
SkipTokenCheck bool
Token string
TokenType TokenType
Git GitInfo
Artifacts artifact.Artifacts
ReleaseNotes string
ReleaseHeader string
ReleaseFooter string
Version string
Snapshot bool
SkipPostBuildHooks bool
SkipPublish bool
SkipSign bool
SkipValidate bool
RmDist bool
PreRelease bool
Deprecated bool
Parallelism int
Semver Semver
}
// Semver represents a semantic version

View File

@ -105,7 +105,28 @@ The release should also look like this:
## Dry run
If you want to test everything before doing a release "for real", you can
use the `--skip-publish` flag, which will only build and package things:
use the following techniques.
### Build-only Mode
Build command will build the project
```console
$ goreleaser build
```
This can be useful as part of CI pipelines to verify the project builds
without errors for all build targets.
You can check the other options by running:
```console
$ goreleaser build --help
```
### Release Flags
Use the `--skip-publish` flag to skip publishing:
```console
$ goreleaser release --skip-publish