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