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