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

     1  package cmd
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"time"
     7  
     8  	goversion "github.com/caarlos0/go-version"
     9  	"github.com/caarlos0/log"
    10  	"github.com/charmbracelet/lipgloss"
    11  	"github.com/goreleaser/goreleaser/pkg/context"
    12  	"github.com/spf13/cobra"
    13  )
    14  
    15  var (
    16  	boldStyle = lipgloss.NewStyle().Bold(true)
    17  	codeStyle = lipgloss.NewStyle().Italic(true)
    18  )
    19  
    20  func Execute(version goversion.Info, exit func(int), args []string) {
    21  	newRootCmd(version, exit).Execute(args)
    22  }
    23  
    24  func (cmd *rootCmd) Execute(args []string) {
    25  	cmd.cmd.SetArgs(args)
    26  
    27  	if shouldPrependRelease(cmd.cmd, args) {
    28  		cmd.cmd.SetArgs(append([]string{"release"}, args...))
    29  	}
    30  
    31  	if err := cmd.cmd.Execute(); err != nil {
    32  		code := 1
    33  		msg := "command failed"
    34  		eerr := &exitError{}
    35  		if errors.As(err, &eerr) {
    36  			code = eerr.code
    37  			if eerr.details != "" {
    38  				msg = eerr.details
    39  			}
    40  		}
    41  		log.WithError(err).Error(msg)
    42  		cmd.exit(code)
    43  	}
    44  }
    45  
    46  type rootCmd struct {
    47  	cmd     *cobra.Command
    48  	verbose bool
    49  	exit    func(int)
    50  
    51  	// Deprecated: use verbose instead.
    52  	debug bool
    53  }
    54  
    55  func newRootCmd(version goversion.Info, exit func(int)) *rootCmd {
    56  	root := &rootCmd{
    57  		exit: exit,
    58  	}
    59  	cmd := &cobra.Command{
    60  		Use:   "goreleaser",
    61  		Short: "Deliver Go binaries as fast and easily as possible",
    62  		Long: `GoReleaser is a release automation tool for Go projects.
    63  Its goal is to simplify the build, release and publish steps while providing variant customization options for all steps.
    64  
    65  GoReleaser is built for CI tools, you only need to download and execute it in your build script. Of course, you can also install it locally if you wish.
    66  
    67  You can customize your entire release process through a single .goreleaser.yaml file.
    68  
    69  Check out our website for more information, examples and documentation: https://goreleaser.com
    70  `,
    71  		Version:           version.String(),
    72  		SilenceUsage:      true,
    73  		SilenceErrors:     true,
    74  		Args:              cobra.NoArgs,
    75  		ValidArgsFunction: cobra.NoFileCompletions,
    76  		PersistentPreRun: func(_ *cobra.Command, _ []string) {
    77  			if root.verbose || root.debug {
    78  				log.SetLevel(log.DebugLevel)
    79  				log.Debug("verbose output enabled")
    80  			}
    81  		},
    82  		PersistentPostRun: func(_ *cobra.Command, _ []string) {
    83  			log.Info("thanks for using goreleaser!")
    84  		},
    85  	}
    86  	cmd.SetVersionTemplate("{{.Version}}")
    87  
    88  	cmd.PersistentFlags().BoolVar(&root.debug, "debug", false, "Enable verbose mode")
    89  	cmd.PersistentFlags().BoolVar(&root.verbose, "verbose", false, "Enable verbose mode")
    90  	_ = cmd.Flags().MarkDeprecated("debug", "please use --verbose instead")
    91  	_ = cmd.Flags().MarkHidden("debug")
    92  	cmd.AddCommand(
    93  		newBuildCmd().cmd,
    94  		newReleaseCmd().cmd,
    95  		newCheckCmd().cmd,
    96  		newHealthcheckCmd().cmd,
    97  		newInitCmd().cmd,
    98  		newDocsCmd().cmd,
    99  		newManCmd().cmd,
   100  		newSchemaCmd().cmd,
   101  	)
   102  	root.cmd = cmd
   103  	return root
   104  }
   105  
   106  func shouldPrependRelease(cmd *cobra.Command, args []string) bool {
   107  	// find current cmd, if its not root, it means the user actively
   108  	// set a command, so let it go
   109  	xmd, _, _ := cmd.Find(args)
   110  	if xmd != cmd {
   111  		return false
   112  	}
   113  
   114  	// allow help and the two __complete commands.
   115  	if len(args) > 0 && (args[0] == "help" || args[0] == "completion" ||
   116  		args[0] == cobra.ShellCompRequestCmd || args[0] == cobra.ShellCompNoDescRequestCmd) {
   117  		return false
   118  	}
   119  
   120  	// if we have != 1 args, assume its a release
   121  	if len(args) != 1 {
   122  		return true
   123  	}
   124  
   125  	// given that its 1, check if its one of the valid standalone flags
   126  	// for the root cmd
   127  	for _, s := range []string{"-h", "--help", "-v", "--version"} {
   128  		if s == args[0] {
   129  			// if it is, we should run the root cmd
   130  			return false
   131  		}
   132  	}
   133  
   134  	// otherwise, we should probably prepend release
   135  	return true
   136  }
   137  
   138  func deprecateWarn(ctx *context.Context) {
   139  	if ctx.Deprecated {
   140  		log.Warn(boldStyle.Render("you are using deprecated options, check the output above for details"))
   141  	}
   142  }
   143  
   144  func timedRunE(verb string, rune func(cmd *cobra.Command, args []string) error) func(cmd *cobra.Command, args []string) error {
   145  	return func(cmd *cobra.Command, args []string) error {
   146  		start := time.Now()
   147  
   148  		log.Infof(boldStyle.Render(fmt.Sprintf("starting %s...", verb)))
   149  
   150  		if err := rune(cmd, args); err != nil {
   151  			return wrapError(err, boldStyle.Render(fmt.Sprintf("%s failed after %s", verb, time.Since(start).Truncate(time.Second))))
   152  		}
   153  
   154  		log.Infof(boldStyle.Render(fmt.Sprintf("%s succeeded after %s", verb, time.Since(start).Truncate(time.Second))))
   155  		return nil
   156  	}
   157  }