mirror of
https://github.com/goreleaser/goreleaser.git
synced 2025-01-22 04:08:49 +02:00
229 lines
6.6 KiB
Go
229 lines
6.6 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"io/ioutil"
|
|
"os"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/alecthomas/kingpin"
|
|
"github.com/apex/log"
|
|
"github.com/apex/log/handlers/cli"
|
|
"github.com/caarlos0/ctrlc"
|
|
"github.com/fatih/color"
|
|
"github.com/goreleaser/goreleaser/internal/pipe"
|
|
"github.com/goreleaser/goreleaser/internal/pipeline"
|
|
"github.com/goreleaser/goreleaser/pkg/config"
|
|
"github.com/goreleaser/goreleaser/pkg/context"
|
|
)
|
|
|
|
var (
|
|
version = "dev"
|
|
commit = "none"
|
|
date = "unknown"
|
|
)
|
|
|
|
type releaseOptions struct {
|
|
Config string
|
|
ReleaseNotes string
|
|
Snapshot bool
|
|
SkipPublish bool
|
|
SkipSign bool
|
|
SkipValidate bool
|
|
RmDist bool
|
|
Debug bool
|
|
Parallelism int
|
|
Timeout time.Duration
|
|
}
|
|
|
|
func init() {
|
|
// enable colored output on travis
|
|
if os.Getenv("CI") != "" {
|
|
color.NoColor = false
|
|
}
|
|
log.SetHandler(cli.Default)
|
|
}
|
|
|
|
func main() {
|
|
fmt.Println()
|
|
defer fmt.Println()
|
|
|
|
var app = kingpin.New("goreleaser", "Deliver Go binaries as fast and easily as possible")
|
|
var initCmd = app.Command("init", "Generates a .goreleaser.yml file").Alias("i")
|
|
var releaseCmd = app.Command("release", "Releases the current project").Alias("r").Default()
|
|
var config = releaseCmd.Flag("config", "Load configuration from file").Short('c').Short('f').PlaceHolder(".goreleaser.yml").String()
|
|
var releaseNotes = releaseCmd.Flag("release-notes", "Load custom release notes from a markdown file").PlaceHolder("notes.md").String()
|
|
var snapshot = releaseCmd.Flag("snapshot", "Generate an unversioned snapshot release, skipping all validations and without publishing any artifacts").Bool()
|
|
var skipPublish = releaseCmd.Flag("skip-publish", "Generates all artifacts but does not publish them anywhere").Bool()
|
|
var skipSign = releaseCmd.Flag("skip-sign", "Skips signing the artifacts").Bool()
|
|
var skipValidate = releaseCmd.Flag("skip-validate", "Skips all git sanity checks").Bool()
|
|
var rmDist = releaseCmd.Flag("rm-dist", "Remove the dist folder before building").Bool()
|
|
var parallelism = releaseCmd.Flag("parallelism", "Amount of slow tasks to do in concurrently").Short('p').Default("4").Int() // TODO: use runtime.NumCPU here?
|
|
var debug = releaseCmd.Flag("debug", "Enable debug mode").Bool()
|
|
var timeout = releaseCmd.Flag("timeout", "Timeout to the entire release process").Default("30m").Duration()
|
|
|
|
app.Version(fmt.Sprintf("%v, commit %v, built at %v", version, commit, date))
|
|
app.VersionFlag.Short('v')
|
|
app.HelpFlag.Short('h')
|
|
|
|
switch kingpin.MustParse(app.Parse(os.Args[1:])) {
|
|
case initCmd.FullCommand():
|
|
var filename = ".goreleaser.yml"
|
|
if err := initProject(filename); err != nil {
|
|
log.WithError(err).Error("failed to init project")
|
|
terminate(1)
|
|
return
|
|
}
|
|
log.WithField("file", filename).Info("config created; please edit accordingly to your needs")
|
|
case releaseCmd.FullCommand():
|
|
start := time.Now()
|
|
log.Infof(color.New(color.Bold).Sprintf("releasing using goreleaser %s...", version))
|
|
var options = releaseOptions{
|
|
Config: *config,
|
|
ReleaseNotes: *releaseNotes,
|
|
Snapshot: *snapshot,
|
|
SkipPublish: *skipPublish,
|
|
SkipValidate: *skipValidate,
|
|
SkipSign: *skipSign,
|
|
RmDist: *rmDist,
|
|
Parallelism: *parallelism,
|
|
Debug: *debug,
|
|
Timeout: *timeout,
|
|
}
|
|
if err := releaseProject(options); err != nil {
|
|
log.WithError(err).Errorf(color.New(color.Bold).Sprintf("release failed after %0.2fs", time.Since(start).Seconds()))
|
|
terminate(1)
|
|
return
|
|
}
|
|
log.Infof(color.New(color.Bold).Sprintf("release succeeded after %0.2fs", time.Since(start).Seconds()))
|
|
}
|
|
}
|
|
|
|
func terminate(status int) {
|
|
os.Exit(status)
|
|
}
|
|
|
|
func releaseProject(options releaseOptions) error {
|
|
if options.Debug {
|
|
log.SetLevel(log.DebugLevel)
|
|
}
|
|
cfg, err := loadConfig(options.Config)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
ctx, cancel := context.NewWithTimeout(cfg, options.Timeout)
|
|
defer cancel()
|
|
ctx.Parallelism = options.Parallelism
|
|
ctx.Debug = options.Debug
|
|
log.Debugf("parallelism: %v", ctx.Parallelism)
|
|
if options.ReleaseNotes != "" {
|
|
bts, err := ioutil.ReadFile(options.ReleaseNotes)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
log.WithField("file", options.ReleaseNotes).Info("loaded custom release notes")
|
|
log.WithField("file", options.ReleaseNotes).Debugf("custom release notes: \n%s", string(bts))
|
|
ctx.ReleaseNotes = string(bts)
|
|
}
|
|
ctx.Snapshot = options.Snapshot
|
|
ctx.SkipPublish = ctx.Snapshot || options.SkipPublish
|
|
ctx.SkipValidate = ctx.Snapshot || options.SkipValidate
|
|
ctx.SkipSign = options.SkipSign
|
|
ctx.RmDist = options.RmDist
|
|
return doRelease(ctx)
|
|
}
|
|
|
|
var bold = color.New(color.Bold)
|
|
|
|
func doRelease(ctx *context.Context) error {
|
|
defer func() { cli.Default.Padding = 3 }()
|
|
var release = func() error {
|
|
for _, pipe := range pipeline.Pipeline {
|
|
cli.Default.Padding = 3
|
|
log.Infof(bold.Sprint(strings.ToUpper(pipe.String())))
|
|
cli.Default.Padding = 6
|
|
if err := handle(pipe.Run(ctx)); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
return ctrlc.Default.Run(ctx, release)
|
|
}
|
|
|
|
func handle(err error) error {
|
|
if err == nil {
|
|
return nil
|
|
}
|
|
if pipe.IsSkip(err) {
|
|
log.WithField("reason", err.Error()).Warn("skipped")
|
|
return nil
|
|
}
|
|
return err
|
|
}
|
|
|
|
// InitProject creates an example goreleaser.yml in the current directory
|
|
func initProject(filename string) error {
|
|
if _, err := os.Stat(filename); !os.IsNotExist(err) {
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return fmt.Errorf("%s already exists", filename)
|
|
}
|
|
log.Infof(color.New(color.Bold).Sprintf("Generating %s file", filename))
|
|
return ioutil.WriteFile(filename, []byte(exampleConfig), 0644)
|
|
}
|
|
|
|
func loadConfig(path string) (config.Project, error) {
|
|
if path != "" {
|
|
return config.Load(path)
|
|
}
|
|
for _, f := range [4]string{
|
|
".goreleaser.yml",
|
|
".goreleaser.yaml",
|
|
"goreleaser.yml",
|
|
"goreleaser.yaml",
|
|
} {
|
|
proj, err := config.Load(f)
|
|
if err != nil && os.IsNotExist(err) {
|
|
continue
|
|
}
|
|
return proj, err
|
|
}
|
|
// the user didn't specified a config file and the known files
|
|
// doest not exist, so, return an empty config and a nil err.
|
|
log.Warn("could not load config, using defaults")
|
|
return config.Project{}, nil
|
|
}
|
|
|
|
var exampleConfig = `# This is an example goreleaser.yaml file with some sane defaults.
|
|
# Make sure to check the documentation at http://goreleaser.com
|
|
before:
|
|
hooks:
|
|
# you may remove this if you don't use vgo
|
|
- go mod download
|
|
# you may remove this if you don't need go generate
|
|
- go generate ./...
|
|
builds:
|
|
- env:
|
|
- CGO_ENABLED=0
|
|
archive:
|
|
replacements:
|
|
darwin: Darwin
|
|
linux: Linux
|
|
windows: Windows
|
|
386: i386
|
|
amd64: x86_64
|
|
checksum:
|
|
name_template: 'checksums.txt'
|
|
snapshot:
|
|
name_template: "{{ .Tag }}-next"
|
|
changelog:
|
|
sort: asc
|
|
filters:
|
|
exclude:
|
|
- '^docs:'
|
|
- '^test:'
|
|
`
|