github.com/goreleaser/goreleaser@v1.25.1/cmd/release.go (about)

     1  package cmd
     2  
     3  import (
     4  	"fmt"
     5  	"runtime"
     6  	"time"
     7  
     8  	"github.com/caarlos0/ctrlc"
     9  	"github.com/caarlos0/log"
    10  	"github.com/goreleaser/goreleaser/internal/deprecate"
    11  	"github.com/goreleaser/goreleaser/internal/logext"
    12  	"github.com/goreleaser/goreleaser/internal/middleware/errhandler"
    13  	"github.com/goreleaser/goreleaser/internal/middleware/logging"
    14  	"github.com/goreleaser/goreleaser/internal/middleware/skip"
    15  	"github.com/goreleaser/goreleaser/internal/pipe/git"
    16  	"github.com/goreleaser/goreleaser/internal/pipeline"
    17  	"github.com/goreleaser/goreleaser/internal/skips"
    18  	"github.com/goreleaser/goreleaser/pkg/context"
    19  	"github.com/spf13/cobra"
    20  )
    21  
    22  type releaseCmd struct {
    23  	cmd  *cobra.Command
    24  	opts releaseOpts
    25  }
    26  
    27  type releaseOpts struct {
    28  	config            string
    29  	releaseNotesFile  string
    30  	releaseNotesTmpl  string
    31  	releaseHeaderFile string
    32  	releaseHeaderTmpl string
    33  	releaseFooterFile string
    34  	releaseFooterTmpl string
    35  	autoSnapshot      bool
    36  	snapshot          bool
    37  	failFast          bool
    38  	clean             bool
    39  	deprecated        bool
    40  	parallelism       int
    41  	timeout           time.Duration
    42  	skips             []string
    43  
    44  	// Deprecated: use clean instead.
    45  	rmDist bool
    46  	// Deprecated: use skips instead.
    47  	skipPublish bool
    48  	// Deprecated: use skips instead.
    49  	skipSign bool
    50  	// Deprecated: use skips instead.
    51  	skipValidate bool
    52  	// Deprecated: use skips instead.
    53  	skipAnnounce bool
    54  	// Deprecated: use skips instead.
    55  	skipSBOMCataloging bool
    56  	// Deprecated: use skips instead.
    57  	skipDocker bool
    58  	// Deprecated: use skips instead.
    59  	skipKo bool
    60  	// Deprecated: use skips instead.
    61  	skipBefore bool
    62  }
    63  
    64  func newReleaseCmd() *releaseCmd {
    65  	root := &releaseCmd{}
    66  	// nolint: dupl
    67  	cmd := &cobra.Command{
    68  		Use:               "release",
    69  		Aliases:           []string{"r"},
    70  		Short:             "Releases the current project",
    71  		SilenceUsage:      true,
    72  		SilenceErrors:     true,
    73  		Args:              cobra.NoArgs,
    74  		ValidArgsFunction: cobra.NoFileCompletions,
    75  		RunE: timedRunE("release", func(_ *cobra.Command, _ []string) error {
    76  			ctx, err := releaseProject(root.opts)
    77  			if err != nil {
    78  				return err
    79  			}
    80  			deprecateWarn(ctx)
    81  			return nil
    82  		}),
    83  	}
    84  
    85  	cmd.Flags().StringVarP(&root.opts.config, "config", "f", "", "Load configuration from file")
    86  	_ = cmd.MarkFlagFilename("config", "yaml", "yml")
    87  	cmd.Flags().StringVar(&root.opts.releaseNotesFile, "release-notes", "", "Load custom release notes from a markdown file (will skip GoReleaser changelog generation)")
    88  	_ = cmd.MarkFlagFilename("release-notes", "md", "mkd", "markdown")
    89  	cmd.Flags().StringVar(&root.opts.releaseHeaderFile, "release-header", "", "Load custom release notes header from a markdown file")
    90  	_ = cmd.MarkFlagFilename("release-header", "md", "mkd", "markdown")
    91  	cmd.Flags().StringVar(&root.opts.releaseFooterFile, "release-footer", "", "Load custom release notes footer from a markdown file")
    92  	_ = cmd.MarkFlagFilename("release-footer", "md", "mkd", "markdown")
    93  	cmd.Flags().StringVar(&root.opts.releaseNotesTmpl, "release-notes-tmpl", "", "Load custom release notes from a templated markdown file (overrides --release-notes)")
    94  	_ = cmd.MarkFlagFilename("release-notes-tmpl", "md", "mkd", "markdown")
    95  	cmd.Flags().StringVar(&root.opts.releaseHeaderTmpl, "release-header-tmpl", "", "Load custom release notes header from a templated markdown file (overrides --release-header)")
    96  	_ = cmd.MarkFlagFilename("release-header-tmpl", "md", "mkd", "markdown")
    97  	cmd.Flags().StringVar(&root.opts.releaseFooterTmpl, "release-footer-tmpl", "", "Load custom release notes footer from a templated markdown file (overrides --release-footer)")
    98  	_ = cmd.MarkFlagFilename("release-footer-tmpl", "md", "mkd", "markdown")
    99  	cmd.Flags().BoolVar(&root.opts.autoSnapshot, "auto-snapshot", false, "Automatically sets --snapshot if the repository is dirty")
   100  	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)")
   101  	cmd.Flags().BoolVar(&root.opts.failFast, "fail-fast", false, "Whether to abort the release publishing on the first error")
   102  	cmd.Flags().BoolVar(&root.opts.skipPublish, "skip-publish", false, "Skips publishing artifacts (implies --skip=announce)")
   103  	cmd.Flags().BoolVar(&root.opts.skipAnnounce, "skip-announce", false, "Skips announcing releases (implies --skip=validate)")
   104  	cmd.Flags().BoolVar(&root.opts.skipSign, "skip-sign", false, "Skips signing artifacts")
   105  	cmd.Flags().BoolVar(&root.opts.skipSBOMCataloging, "skip-sbom", false, "Skips cataloging artifacts")
   106  	cmd.Flags().BoolVar(&root.opts.skipDocker, "skip-docker", false, "Skips Docker Images/Manifests builds")
   107  	cmd.Flags().BoolVar(&root.opts.skipKo, "skip-ko", false, "Skips Ko builds")
   108  	cmd.Flags().BoolVar(&root.opts.skipBefore, "skip-before", false, "Skips global before hooks")
   109  	cmd.Flags().BoolVar(&root.opts.skipValidate, "skip-validate", false, "Skips git checks")
   110  	cmd.Flags().BoolVar(&root.opts.clean, "clean", false, "Removes the 'dist' directory")
   111  	cmd.Flags().BoolVar(&root.opts.rmDist, "rm-dist", false, "Removes the 'dist' directory")
   112  	cmd.Flags().IntVarP(&root.opts.parallelism, "parallelism", "p", 0, "Amount tasks to run concurrently (default: number of CPUs)")
   113  	_ = cmd.RegisterFlagCompletionFunc("parallelism", cobra.NoFileCompletions)
   114  	cmd.Flags().DurationVar(&root.opts.timeout, "timeout", 30*time.Minute, "Timeout to the entire release process")
   115  	_ = cmd.RegisterFlagCompletionFunc("timeout", cobra.NoFileCompletions)
   116  	cmd.Flags().BoolVar(&root.opts.deprecated, "deprecated", false, "Force print the deprecation message - tests only")
   117  	_ = cmd.Flags().MarkHidden("deprecated")
   118  	_ = cmd.Flags().MarkHidden("rm-dist")
   119  	_ = cmd.Flags().MarkDeprecated("rm-dist", "please use --clean instead")
   120  	for _, f := range []string{
   121  		"publish",
   122  		"announce",
   123  		"sign",
   124  		"sbom",
   125  		"docker",
   126  		"ko",
   127  		"before",
   128  		"validate",
   129  	} {
   130  		_ = cmd.Flags().MarkHidden("skip-" + f)
   131  		_ = cmd.Flags().MarkDeprecated("skip"+f, fmt.Sprintf("please use --skip=%s instead", f))
   132  	}
   133  	cmd.Flags().StringSliceVar(
   134  		&root.opts.skips,
   135  		"skip",
   136  		nil,
   137  		fmt.Sprintf("Skip the given options (valid options are %s)", skips.Release.String()),
   138  	)
   139  	_ = cmd.RegisterFlagCompletionFunc("skip", func(_ *cobra.Command, _ []string, toComplete string) ([]string, cobra.ShellCompDirective) {
   140  		return skips.Release.Complete(toComplete), cobra.ShellCompDirectiveDefault
   141  	})
   142  
   143  	root.cmd = cmd
   144  	return root
   145  }
   146  
   147  func releaseProject(options releaseOpts) (*context.Context, error) {
   148  	cfg, err := loadConfig(options.config)
   149  	if err != nil {
   150  		return nil, err
   151  	}
   152  	ctx, cancel := context.NewWithTimeout(cfg, options.timeout)
   153  	defer cancel()
   154  	if err := setupReleaseContext(ctx, options); err != nil {
   155  		return nil, err
   156  	}
   157  	return ctx, ctrlc.Default.Run(ctx, func() error {
   158  		for _, pipe := range pipeline.Pipeline {
   159  			if err := skip.Maybe(
   160  				pipe,
   161  				logging.Log(
   162  					pipe.String(),
   163  					errhandler.Handle(pipe.Run),
   164  				),
   165  			)(ctx); err != nil {
   166  				return err
   167  			}
   168  		}
   169  		return nil
   170  	})
   171  }
   172  
   173  func setupReleaseContext(ctx *context.Context, options releaseOpts) error {
   174  	ctx.Action = context.ActionRelease
   175  	ctx.Deprecated = options.deprecated // test only
   176  	ctx.Parallelism = runtime.GOMAXPROCS(0)
   177  	if options.parallelism > 0 {
   178  		ctx.Parallelism = options.parallelism
   179  	}
   180  	log.Debugf("parallelism: %v", ctx.Parallelism)
   181  	ctx.ReleaseNotesFile = options.releaseNotesFile
   182  	ctx.ReleaseNotesTmpl = options.releaseNotesTmpl
   183  	ctx.ReleaseHeaderFile = options.releaseHeaderFile
   184  	ctx.ReleaseHeaderTmpl = options.releaseHeaderTmpl
   185  	ctx.ReleaseFooterFile = options.releaseFooterFile
   186  	ctx.ReleaseFooterTmpl = options.releaseFooterTmpl
   187  	ctx.Snapshot = options.snapshot
   188  	ctx.FailFast = options.failFast
   189  	ctx.Clean = options.clean || options.rmDist
   190  	if options.autoSnapshot && git.CheckDirty(ctx) != nil {
   191  		log.Info("git repository is dirty and --auto-snapshot is set, implying --snapshot")
   192  		ctx.Snapshot = true
   193  	}
   194  
   195  	if err := skips.SetRelease(ctx, options.skips...); err != nil {
   196  		return err
   197  	}
   198  
   199  	// wire deprecated options
   200  	// XXX: remove soon
   201  	if options.skipPublish {
   202  		skips.Set(ctx, skips.Publish)
   203  		deprecate.NoticeCustom(ctx, "-skip", "--skip-publish was deprecated in favor of --skip=publish, check {{ .URL }} for more details")
   204  	}
   205  	if options.skipSign {
   206  		skips.Set(ctx, skips.Sign)
   207  		deprecate.NoticeCustom(ctx, "-skip", "--skip-sign was deprecated in favor of --skip=sign, check {{ .URL }} for more details")
   208  	}
   209  	if options.skipValidate {
   210  		skips.Set(ctx, skips.Validate)
   211  		deprecate.NoticeCustom(ctx, "-skip", "--skip-validate was deprecated in favor of --skip=validate, check {{ .URL }} for more details")
   212  	}
   213  	if options.skipAnnounce {
   214  		skips.Set(ctx, skips.Announce)
   215  		deprecate.NoticeCustom(ctx, "-skip", "--skip-announce was deprecated in favor of --skip=announce, check {{ .URL }} for more details")
   216  	}
   217  	if options.skipSBOMCataloging {
   218  		skips.Set(ctx, skips.SBOM)
   219  		deprecate.NoticeCustom(ctx, "-skip", "--skip-sbom was deprecated in favor of --skip=sbom, check {{ .URL }} for more details")
   220  	}
   221  	if options.skipDocker {
   222  		skips.Set(ctx, skips.Docker)
   223  		deprecate.NoticeCustom(ctx, "-skip", "--skip-docker was deprecated in favor of --skip=docker, check {{ .URL }} for more details")
   224  	}
   225  	if options.skipKo {
   226  		skips.Set(ctx, skips.Ko)
   227  		deprecate.NoticeCustom(ctx, "-skip", "--skip-ko was deprecated in favor of --skip=ko, check {{ .URL }} for more details")
   228  	}
   229  	if options.skipBefore {
   230  		skips.Set(ctx, skips.Before)
   231  		deprecate.NoticeCustom(ctx, "-skip", "--skip-before was deprecated in favor of --skip=before, check {{ .URL }} for more details")
   232  	}
   233  	if options.rmDist {
   234  		deprecate.NoticeCustom(ctx, "-rm-dist", "--rm-dist was deprecated in favor of --clean, check {{ .URL }} for more details")
   235  	}
   236  
   237  	if ctx.Snapshot {
   238  		skips.Set(ctx, skips.Publish, skips.Announce, skips.Validate)
   239  	}
   240  	if skips.Any(ctx, skips.Publish) {
   241  		skips.Set(ctx, skips.Announce)
   242  	}
   243  
   244  	if skips.Any(ctx, skips.Release...) {
   245  		log.Warnf(
   246  			logext.Warning("skipping %s..."),
   247  			skips.String(ctx),
   248  		)
   249  	}
   250  	return nil
   251  }