github.com/neomantra/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  `