github.com/wesleimp/goreleaser@v0.92.0/main.go (about) 1 package main 2 3 import ( 4 "fmt" 5 "io/ioutil" 6 "os" 7 "strings" 8 "time" 9 10 "github.com/alecthomas/kingpin" 11 "github.com/apex/log" 12 "github.com/apex/log/handlers/cli" 13 "github.com/caarlos0/ctrlc" 14 "github.com/fatih/color" 15 "github.com/goreleaser/goreleaser/internal/pipe" 16 "github.com/goreleaser/goreleaser/internal/pipeline" 17 "github.com/goreleaser/goreleaser/pkg/config" 18 "github.com/goreleaser/goreleaser/pkg/context" 19 ) 20 21 var ( 22 version = "dev" 23 commit = "none" 24 date = "unknown" 25 ) 26 27 type releaseOptions struct { 28 Config string 29 ReleaseNotes string 30 Snapshot bool 31 SkipPublish bool 32 SkipSign bool 33 SkipValidate bool 34 RmDist bool 35 Debug bool 36 Parallelism int 37 Timeout time.Duration 38 } 39 40 func init() { 41 // enable colored output on travis 42 if os.Getenv("CI") != "" { 43 color.NoColor = false 44 } 45 log.SetHandler(cli.Default) 46 } 47 48 func main() { 49 fmt.Println() 50 defer fmt.Println() 51 52 var app = kingpin.New("goreleaser", "Deliver Go binaries as fast and easily as possible") 53 var initCmd = app.Command("init", "Generates a .goreleaser.yml file").Alias("i") 54 var releaseCmd = app.Command("release", "Releases the current project").Alias("r").Default() 55 var config = releaseCmd.Flag("config", "Load configuration from file").Short('c').Short('f').PlaceHolder(".goreleaser.yml").String() 56 var releaseNotes = releaseCmd.Flag("release-notes", "Load custom release notes from a markdown file").PlaceHolder("notes.md").String() 57 var snapshot = releaseCmd.Flag("snapshot", "Generate an unversioned snapshot release, skipping all validations and without publishing any artifacts").Bool() 58 var skipPublish = releaseCmd.Flag("skip-publish", "Generates all artifacts but does not publish them anywhere").Bool() 59 var skipSign = releaseCmd.Flag("skip-sign", "Skips signing the artifacts").Bool() 60 var skipValidate = releaseCmd.Flag("skip-validate", "Skips all git sanity checks").Bool() 61 var rmDist = releaseCmd.Flag("rm-dist", "Remove the dist folder before building").Bool() 62 var parallelism = releaseCmd.Flag("parallelism", "Amount of slow tasks to do in concurrently").Short('p').Default("4").Int() // TODO: use runtime.NumCPU here? 63 var debug = releaseCmd.Flag("debug", "Enable debug mode").Bool() 64 var timeout = releaseCmd.Flag("timeout", "Timeout to the entire release process").Default("30m").Duration() 65 66 app.Version(fmt.Sprintf("%v, commit %v, built at %v", version, commit, date)) 67 app.VersionFlag.Short('v') 68 app.HelpFlag.Short('h') 69 70 switch kingpin.MustParse(app.Parse(os.Args[1:])) { 71 case initCmd.FullCommand(): 72 var filename = ".goreleaser.yml" 73 if err := initProject(filename); err != nil { 74 log.WithError(err).Error("failed to init project") 75 terminate(1) 76 return 77 } 78 log.WithField("file", filename).Info("config created; please edit accordingly to your needs") 79 case releaseCmd.FullCommand(): 80 start := time.Now() 81 log.Infof(color.New(color.Bold).Sprintf("releasing using goreleaser %s...", version)) 82 var options = releaseOptions{ 83 Config: *config, 84 ReleaseNotes: *releaseNotes, 85 Snapshot: *snapshot, 86 SkipPublish: *skipPublish, 87 SkipValidate: *skipValidate, 88 SkipSign: *skipSign, 89 RmDist: *rmDist, 90 Parallelism: *parallelism, 91 Debug: *debug, 92 Timeout: *timeout, 93 } 94 if err := releaseProject(options); err != nil { 95 log.WithError(err).Errorf(color.New(color.Bold).Sprintf("release failed after %0.2fs", time.Since(start).Seconds())) 96 terminate(1) 97 return 98 } 99 log.Infof(color.New(color.Bold).Sprintf("release succeeded after %0.2fs", time.Since(start).Seconds())) 100 } 101 } 102 103 func terminate(status int) { 104 os.Exit(status) 105 } 106 107 func releaseProject(options releaseOptions) error { 108 if options.Debug { 109 log.SetLevel(log.DebugLevel) 110 } 111 cfg, err := loadConfig(options.Config) 112 if err != nil { 113 return err 114 } 115 ctx, cancel := context.NewWithTimeout(cfg, options.Timeout) 116 defer cancel() 117 ctx.Parallelism = options.Parallelism 118 ctx.Debug = options.Debug 119 log.Debugf("parallelism: %v", ctx.Parallelism) 120 if options.ReleaseNotes != "" { 121 bts, err := ioutil.ReadFile(options.ReleaseNotes) 122 if err != nil { 123 return err 124 } 125 log.WithField("file", options.ReleaseNotes).Info("loaded custom release notes") 126 log.WithField("file", options.ReleaseNotes).Debugf("custom release notes: \n%s", string(bts)) 127 ctx.ReleaseNotes = string(bts) 128 } 129 ctx.Snapshot = options.Snapshot 130 ctx.SkipPublish = ctx.Snapshot || options.SkipPublish 131 ctx.SkipValidate = ctx.Snapshot || options.SkipValidate 132 ctx.SkipSign = options.SkipSign 133 ctx.RmDist = options.RmDist 134 return doRelease(ctx) 135 } 136 137 var bold = color.New(color.Bold) 138 139 func doRelease(ctx *context.Context) error { 140 defer func() { cli.Default.Padding = 3 }() 141 var release = func() error { 142 for _, pipe := range pipeline.Pipeline { 143 cli.Default.Padding = 3 144 log.Infof(bold.Sprint(strings.ToUpper(pipe.String()))) 145 cli.Default.Padding = 6 146 if err := handle(pipe.Run(ctx)); err != nil { 147 return err 148 } 149 } 150 return nil 151 } 152 return ctrlc.Default.Run(ctx, release) 153 } 154 155 func handle(err error) error { 156 if err == nil { 157 return nil 158 } 159 if pipe.IsSkip(err) { 160 log.WithField("reason", err.Error()).Warn("skipped") 161 return nil 162 } 163 return err 164 } 165 166 // InitProject creates an example goreleaser.yml in the current directory 167 func initProject(filename string) error { 168 if _, err := os.Stat(filename); !os.IsNotExist(err) { 169 if err != nil { 170 return err 171 } 172 return fmt.Errorf("%s already exists", filename) 173 } 174 log.Infof(color.New(color.Bold).Sprintf("Generating %s file", filename)) 175 return ioutil.WriteFile(filename, []byte(exampleConfig), 0644) 176 } 177 178 func loadConfig(path string) (config.Project, error) { 179 if path != "" { 180 return config.Load(path) 181 } 182 for _, f := range [4]string{ 183 ".goreleaser.yml", 184 ".goreleaser.yaml", 185 "goreleaser.yml", 186 "goreleaser.yaml", 187 } { 188 proj, err := config.Load(f) 189 if err != nil && os.IsNotExist(err) { 190 continue 191 } 192 return proj, err 193 } 194 // the user didn't specified a config file and the known files 195 // doest not exist, so, return an empty config and a nil err. 196 log.Warn("could not load config, using defaults") 197 return config.Project{}, nil 198 } 199 200 var exampleConfig = `# This is an example goreleaser.yaml file with some sane defaults. 201 # Make sure to check the documentation at http://goreleaser.com 202 before: 203 hooks: 204 # you may remove this if you don't use vgo 205 - go mod download 206 # you may remove this if you don't need go generate 207 - go generate ./... 208 builds: 209 - env: 210 - CGO_ENABLED=0 211 archive: 212 replacements: 213 darwin: Darwin 214 linux: Linux 215 windows: Windows 216 386: i386 217 amd64: x86_64 218 checksum: 219 name_template: 'checksums.txt' 220 snapshot: 221 name_template: "{{ .Tag }}-next" 222 changelog: 223 sort: asc 224 filters: 225 exclude: 226 - '^docs:' 227 - '^test:' 228 `