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 }