github.com/ahmet2mir/goreleaser@v0.180.3-0.20210927151101-8e5ee5a9b8c5/cmd/build.go (about)

     1  package cmd
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"runtime"
     7  	"time"
     8  
     9  	"github.com/apex/log"
    10  	"github.com/caarlos0/ctrlc"
    11  	"github.com/fatih/color"
    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/pipeline"
    16  	"github.com/goreleaser/goreleaser/pkg/config"
    17  	"github.com/goreleaser/goreleaser/pkg/context"
    18  	"github.com/spf13/cobra"
    19  )
    20  
    21  type buildCmd struct {
    22  	cmd  *cobra.Command
    23  	opts buildOpts
    24  }
    25  
    26  type buildOpts struct {
    27  	config        string
    28  	id            string
    29  	snapshot      bool
    30  	skipValidate  bool
    31  	skipPostHooks bool
    32  	rmDist        bool
    33  	deprecated    bool
    34  	parallelism   int
    35  	timeout       time.Duration
    36  	singleTarget  bool
    37  }
    38  
    39  func newBuildCmd() *buildCmd {
    40  	root := &buildCmd{}
    41  	// nolint: dupl
    42  	cmd := &cobra.Command{
    43  		Use:     "build",
    44  		Aliases: []string{"b"},
    45  		Short:   "Builds the current project",
    46  		Long: `The build command allows you to execute only a subset of the pipeline, i.e. only the build step with its dependencies.
    47  
    48  It allows you to quickly check if your GoReleaser build configurations are doing what you expect.
    49  
    50  Finally, it 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.
    51  `,
    52  		SilenceUsage:  true,
    53  		SilenceErrors: true,
    54  		Args:          cobra.NoArgs,
    55  		RunE: func(cmd *cobra.Command, args []string) error {
    56  			start := time.Now()
    57  
    58  			log.Infof(color.New(color.Bold).Sprint("building..."))
    59  
    60  			ctx, err := buildProject(root.opts)
    61  			if err != nil {
    62  				return wrapError(err, color.New(color.Bold).Sprintf("build failed after %0.2fs", time.Since(start).Seconds()))
    63  			}
    64  
    65  			if ctx.Deprecated {
    66  				log.Warn(color.New(color.Bold).Sprintf("your config is using deprecated properties, check logs above for details"))
    67  			}
    68  
    69  			log.Infof(color.New(color.Bold).Sprintf("build succeeded after %0.2fs", time.Since(start).Seconds()))
    70  			return nil
    71  		},
    72  	}
    73  
    74  	cmd.Flags().StringVarP(&root.opts.config, "config", "f", "", "Load configuration from file")
    75  	cmd.Flags().BoolVar(&root.opts.snapshot, "snapshot", false, "Generate an unversioned snapshot build, skipping all validations")
    76  	cmd.Flags().BoolVar(&root.opts.skipValidate, "skip-validate", false, "Skips several sanity checks")
    77  	cmd.Flags().BoolVar(&root.opts.skipPostHooks, "skip-post-hooks", false, "Skips all post-build hooks")
    78  	cmd.Flags().BoolVar(&root.opts.rmDist, "rm-dist", false, "Remove the dist folder before building")
    79  	cmd.Flags().IntVarP(&root.opts.parallelism, "parallelism", "p", 0, "Amount tasks to run concurrently (default: number of CPUs)")
    80  	cmd.Flags().DurationVar(&root.opts.timeout, "timeout", 30*time.Minute, "Timeout to the entire build process")
    81  	cmd.Flags().BoolVar(&root.opts.singleTarget, "single-target", false, "Builds only for current GOOS and GOARCH")
    82  	cmd.Flags().StringVar(&root.opts.id, "id", "", "Builds only the specified build id")
    83  	cmd.Flags().BoolVar(&root.opts.deprecated, "deprecated", false, "Force print the deprecation message - tests only")
    84  	_ = cmd.Flags().MarkHidden("deprecated")
    85  
    86  	root.cmd = cmd
    87  	return root
    88  }
    89  
    90  func buildProject(options buildOpts) (*context.Context, error) {
    91  	cfg, err := loadConfig(options.config)
    92  	if err != nil {
    93  		return nil, err
    94  	}
    95  	ctx, cancel := context.NewWithTimeout(cfg, options.timeout)
    96  	defer cancel()
    97  	if err := setupBuildContext(ctx, options); err != nil {
    98  		return nil, err
    99  	}
   100  	return ctx, ctrlc.Default.Run(ctx, func() error {
   101  		for _, pipe := range pipeline.BuildPipeline {
   102  			if err := skip.Maybe(
   103  				pipe,
   104  				logging.Log(
   105  					pipe.String(),
   106  					errhandler.Handle(pipe.Run),
   107  					logging.DefaultInitialPadding,
   108  				),
   109  			)(ctx); err != nil {
   110  				return err
   111  			}
   112  		}
   113  		return nil
   114  	})
   115  }
   116  
   117  func setupBuildContext(ctx *context.Context, options buildOpts) error {
   118  	ctx.Parallelism = runtime.NumCPU()
   119  	if options.parallelism > 0 {
   120  		ctx.Parallelism = options.parallelism
   121  	}
   122  	log.Debugf("parallelism: %v", ctx.Parallelism)
   123  	ctx.Snapshot = options.snapshot
   124  	ctx.SkipValidate = ctx.Snapshot || options.skipValidate
   125  	ctx.SkipPostBuildHooks = options.skipPostHooks
   126  	ctx.RmDist = options.rmDist
   127  	ctx.SkipTokenCheck = true
   128  
   129  	if options.singleTarget {
   130  		setupBuildSingleTarget(ctx)
   131  	}
   132  
   133  	if options.id != "" {
   134  		if err := setupBuildID(ctx, options.id); err != nil {
   135  			return err
   136  		}
   137  	}
   138  
   139  	// test only
   140  	ctx.Deprecated = options.deprecated
   141  	return nil
   142  }
   143  
   144  func setupBuildSingleTarget(ctx *context.Context) {
   145  	goos := os.Getenv("GOOS")
   146  	if goos == "" {
   147  		goos = runtime.GOOS
   148  	}
   149  	goarch := os.Getenv("GOARCH")
   150  	if goarch == "" {
   151  		goarch = runtime.GOARCH
   152  	}
   153  	log.Infof("building only for %s/%s", goos, goarch)
   154  	if len(ctx.Config.Builds) == 0 {
   155  		ctx.Config.Builds = append(ctx.Config.Builds, config.Build{})
   156  	}
   157  	for i := range ctx.Config.Builds {
   158  		build := &ctx.Config.Builds[i]
   159  		build.Goos = []string{goos}
   160  		build.Goarch = []string{goarch}
   161  	}
   162  }
   163  
   164  func setupBuildID(ctx *context.Context, id string) error {
   165  	if len(ctx.Config.Builds) < 2 {
   166  		log.Warn("single build in config, '--id' ignored")
   167  		return nil
   168  	}
   169  
   170  	var keep []config.Build
   171  	for _, build := range ctx.Config.Builds {
   172  		if build.ID == id {
   173  			keep = append(keep, build)
   174  			break
   175  		}
   176  	}
   177  
   178  	if len(keep) == 0 {
   179  		return fmt.Errorf("no builds with id '%s'", id)
   180  	}
   181  
   182  	ctx.Config.Builds = keep
   183  	return nil
   184  }