2020-05-15 15:19:20 +01:00
package cmd
import (
2021-04-21 16:36:53 -03:00
"fmt"
2022-02-05 18:19:12 -03:00
"path/filepath"
2021-01-13 14:21:04 -03:00
"runtime"
2022-06-28 23:37:38 -03:00
"strings"
2020-05-15 15:19:20 +01:00
"time"
"github.com/caarlos0/ctrlc"
2022-06-21 21:11:15 -03:00
"github.com/caarlos0/log"
2022-02-05 18:19:12 -03:00
"github.com/goreleaser/goreleaser/internal/artifact"
2023-01-29 00:24:11 -03:00
"github.com/goreleaser/goreleaser/internal/deprecate"
2022-02-05 18:19:12 -03:00
"github.com/goreleaser/goreleaser/internal/gio"
2023-09-23 05:36:31 +00:00
"github.com/goreleaser/goreleaser/internal/logext"
2021-09-18 10:21:29 -03:00
"github.com/goreleaser/goreleaser/internal/middleware/errhandler"
"github.com/goreleaser/goreleaser/internal/middleware/logging"
"github.com/goreleaser/goreleaser/internal/middleware/skip"
2020-05-15 15:19:20 +01:00
"github.com/goreleaser/goreleaser/internal/pipeline"
2023-09-16 17:01:20 -03:00
"github.com/goreleaser/goreleaser/internal/skips"
2021-04-21 16:36:53 -03:00
"github.com/goreleaser/goreleaser/pkg/config"
2020-05-15 15:19:20 +01:00
"github.com/goreleaser/goreleaser/pkg/context"
2022-05-06 20:38:50 -03:00
"github.com/spf13/cobra"
2020-05-15 15:19:20 +01:00
)
type buildCmd struct {
2022-05-06 20:38:50 -03:00
cmd * cobra . Command
2020-05-15 15:19:20 +01:00
opts buildOpts
}
type buildOpts struct {
2023-09-16 17:01:20 -03:00
config string
ids [ ] string
snapshot bool
clean bool
deprecated bool
parallelism int
timeout time . Duration
singleTarget bool
output string
skips [ ] string
2023-08-04 14:47:54 +00:00
// Deprecated: use clean instead.
rmDist bool
2023-09-16 17:01:20 -03:00
// Deprecated: use skip instead.
skipValidate bool
// Deprecated: use skip instead.
skipBefore bool
// Deprecated: use skip instead.
skipPostHooks bool
2020-05-15 15:19:20 +01:00
}
func newBuildCmd ( ) * buildCmd {
2021-04-21 16:36:53 -03:00
root := & buildCmd { }
2020-05-15 15:19:20 +01:00
// nolint: dupl
2022-05-06 20:38:50 -03:00
cmd := & cobra . Command {
2021-04-25 14:20:49 -03:00
Use : "build" ,
Aliases : [ ] string { "b" } ,
Short : "Builds the current project" ,
2022-04-16 15:31:43 -03:00
Long : ` The ` + "`goreleaser build`" + ` command is analogous to the ` + "`go build`" + ` command , in the sense it only builds binaries .
2021-04-22 10:58:58 -03:00
2022-04-20 00:34:24 +02:00
Its intended usage is , for example , within Makefiles to avoid setting up ldflags and etc in several places . That way , the GoReleaser config becomes the source of truth for how the binaries should be built .
2021-04-22 10:58:58 -03:00
2022-04-16 15:31:43 -03:00
It also allows you to generate a local build for your current machine only using the ` + " ` -- single - target ` " + ` option , and specific build IDs using the ` + " ` -- id ` " + ` option in case you have more than one .
2021-11-30 10:41:05 -03:00
2022-04-20 00:34:24 +02:00
When using ` + " ` -- single - target ` " + ` , the ` + " ` GOOS ` " + ` and ` + " ` GOARCH ` " + ` environment variables are used to determine the target , defaulting to the current machine target if not set .
2021-04-22 10:58:58 -03:00
` ,
2023-06-06 06:21:17 +03:00
SilenceUsage : true ,
SilenceErrors : true ,
Args : cobra . NoArgs ,
ValidArgsFunction : cobra . NoFileCompletions ,
chore: fix all existing lint issues (#4637)
The PR fixes lint issues, pins `golangci-lint` to `v1.56.2`, disables
`only-new-issues`, and enables `fail-on-issues` in the lint workflow.
After this, all new lint issues will fail CI.
The full log of fixed lint issues:
```sh
❯ golangci-lint run
cmd/docs.go:24:14: unused-parameter: parameter 'cmd' seems to be unused, consider removing or renaming it as _ (revive)
RunE: func(cmd *cobra.Command, args []string) error {
^
cmd/man.go:26:14: unused-parameter: parameter 'cmd' seems to be unused, consider removing or renaming it as _ (revive)
RunE: func(cmd *cobra.Command, args []string) error {
^
cmd/healthcheck.go:36:14: unused-parameter: parameter 'cmd' seems to be unused, consider removing or renaming it as _ (revive)
RunE: func(cmd *cobra.Command, args []string) error {
^
cmd/build.go:72:33: unused-parameter: parameter 'cmd' seems to be unused, consider removing or renaming it as _ (revive)
RunE: timedRunE("build", func(cmd *cobra.Command, args []string) error {
^
cmd/schema.go:29:14: unused-parameter: parameter 'cmd' seems to be unused, consider removing or renaming it as _ (revive)
RunE: func(cmd *cobra.Command, args []string) error {
^
internal/pipe/nix/nix_test.go:547:5: error-is-as: second argument to require.ErrorAs should not be *error (testifylint)
require.ErrorAs(t, err, &tt.expectDefaultErrorIs)
^
internal/pipe/nix/nix_test.go:556:5: error-is-as: second argument to require.ErrorAs should not be *error (testifylint)
require.ErrorAs(t, err, &tt.expectRunErrorIs)
^
internal/pipe/nix/nix_test.go:567:5: error-is-as: second argument to require.ErrorAs should not be *error (testifylint)
require.ErrorAs(t, err, &tt.expectPublishErrorIs)
^
internal/pipe/winget/winget_test.go:709:5: error-is-as: second argument to require.ErrorAs should not be *error (testifylint)
require.ErrorAs(t, err, &tt.expectPublishErrorIs)
^
internal/pipe/winget/winget_test.go:728:5: error-is-as: second argument to require.ErrorAs should not be *error (testifylint)
require.ErrorAs(t, err, &tt.expectPublishErrorIs)
^
internal/pipe/docker/docker_test.go:56:29: unused-parameter: parameter 'use' seems to be unused, consider removing or renaming it as _ (revive)
return func(t *testing.T, use string) {
^
internal/gio/safe_test.go:23:4: go-require: require must only be used in the goroutine running the test function (testifylint)
require.Equal(t, 1, s)
^
internal/gio/safe_test.go:24:4: go-require: require must only be used in the goroutine running the test function (testifylint)
require.NoError(t, err)
^
internal/pipe/gomod/gomod_proxy_test.go:126:3: useless-assert: asserting of the same variable (testifylint)
require.Equal(t, ctx.ModulePath, ctx.ModulePath)
^
internal/pipe/gomod/gomod_proxy_test.go:152:3: useless-assert: asserting of the same variable (testifylint)
require.Equal(t, ctx.ModulePath, ctx.ModulePath)
^
internal/pipe/gomod/gomod_proxy_test.go:178:3: useless-assert: asserting of the same variable (testifylint)
require.Equal(t, ctx.ModulePath, ctx.ModulePath)
^
internal/pipe/gomod/gomod_proxy_test.go:239:3: useless-assert: asserting of the same variable (testifylint)
require.Equal(t, ctx.ModulePath, ctx.ModulePath)
^
internal/artifact/artifact_test.go:638:46: unused-parameter: parameter 'a' seems to be unused, consider removing or renaming it as _ (revive)
require.EqualError(t, artifacts.Visit(func(a *Artifact) error {
^
internal/pipe/milestone/milestone.go:79: File is not `gofumpt`-ed (gofumpt)
internal/http/http_test.go:217:19: unused-parameter: parameter 'k' seems to be unused, consider removing or renaming it as _ (revive)
assetOpen = func(k string, a *artifact.Artifact) (*asset, error) {
^
internal/middleware/logging/logging_test.go:12:37: unused-parameter: parameter 'ctx' seems to be unused, consider removing or renaming it as _ (revive)
require.NoError(t, Log("foo", func(ctx *context.Context) error {
^
internal/middleware/logging/logging_test.go:16:40: unused-parameter: parameter 'ctx' seems to be unused, consider removing or renaming it as _ (revive)
require.NoError(t, PadLog("foo", func(ctx *context.Context) error {
^
internal/pipe/chocolatey/chocolatey_test.go:277:15: unused-parameter: parameter 'cmd' seems to be unused, consider removing or renaming it as _ (revive)
exec: func(cmd string, args ...string) ([]byte, error) {
^
internal/client/gitlab.go:325: File is not `gofumpt`-ed (gofumpt)
internal/pipe/linkedin/client_test.go:58:77: unused-parameter: parameter 'req' seems to be unused, consider removing or renaming it as _ (revive)
server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
^
internal/middleware/errhandler/error_test.go:15:34: unused-parameter: parameter 'ctx' seems to be unused, consider removing or renaming it as _ (revive)
require.NoError(t, Handle(func(ctx *context.Context) error {
^
internal/middleware/errhandler/error_test.go:21:34: unused-parameter: parameter 'ctx' seems to be unused, consider removing or renaming it as _ (revive)
require.NoError(t, Handle(func(ctx *context.Context) error {
^
internal/middleware/errhandler/error_test.go:27:32: unused-parameter: parameter 'ctx' seems to be unused, consider removing or renaming it as _ (revive)
require.Error(t, Handle(func(ctx *context.Context) error {
^
internal/middleware/errhandler/error_test.go:36:37: unused-parameter: parameter 'ctx' seems to be unused, consider removing or renaming it as _ (revive)
require.NoError(t, memo.Wrap(func(ctx *context.Context) error {
^
internal/middleware/errhandler/error_test.go:42:37: unused-parameter: parameter 'ctx' seems to be unused, consider removing or renaming it as _ (revive)
require.NoError(t, memo.Wrap(func(ctx *context.Context) error {
^
internal/middleware/errhandler/error_test.go:48:37: unused-parameter: parameter 'ctx' seems to be unused, consider removing or renaming it as _ (revive)
require.NoError(t, memo.Wrap(func(ctx *context.Context) error {
^
internal/pipe/ko/ko.go:175:29: unused-parameter: parameter 'ctx' seems to be unused, consider removing or renaming it as _ (revive)
build.WithBaseImages(func(ctx stdctx.Context, s string) (name.Reference, build.Result, error) {
^
```
2024-02-19 13:49:39 +02:00
RunE : timedRunE ( "build" , func ( _ * cobra . Command , _ [ ] string ) error {
2020-05-15 15:19:20 +01:00
ctx , err := buildProject ( root . opts )
if err != nil {
2022-06-22 01:48:11 -03:00
return err
2020-05-15 15:19:20 +01:00
}
2022-06-22 01:48:11 -03:00
deprecateWarn ( ctx )
2020-05-15 15:19:20 +01:00
return nil
2022-06-22 01:48:11 -03:00
} ) ,
2020-05-15 15:19:20 +01:00
}
cmd . Flags ( ) . StringVarP ( & root . opts . config , "config" , "f" , "" , "Load configuration from file" )
2023-06-06 06:21:17 +03:00
_ = cmd . MarkFlagFilename ( "config" , "yaml" , "yml" )
2021-04-22 10:58:58 -03:00
cmd . Flags ( ) . BoolVar ( & root . opts . snapshot , "snapshot" , false , "Generate an unversioned snapshot build, skipping all validations" )
2020-05-15 15:19:20 +01:00
cmd . Flags ( ) . BoolVar ( & root . opts . skipValidate , "skip-validate" , false , "Skips several sanity checks" )
2022-06-22 21:56:53 -03:00
cmd . Flags ( ) . BoolVar ( & root . opts . skipBefore , "skip-before" , false , "Skips global before hooks" )
2020-05-15 15:19:20 +01:00
cmd . Flags ( ) . BoolVar ( & root . opts . skipPostHooks , "skip-post-hooks" , false , "Skips all post-build hooks" )
2023-01-20 23:47:08 -03:00
cmd . Flags ( ) . BoolVar ( & root . opts . clean , "clean" , false , "Remove the dist folder before building" )
2023-01-29 00:24:11 -03:00
cmd . Flags ( ) . BoolVar ( & root . opts . rmDist , "rm-dist" , false , "Remove the dist folder before building" )
2021-04-22 10:45:36 -03:00
cmd . Flags ( ) . IntVarP ( & root . opts . parallelism , "parallelism" , "p" , 0 , "Amount tasks to run concurrently (default: number of CPUs)" )
2023-06-06 06:21:17 +03:00
_ = cmd . RegisterFlagCompletionFunc ( "parallelism" , cobra . NoFileCompletions )
2020-05-15 15:19:20 +01:00
cmd . Flags ( ) . DurationVar ( & root . opts . timeout , "timeout" , 30 * time . Minute , "Timeout to the entire build process" )
2023-06-06 06:21:17 +03:00
_ = cmd . RegisterFlagCompletionFunc ( "timeout" , cobra . NoFileCompletions )
2022-07-28 22:29:01 -03:00
cmd . Flags ( ) . BoolVar ( & root . opts . singleTarget , "single-target" , false , "Builds only for current GOOS and GOARCH, regardless of what's set in the configuration file" )
2022-06-28 23:37:38 -03:00
cmd . Flags ( ) . StringArrayVar ( & root . opts . ids , "id" , nil , "Builds only the specified build ids" )
2023-09-16 17:01:20 -03:00
_ = cmd . RegisterFlagCompletionFunc ( "id" , func ( _ * cobra . Command , _ [ ] string , _ string ) ( [ ] string , cobra . ShellCompDirective ) {
// TODO: improve this
2023-06-06 06:21:17 +03:00
cfg , err := loadConfig ( root . opts . config )
if err != nil {
return nil , cobra . ShellCompDirectiveNoFileComp
}
ids := make ( [ ] string , 0 , len ( cfg . Builds ) )
for _ , build := range cfg . Builds {
ids = append ( ids , build . ID )
}
return ids , cobra . ShellCompDirectiveNoFileComp
} )
2020-05-15 15:19:20 +01:00
cmd . Flags ( ) . BoolVar ( & root . opts . deprecated , "deprecated" , false , "Force print the deprecation message - tests only" )
2022-07-28 22:29:01 -03:00
cmd . Flags ( ) . StringVarP ( & root . opts . output , "output" , "o" , "" , "Copy the binary to the path after the build. Only taken into account when using --single-target and a single id (either with --id or if configuration only has one build)" )
2023-06-06 06:21:17 +03:00
_ = cmd . MarkFlagFilename ( "output" , "" )
2023-01-20 23:47:08 -03:00
_ = cmd . Flags ( ) . MarkHidden ( "rm-dist" )
2020-05-15 15:19:20 +01:00
_ = cmd . Flags ( ) . MarkHidden ( "deprecated" )
2023-09-16 17:01:20 -03:00
for _ , f := range [ ] string {
"post-hooks" ,
"before" ,
"validate" ,
} {
_ = cmd . Flags ( ) . MarkHidden ( "skip-" + f )
_ = cmd . Flags ( ) . MarkDeprecated ( "skip-" + f , fmt . Sprintf ( "please use --skip=%s instead" , f ) )
}
cmd . Flags ( ) . StringSliceVar (
& root . opts . skips ,
"skip" ,
nil ,
2023-11-27 18:29:50 -03:00
fmt . Sprintf ( "Skip the given options (valid options are: %s)" , skips . Build . String ( ) ) ,
2023-09-16 17:01:20 -03:00
)
_ = cmd . RegisterFlagCompletionFunc ( "skip" , func ( _ * cobra . Command , _ [ ] string , toComplete string ) ( [ ] string , cobra . ShellCompDirective ) {
2023-09-17 03:00:15 +00:00
return skips . Build . Complete ( toComplete ) , cobra . ShellCompDirectiveDefault
2023-09-16 17:01:20 -03:00
} )
2020-05-15 15:19:20 +01:00
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 ( )
2021-04-21 16:36:53 -03:00
if err := setupBuildContext ( ctx , options ) ; err != nil {
return nil , err
}
2020-05-15 15:19:20 +01:00
return ctx , ctrlc . Default . Run ( ctx , func ( ) error {
2022-02-05 18:19:12 -03:00
for _ , pipe := range setupPipeline ( ctx , options ) {
2021-09-18 10:21:29 -03:00
if err := skip . Maybe (
pipe ,
logging . Log (
pipe . String ( ) ,
errhandler . Handle ( pipe . Run ) ,
) ,
2020-05-15 15:19:20 +01:00
) ( ctx ) ; err != nil {
return err
}
}
return nil
} )
}
2022-02-05 18:19:12 -03:00
func setupPipeline ( ctx * context . Context , options buildOpts ) [ ] pipeline . Piper {
2022-06-28 23:37:38 -03:00
if options . output != "" && options . singleTarget && ( len ( options . ids ) > 0 || len ( ctx . Config . Builds ) == 1 ) {
2022-02-05 18:19:12 -03:00
return append ( pipeline . BuildCmdPipeline , withOutputPipe { options . output } )
}
return pipeline . BuildCmdPipeline
}
2021-04-21 16:36:53 -03:00
func setupBuildContext ( ctx * context . Context , options buildOpts ) error {
2023-12-29 15:22:03 -03:00
ctx . Action = context . ActionBuild
2023-01-29 00:24:11 -03:00
ctx . Deprecated = options . deprecated // test only
fix: set parallelism to match Linux container CPU (#3901)
<!--
Hi, thanks for contributing!
Please make sure you read our CONTRIBUTING guide.
Also, add tests and the respective documentation changes as well.
-->
Currently Goreleaser uses `runtime.NumCPU()` as the default value if
`--parallelism` is not set.
However, this will get the number of CPUs on the host even when
Goreleaser is run in a container with a limit on the maximum number of
CPUs that can be used (typically in a Kubernetes pod).
Actually, `docker run --cpus=1 goreleaser/goreleaser --debug` shows
`parallelism: 4` on my machine.
This behavior causes CPU throttling, which increases execution time and,
in the worst case, terminates with an error.
I ran into this problem with Jenkins where the agent runs on pod
([Kubernetes plugin for
Jenkins](https://plugins.jenkins.io/kubernetes/)).
This commit introduces
[automaxprocs](https://github.com/uber-go/automaxprocs) to fix this
issue.
This library sets `GOMAXPROCS` to match Linux container CPU quota.
I have also looked for a library that can get CPU quota more directly,
but this seems to be the best I could find.
The reason it is set in a different notation from the automaxprocs
README is to prevent logs from being displayed
([comment](https://github.com/uber-go/automaxprocs/issues/18#issuecomment-511330567)).
I would have liked to write a test, but this change is dependent on the
number of CPUs in the execution environment, so I could not.
Instead, I wrote a Dockerfile for testing
```Dockerfile
FROM golang:1.20.2
WORKDIR /go/app
RUN sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d -b /usr/local/bin
COPY . .
RUN task build
```
and confirmed built binary shows expected parallelism by following
commands:
```sh
docker build --file Dockerfile.test . -t test-goreleaser
docker run --cpus=1 test-goreleaser ./goreleaser build --snapshot --debug # parallelism: 1
docker run test-goreleaser ./goreleaser build --snapshot --debug # parallelism: 4
```
I also ran the built binary on my Macbook and it was fine.
2023-04-03 05:16:41 +09:00
ctx . Parallelism = runtime . GOMAXPROCS ( 0 )
2021-04-22 10:45:36 -03:00
if options . parallelism > 0 {
ctx . Parallelism = options . parallelism
}
2020-05-15 15:19:20 +01:00
log . Debugf ( "parallelism: %v" , ctx . Parallelism )
ctx . Snapshot = options . snapshot
2023-11-27 18:29:50 -03:00
2023-09-16 17:01:20 -03:00
if err := skips . SetBuild ( ctx , options . skips ... ) ; err != nil {
return err
}
if options . skipValidate {
skips . Set ( ctx , skips . Validate )
deprecate . NoticeCustom ( ctx , "-skip" , "--skip-validate was deprecated in favor of --skip=validate, check {{ .URL }} for more details" )
}
if options . skipBefore {
skips . Set ( ctx , skips . Before )
deprecate . NoticeCustom ( ctx , "-skip" , "--skip-before was deprecated in favor of --skip=before, check {{ .URL }} for more details" )
}
if options . skipPostHooks {
skips . Set ( ctx , skips . PostBuildHooks )
deprecate . NoticeCustom ( ctx , "-skip" , "--skip-post-hooks was deprecated in favor of --skip=post-hooks, check {{ .URL }} for more details" )
}
2023-01-29 00:24:11 -03:00
if options . rmDist {
deprecate . NoticeCustom ( ctx , "-rm-dist" , "--rm-dist was deprecated in favor of --clean, check {{ .URL }} for more details" )
}
2020-05-15 15:19:20 +01:00
2023-09-16 17:01:20 -03:00
if ctx . Snapshot {
skips . Set ( ctx , skips . Validate )
}
ctx . SkipTokenCheck = true
ctx . Clean = options . clean || options . rmDist
2021-04-21 16:36:53 -03:00
if options . singleTarget {
2023-11-27 18:29:50 -03:00
ctx . Partial = true
2021-04-21 16:36:53 -03:00
}
2022-06-28 23:37:38 -03:00
if len ( options . ids ) > 0 {
if err := setupBuildID ( ctx , options . ids ) ; err != nil {
2021-04-21 16:36:53 -03:00
return err
}
}
2023-09-23 05:36:31 +00:00
if skips . Any ( ctx , skips . Build ... ) {
log . Warnf (
logext . Warning ( "skipping %s..." ) ,
skips . String ( ctx ) ,
)
}
2021-04-21 16:36:53 -03:00
return nil
}
2022-06-28 23:37:38 -03:00
func setupBuildID ( ctx * context . Context , ids [ ] string ) error {
2021-04-21 16:36:53 -03:00
if len ( ctx . Config . Builds ) < 2 {
log . Warn ( "single build in config, '--id' ignored" )
return nil
}
var keep [ ] config . Build
for _ , build := range ctx . Config . Builds {
2022-06-28 23:37:38 -03:00
for _ , id := range ids {
if build . ID == id {
keep = append ( keep , build )
break
}
2021-04-21 16:36:53 -03:00
}
}
if len ( keep ) == 0 {
2022-06-28 23:37:38 -03:00
return fmt . Errorf ( "no builds with ids %s" , strings . Join ( ids , ", " ) )
2021-04-21 16:36:53 -03:00
}
ctx . Config . Builds = keep
return nil
2020-05-15 15:19:20 +01:00
}
2022-02-05 18:19:12 -03:00
// 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 {
2023-05-12 15:50:12 -03:00
bins := ctx . Artifacts . Filter ( artifact . ByType ( artifact . Binary ) ) . List ( )
if len ( bins ) == 0 {
return fmt . Errorf ( "no binary found" )
}
path := bins [ 0 ] . Path
2022-02-05 18:19:12 -03:00
out := w . output
2022-02-19 15:10:37 -03:00
if out == "." {
2022-02-05 18:19:12 -03:00
out = filepath . Base ( path )
}
return gio . Copy ( path , out )
}