github.com/ncabatoff/goreleaser@v0.83.4-0.20180825212434-93a0055d0362/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 16 "github.com/goreleaser/goreleaser/internal/pipeline" 17 "github.com/goreleaser/goreleaser/internal/pipeline/archive" 18 "github.com/goreleaser/goreleaser/internal/pipeline/artifactory" 19 "github.com/goreleaser/goreleaser/internal/pipeline/before" 20 "github.com/goreleaser/goreleaser/internal/pipeline/brew" 21 "github.com/goreleaser/goreleaser/internal/pipeline/build" 22 "github.com/goreleaser/goreleaser/internal/pipeline/changelog" 23 "github.com/goreleaser/goreleaser/internal/pipeline/checksums" 24 "github.com/goreleaser/goreleaser/internal/pipeline/defaults" 25 "github.com/goreleaser/goreleaser/internal/pipeline/dist" 26 "github.com/goreleaser/goreleaser/internal/pipeline/docker" 27 "github.com/goreleaser/goreleaser/internal/pipeline/effectiveconfig" 28 "github.com/goreleaser/goreleaser/internal/pipeline/env" 29 "github.com/goreleaser/goreleaser/internal/pipeline/git" 30 "github.com/goreleaser/goreleaser/internal/pipeline/nfpm" 31 "github.com/goreleaser/goreleaser/internal/pipeline/put" 32 "github.com/goreleaser/goreleaser/internal/pipeline/release" 33 "github.com/goreleaser/goreleaser/internal/pipeline/s3" 34 "github.com/goreleaser/goreleaser/internal/pipeline/scoop" 35 "github.com/goreleaser/goreleaser/internal/pipeline/sign" 36 "github.com/goreleaser/goreleaser/internal/pipeline/snapcraft" 37 "github.com/goreleaser/goreleaser/pkg/config" 38 "github.com/goreleaser/goreleaser/pkg/context" 39 ) 40 41 var ( 42 version = "dev" 43 commit = "none" 44 date = "unknown" 45 ) 46 47 var pipes = []Piper{ 48 defaults.Pipe{}, // load default configs 49 before.Pipe{}, // run global hooks before build 50 dist.Pipe{}, // ensure ./dist is clean 51 git.Pipe{}, // get and validate git repo state 52 effectiveconfig.Pipe{}, // writes the actual config (with defaults et al set) to dist 53 changelog.Pipe{}, // builds the release changelog 54 env.Pipe{}, // load and validate environment variables 55 build.Pipe{}, // build 56 archive.Pipe{}, // archive in tar.gz, zip or binary (which does no archiving at all) 57 nfpm.Pipe{}, // archive via fpm (deb, rpm) using "native" go impl 58 snapcraft.Pipe{}, // archive via snapcraft (snap) 59 checksums.Pipe{}, // checksums of the files 60 sign.Pipe{}, // sign artifacts 61 docker.Pipe{}, // create and push docker images 62 artifactory.Pipe{}, // push to artifactory 63 put.Pipe{}, // upload to http server 64 s3.Pipe{}, // push to s3/minio 65 release.Pipe{}, // release to github 66 brew.Pipe{}, // push to brew tap 67 scoop.Pipe{}, // push to scoop bucket 68 } 69 70 // Piper defines a pipe, which can be part of a pipeline (a serie of pipes). 71 type Piper interface { 72 fmt.Stringer 73 74 // Run the pipe 75 Run(ctx *context.Context) error 76 } 77 78 type releaseOptions struct { 79 Config string 80 ReleaseNotes string 81 Snapshot bool 82 SkipPublish bool 83 SkipSign bool 84 SkipValidate bool 85 RmDist bool 86 Debug bool 87 Parallelism int 88 Timeout time.Duration 89 } 90 91 func init() { 92 log.SetHandler(cli.Default) 93 } 94 95 func main() { 96 fmt.Println() 97 defer fmt.Println() 98 99 var app = kingpin.New("goreleaser", "Deliver Go binaries as fast and easily as possible") 100 var initCmd = app.Command("init", "Generates a .goreleaser.yml file").Alias("i") 101 var releaseCmd = app.Command("release", "Releases the current project").Alias("r").Default() 102 var config = releaseCmd.Flag("config", "Load configuration from file").Short('c').Short('f').PlaceHolder(".goreleaser.yml").String() 103 var releaseNotes = releaseCmd.Flag("release-notes", "Load custom release notes from a markdown file").PlaceHolder("notes.md").String() 104 var snapshot = releaseCmd.Flag("snapshot", "Generate an unversioned snapshot release, skipping all validations and without publishing any artifacts").Bool() 105 var skipPublish = releaseCmd.Flag("skip-publish", "Generates all artifacts but does not publish them anywhere").Bool() 106 var skipSign = releaseCmd.Flag("skip-sign", "Skips signing the artifacts").Bool() 107 var skipValidate = releaseCmd.Flag("skip-validate", "Skips all git sanity checks").Bool() 108 var rmDist = releaseCmd.Flag("rm-dist", "Remove the dist folder before building").Bool() 109 var parallelism = releaseCmd.Flag("parallelism", "Amount of slow tasks to do in concurrently").Short('p').Default("4").Int() // TODO: use runtime.NumCPU here? 110 var debug = releaseCmd.Flag("debug", "Enable debug mode").Bool() 111 var timeout = releaseCmd.Flag("timeout", "Timeout to the entire release process").Default("30m").Duration() 112 113 app.Version(fmt.Sprintf("%v, commit %v, built at %v", version, commit, date)) 114 app.VersionFlag.Short('v') 115 app.HelpFlag.Short('h') 116 117 switch kingpin.MustParse(app.Parse(os.Args[1:])) { 118 case initCmd.FullCommand(): 119 var filename = ".goreleaser.yml" 120 if err := initProject(filename); err != nil { 121 log.WithError(err).Error("failed to init project") 122 terminate(1) 123 return 124 } 125 log.WithField("file", filename).Info("config created; please edit accordingly to your needs") 126 case releaseCmd.FullCommand(): 127 start := time.Now() 128 log.Infof(color.New(color.Bold).Sprintf("releasing using goreleaser %s...", version)) 129 var options = releaseOptions{ 130 Config: *config, 131 ReleaseNotes: *releaseNotes, 132 Snapshot: *snapshot, 133 SkipPublish: *skipPublish, 134 SkipValidate: *skipValidate, 135 SkipSign: *skipSign, 136 RmDist: *rmDist, 137 Parallelism: *parallelism, 138 Debug: *debug, 139 Timeout: *timeout, 140 } 141 if err := releaseProject(options); err != nil { 142 log.WithError(err).Errorf(color.New(color.Bold).Sprintf("release failed after %0.2fs", time.Since(start).Seconds())) 143 terminate(1) 144 return 145 } 146 log.Infof(color.New(color.Bold).Sprintf("release succeeded after %0.2fs", time.Since(start).Seconds())) 147 } 148 } 149 150 func terminate(status int) { 151 os.Exit(status) 152 } 153 154 func releaseProject(options releaseOptions) error { 155 if options.Debug { 156 log.SetLevel(log.DebugLevel) 157 } 158 cfg, err := loadConfig(options.Config) 159 if err != nil { 160 return err 161 } 162 ctx, cancel := context.NewWithTimeout(cfg, options.Timeout) 163 defer cancel() 164 ctx.Parallelism = options.Parallelism 165 ctx.Debug = options.Debug 166 log.Debugf("parallelism: %v", ctx.Parallelism) 167 if options.ReleaseNotes != "" { 168 bts, err := ioutil.ReadFile(options.ReleaseNotes) 169 if err != nil { 170 return err 171 } 172 log.WithField("file", options.ReleaseNotes).Info("loaded custom release notes") 173 log.WithField("file", options.ReleaseNotes).Debugf("custom release notes: \n%s", string(bts)) 174 ctx.ReleaseNotes = string(bts) 175 } 176 ctx.Snapshot = options.Snapshot 177 ctx.SkipPublish = ctx.Snapshot || options.SkipPublish 178 ctx.SkipValidate = ctx.Snapshot || options.SkipValidate 179 ctx.SkipSign = options.SkipSign 180 ctx.RmDist = options.RmDist 181 return doRelease(ctx) 182 } 183 184 func doRelease(ctx *context.Context) error { 185 defer func() { cli.Default.Padding = 3 }() 186 var release = func() error { 187 for _, pipe := range pipes { 188 cli.Default.Padding = 3 189 log.Infof(color.New(color.Bold).Sprint(strings.ToUpper(pipe.String()))) 190 cli.Default.Padding = 6 191 if err := handle(pipe.Run(ctx)); err != nil { 192 return err 193 } 194 } 195 return nil 196 } 197 return ctrlc.Default.Run(ctx, release) 198 } 199 200 func handle(err error) error { 201 if err == nil { 202 return nil 203 } 204 if pipeline.IsSkip(err) { 205 log.WithField("reason", err.Error()).Warn("skipped") 206 return nil 207 } 208 return err 209 } 210 211 // InitProject creates an example goreleaser.yml in the current directory 212 func initProject(filename string) error { 213 if _, err := os.Stat(filename); !os.IsNotExist(err) { 214 if err != nil { 215 return err 216 } 217 return fmt.Errorf("%s already exists", filename) 218 } 219 log.Infof(color.New(color.Bold).Sprintf("Generating %s file", filename)) 220 return ioutil.WriteFile(filename, []byte(exampleConfig), 0644) 221 } 222 223 func loadConfig(path string) (config.Project, error) { 224 if path != "" { 225 return config.Load(path) 226 } 227 for _, f := range [4]string{ 228 ".goreleaser.yml", 229 ".goreleaser.yaml", 230 "goreleaser.yml", 231 "goreleaser.yaml", 232 } { 233 proj, err := config.Load(f) 234 if err != nil && os.IsNotExist(err) { 235 continue 236 } 237 return proj, err 238 } 239 // the user didn't specified a config file and the known files 240 // doest not exist, so, return an empty config and a nil err. 241 log.Warn("could not load config, using defaults") 242 return config.Project{}, nil 243 } 244 245 var exampleConfig = `# This is an example goreleaser.yaml file with some sane defaults. 246 # Make sure to check the documentation at http://goreleaser.com 247 builds: 248 - env: 249 - CGO_ENABLED=0 250 archive: 251 replacements: 252 darwin: Darwin 253 linux: Linux 254 windows: Windows 255 386: i386 256 amd64: x86_64 257 checksum: 258 name_template: 'checksums.txt' 259 snapshot: 260 name_template: "{{ .Tag }}-next" 261 changelog: 262 sort: asc 263 filters: 264 exclude: 265 - '^docs:' 266 - '^test:' 267 `