2020-04-28 02:42:44 +02:00
package cmd
import (
2023-09-16 22:01:20 +02:00
"fmt"
2021-01-13 19:21:04 +02:00
"runtime"
2020-04-28 02:42:44 +02:00
"time"
"github.com/caarlos0/ctrlc"
2022-06-22 02:11:15 +02:00
"github.com/caarlos0/log"
2024-05-26 20:02:57 +02:00
"github.com/goreleaser/goreleaser/v2/internal/logext"
"github.com/goreleaser/goreleaser/v2/internal/middleware/errhandler"
"github.com/goreleaser/goreleaser/v2/internal/middleware/logging"
"github.com/goreleaser/goreleaser/v2/internal/middleware/skip"
"github.com/goreleaser/goreleaser/v2/internal/pipe/git"
"github.com/goreleaser/goreleaser/v2/internal/pipeline"
"github.com/goreleaser/goreleaser/v2/internal/skips"
"github.com/goreleaser/goreleaser/v2/pkg/context"
2022-05-07 01:38:50 +02:00
"github.com/spf13/cobra"
2020-04-28 02:42:44 +02:00
)
type releaseCmd struct {
2022-05-07 01:38:50 +02:00
cmd * cobra . Command
2020-04-28 02:42:44 +02:00
opts releaseOpts
}
type releaseOpts struct {
2023-09-16 22:01:20 +02:00
config string
releaseNotesFile string
releaseNotesTmpl string
releaseHeaderFile string
releaseHeaderTmpl string
releaseFooterFile string
releaseFooterTmpl string
autoSnapshot bool
snapshot bool
2024-04-12 16:08:58 +02:00
draft bool
2023-09-16 22:01:20 +02:00
failFast bool
clean bool
deprecated bool
parallelism int
timeout time . Duration
skips [ ] string
2020-04-28 02:42:44 +02:00
}
func newReleaseCmd ( ) * releaseCmd {
2021-04-22 15:45:36 +02:00
root := & releaseCmd { }
2024-05-12 19:11:11 +02:00
//nolint:dupl
2022-05-07 01:38:50 +02:00
cmd := & cobra . Command {
2023-06-06 05:21:17 +02:00
Use : "release" ,
Aliases : [ ] string { "r" } ,
Short : "Releases the current project" ,
SilenceUsage : true ,
SilenceErrors : true ,
Args : cobra . NoArgs ,
ValidArgsFunction : cobra . NoFileCompletions ,
2023-09-16 22:01:20 +02:00
RunE : timedRunE ( "release" , func ( _ * cobra . Command , _ [ ] string ) error {
2020-04-28 02:42:44 +02:00
ctx , err := releaseProject ( root . opts )
if err != nil {
2022-06-22 06:48:11 +02:00
return err
2020-04-28 02:42:44 +02:00
}
2022-06-22 06:48:11 +02:00
deprecateWarn ( ctx )
2020-04-28 02:42:44 +02:00
return nil
2022-06-22 06:48:11 +02:00
} ) ,
2020-04-28 02:42:44 +02:00
}
cmd . Flags ( ) . StringVarP ( & root . opts . config , "config" , "f" , "" , "Load configuration from file" )
2023-06-06 05:21:17 +02:00
_ = cmd . MarkFlagFilename ( "config" , "yaml" , "yml" )
2022-01-30 16:28:00 +02:00
cmd . Flags ( ) . StringVar ( & root . opts . releaseNotesFile , "release-notes" , "" , "Load custom release notes from a markdown file (will skip GoReleaser changelog generation)" )
2023-06-06 05:21:17 +02:00
_ = cmd . MarkFlagFilename ( "release-notes" , "md" , "mkd" , "markdown" )
2021-05-22 02:07:47 +02:00
cmd . Flags ( ) . StringVar ( & root . opts . releaseHeaderFile , "release-header" , "" , "Load custom release notes header from a markdown file" )
2023-06-06 05:21:17 +02:00
_ = cmd . MarkFlagFilename ( "release-header" , "md" , "mkd" , "markdown" )
2021-05-22 02:07:47 +02:00
cmd . Flags ( ) . StringVar ( & root . opts . releaseFooterFile , "release-footer" , "" , "Load custom release notes footer from a markdown file" )
2023-06-06 05:21:17 +02:00
_ = cmd . MarkFlagFilename ( "release-footer" , "md" , "mkd" , "markdown" )
2021-05-22 02:07:47 +02:00
cmd . Flags ( ) . StringVar ( & root . opts . releaseNotesTmpl , "release-notes-tmpl" , "" , "Load custom release notes from a templated markdown file (overrides --release-notes)" )
2023-06-06 05:21:17 +02:00
_ = cmd . MarkFlagFilename ( "release-notes-tmpl" , "md" , "mkd" , "markdown" )
2021-05-22 02:07:47 +02:00
cmd . Flags ( ) . StringVar ( & root . opts . releaseHeaderTmpl , "release-header-tmpl" , "" , "Load custom release notes header from a templated markdown file (overrides --release-header)" )
2023-06-06 05:21:17 +02:00
_ = cmd . MarkFlagFilename ( "release-header-tmpl" , "md" , "mkd" , "markdown" )
2021-05-22 02:07:47 +02:00
cmd . Flags ( ) . StringVar ( & root . opts . releaseFooterTmpl , "release-footer-tmpl" , "" , "Load custom release notes footer from a templated markdown file (overrides --release-footer)" )
2023-06-06 05:21:17 +02:00
_ = cmd . MarkFlagFilename ( "release-footer-tmpl" , "md" , "mkd" , "markdown" )
2022-12-29 15:41:59 +02:00
cmd . Flags ( ) . BoolVar ( & root . opts . autoSnapshot , "auto-snapshot" , false , "Automatically sets --snapshot if the repository is dirty" )
2023-09-16 22:01:20 +02:00
cmd . Flags ( ) . BoolVar ( & root . opts . snapshot , "snapshot" , false , "Generate an unversioned snapshot release, skipping all validations and without publishing any artifacts (implies --skip=announce,publish,validate)" )
2024-04-12 16:08:58 +02:00
cmd . Flags ( ) . BoolVar ( & root . opts . draft , "draft" , false , "Whether to set the release to draft. Overrides release.draft in the configuration file" )
2023-06-20 14:33:59 +02:00
cmd . Flags ( ) . BoolVar ( & root . opts . failFast , "fail-fast" , false , "Whether to abort the release publishing on the first error" )
2024-04-01 15:01:56 +02:00
cmd . Flags ( ) . BoolVar ( & root . opts . clean , "clean" , false , "Removes the 'dist' directory" )
2021-04-22 15:45:36 +02:00
cmd . Flags ( ) . IntVarP ( & root . opts . parallelism , "parallelism" , "p" , 0 , "Amount tasks to run concurrently (default: number of CPUs)" )
2023-06-06 05:21:17 +02:00
_ = cmd . RegisterFlagCompletionFunc ( "parallelism" , cobra . NoFileCompletions )
2020-04-28 02:42:44 +02:00
cmd . Flags ( ) . DurationVar ( & root . opts . timeout , "timeout" , 30 * time . Minute , "Timeout to the entire release process" )
2023-06-06 05:21:17 +02:00
_ = cmd . RegisterFlagCompletionFunc ( "timeout" , cobra . NoFileCompletions )
2020-04-28 02:42:44 +02:00
cmd . Flags ( ) . BoolVar ( & root . opts . deprecated , "deprecated" , false , "Force print the deprecation message - tests only" )
_ = cmd . Flags ( ) . MarkHidden ( "deprecated" )
2023-09-16 22:01:20 +02:00
cmd . Flags ( ) . StringSliceVar (
& root . opts . skips ,
"skip" ,
nil ,
fmt . Sprintf ( "Skip the given options (valid options are %s)" , skips . Release . String ( ) ) ,
)
_ = cmd . RegisterFlagCompletionFunc ( "skip" , func ( _ * cobra . Command , _ [ ] string , toComplete string ) ( [ ] string , cobra . ShellCompDirective ) {
2023-09-17 05:00:15 +02:00
return skips . Release . Complete ( toComplete ) , cobra . ShellCompDirectiveDefault
2023-09-16 22:01:20 +02:00
} )
2020-04-28 02:42:44 +02:00
root . cmd = cmd
return root
}
func releaseProject ( options releaseOpts ) ( * context . Context , error ) {
cfg , err := loadConfig ( options . config )
if err != nil {
return nil , err
}
ctx , cancel := context . NewWithTimeout ( cfg , options . timeout )
defer cancel ( )
2023-09-16 22:01:20 +02:00
if err := setupReleaseContext ( ctx , options ) ; err != nil {
return nil , err
}
2020-04-28 02:42:44 +02:00
return ctx , ctrlc . Default . Run ( ctx , func ( ) error {
for _ , pipe := range pipeline . Pipeline {
2021-09-18 15:21:29 +02:00
if err := skip . Maybe (
pipe ,
logging . Log (
pipe . String ( ) ,
errhandler . Handle ( pipe . Run ) ,
) ,
2020-04-28 02:42:44 +02:00
) ( ctx ) ; err != nil {
return err
}
}
return nil
} )
}
2023-09-16 22:01:20 +02:00
func setupReleaseContext ( ctx * context . Context , options releaseOpts ) error {
2023-12-29 20:22:03 +02:00
ctx . Action = context . ActionRelease
2023-01-29 05:24:11 +02: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-02 22:16:41 +02:00
ctx . Parallelism = runtime . GOMAXPROCS ( 0 )
2021-04-22 15:45:36 +02:00
if options . parallelism > 0 {
ctx . Parallelism = options . parallelism
}
2020-04-28 02:42:44 +02:00
log . Debugf ( "parallelism: %v" , ctx . Parallelism )
2021-05-22 02:07:47 +02:00
ctx . ReleaseNotesFile = options . releaseNotesFile
ctx . ReleaseNotesTmpl = options . releaseNotesTmpl
ctx . ReleaseHeaderFile = options . releaseHeaderFile
ctx . ReleaseHeaderTmpl = options . releaseHeaderTmpl
ctx . ReleaseFooterFile = options . releaseFooterFile
ctx . ReleaseFooterTmpl = options . releaseFooterTmpl
2020-04-28 02:42:44 +02:00
ctx . Snapshot = options . snapshot
2023-06-20 14:33:59 +02:00
ctx . FailFast = options . failFast
2024-05-25 20:04:08 +02:00
ctx . Clean = options . clean
2022-04-12 13:35:19 +02:00
if options . autoSnapshot && git . CheckDirty ( ctx ) != nil {
2022-12-29 15:41:59 +02:00
log . Info ( "git repository is dirty and --auto-snapshot is set, implying --snapshot" )
2021-06-05 04:09:12 +02:00
ctx . Snapshot = true
}
2020-04-28 02:42:44 +02:00
2024-05-15 03:47:51 +02:00
if options . draft {
ctx . Config . Release . Draft = true
}
2024-04-12 16:08:58 +02:00
2023-09-16 22:01:20 +02:00
if err := skips . SetRelease ( ctx , options . skips ... ) ; err != nil {
return err
}
if ctx . Snapshot {
skips . Set ( ctx , skips . Publish , skips . Announce , skips . Validate )
}
if skips . Any ( ctx , skips . Publish ) {
skips . Set ( ctx , skips . Announce )
}
2023-09-20 17:03:15 +02:00
2023-09-23 06:28:03 +02:00
if skips . Any ( ctx , skips . Release ... ) {
log . Warnf (
logext . Warning ( "skipping %s..." ) ,
skips . String ( ctx ) ,
)
}
2023-09-16 22:01:20 +02:00
return nil
2020-04-28 02:42:44 +02:00
}