2017-04-21 20:25:32 +02:00
package main
import (
2017-04-22 02:58:59 +02:00
"fmt"
2018-02-22 01:04:22 +02:00
"io/ioutil"
2017-04-21 20:25:32 +02:00
"os"
2018-02-22 01:04:22 +02:00
"strings"
2017-12-06 05:02:52 +02:00
"time"
2017-04-21 20:25:32 +02:00
2018-03-11 17:00:47 +02:00
"github.com/alecthomas/kingpin"
2017-06-22 05:09:14 +02:00
"github.com/apex/log"
2018-03-11 17:00:47 +02:00
"github.com/apex/log/handlers/cli"
2018-02-24 22:59:08 +02:00
"github.com/caarlos0/ctrlc"
2018-01-17 22:48:01 +02:00
"github.com/fatih/color"
2018-02-22 01:04:22 +02:00
2018-08-15 04:50:20 +02:00
"github.com/goreleaser/goreleaser/internal/pipeline"
"github.com/goreleaser/goreleaser/internal/pipeline/archive"
"github.com/goreleaser/goreleaser/internal/pipeline/artifactory"
"github.com/goreleaser/goreleaser/internal/pipeline/before"
"github.com/goreleaser/goreleaser/internal/pipeline/brew"
"github.com/goreleaser/goreleaser/internal/pipeline/build"
"github.com/goreleaser/goreleaser/internal/pipeline/changelog"
"github.com/goreleaser/goreleaser/internal/pipeline/checksums"
"github.com/goreleaser/goreleaser/internal/pipeline/defaults"
"github.com/goreleaser/goreleaser/internal/pipeline/dist"
"github.com/goreleaser/goreleaser/internal/pipeline/docker"
"github.com/goreleaser/goreleaser/internal/pipeline/effectiveconfig"
"github.com/goreleaser/goreleaser/internal/pipeline/env"
"github.com/goreleaser/goreleaser/internal/pipeline/git"
"github.com/goreleaser/goreleaser/internal/pipeline/nfpm"
"github.com/goreleaser/goreleaser/internal/pipeline/put"
"github.com/goreleaser/goreleaser/internal/pipeline/release"
"github.com/goreleaser/goreleaser/internal/pipeline/s3"
"github.com/goreleaser/goreleaser/internal/pipeline/scoop"
"github.com/goreleaser/goreleaser/internal/pipeline/sign"
"github.com/goreleaser/goreleaser/internal/pipeline/snapcraft"
"github.com/goreleaser/goreleaser/pkg/config"
"github.com/goreleaser/goreleaser/pkg/context"
2017-04-21 20:25:32 +02:00
)
var (
version = "dev"
commit = "none"
date = "unknown"
)
2018-03-11 17:00:47 +02:00
var pipes = [ ] Piper {
defaults . Pipe { } , // load default configs
2018-03-28 15:31:09 +02:00
before . Pipe { } , // run global hooks before build
2018-03-11 17:00:47 +02:00
dist . Pipe { } , // ensure ./dist is clean
git . Pipe { } , // get and validate git repo state
effectiveconfig . Pipe { } , // writes the actual config (with defaults et al set) to dist
changelog . Pipe { } , // builds the release changelog
env . Pipe { } , // load and validate environment variables
build . Pipe { } , // build
archive . Pipe { } , // archive in tar.gz, zip or binary (which does no archiving at all)
nfpm . Pipe { } , // archive via fpm (deb, rpm) using "native" go impl
snapcraft . Pipe { } , // archive via snapcraft (snap)
checksums . Pipe { } , // checksums of the files
sign . Pipe { } , // sign artifacts
docker . Pipe { } , // create and push docker images
artifactory . Pipe { } , // push to artifactory
2018-06-14 05:37:48 +02:00
put . Pipe { } , // upload to http server
2018-05-13 18:08:14 +02:00
s3 . Pipe { } , // push to s3/minio
2018-03-11 17:00:47 +02:00
release . Pipe { } , // release to github
brew . Pipe { } , // push to brew tap
scoop . Pipe { } , // push to scoop bucket
}
2018-03-08 23:46:15 +02:00
// Piper defines a pipe, which can be part of a pipeline (a serie of pipes).
type Piper interface {
fmt . Stringer
// Run the pipe
Run ( ctx * context . Context ) error
}
2018-03-11 17:00:47 +02:00
type releaseOptions struct {
Config string
ReleaseNotes string
Snapshot bool
SkipPublish bool
2018-05-24 10:50:14 +02:00
SkipSign bool
2018-03-11 17:00:47 +02:00
SkipValidate bool
RmDist bool
Debug bool
Parallelism int
Timeout time . Duration
2018-02-22 01:04:22 +02:00
}
2017-06-22 05:09:14 +02:00
func init ( ) {
2018-03-11 17:00:47 +02:00
log . SetHandler ( cli . Default )
2017-06-22 05:09:14 +02:00
}
2017-04-21 20:25:32 +02:00
func main ( ) {
2017-11-27 01:48:05 +02:00
fmt . Println ( )
defer fmt . Println ( )
2018-03-11 17:00:47 +02:00
var app = kingpin . New ( "goreleaser" , "Deliver Go binaries as fast and easily as possible" )
2018-03-11 17:09:19 +02:00
var initCmd = app . Command ( "init" , "Generates a .goreleaser.yml file" ) . Alias ( "i" )
var releaseCmd = app . Command ( "release" , "Releases the current project" ) . Alias ( "r" ) . Default ( )
2018-03-11 17:00:47 +02:00
var config = releaseCmd . Flag ( "config" , "Load configuration from file" ) . Short ( 'c' ) . Short ( 'f' ) . PlaceHolder ( ".goreleaser.yml" ) . String ( )
2018-03-11 17:03:35 +02:00
var releaseNotes = releaseCmd . Flag ( "release-notes" , "Load custom release notes from a markdown file" ) . PlaceHolder ( "notes.md" ) . String ( )
2018-03-11 17:00:47 +02:00
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 ( )
2018-05-24 10:50:14 +02:00
var skipSign = releaseCmd . Flag ( "skip-sign" , "Skips signing the artifacts" ) . Bool ( )
2018-03-11 17:00:47 +02:00
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 ( ) :
2017-12-06 05:02:52 +02:00
start := time . Now ( )
2018-03-11 17:00:47 +02:00
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 ,
2018-05-24 10:50:14 +02:00
SkipSign : * skipSign ,
2018-03-11 17:00:47 +02:00
RmDist : * rmDist ,
Parallelism : * parallelism ,
Debug : * debug ,
Timeout : * timeout ,
2017-04-21 20:25:32 +02:00
}
2018-03-11 17:00:47 +02:00
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 ( ) ) )
2017-04-21 20:25:32 +02:00
}
2018-03-11 17:00:47 +02:00
}
2017-04-27 11:03:26 +02:00
2018-03-11 17:00:47 +02:00
func terminate ( status int ) {
os . Exit ( status )
2017-04-21 20:25:32 +02:00
}
2018-02-22 01:04:22 +02:00
2018-03-11 17:00:47 +02:00
func releaseProject ( options releaseOptions ) error {
if options . Debug {
2018-02-22 01:04:22 +02:00
log . SetLevel ( log . DebugLevel )
}
2018-03-11 17:00:47 +02:00
cfg , err := loadConfig ( options . Config )
2018-02-22 01:04:22 +02:00
if err != nil {
2018-03-11 17:00:47 +02:00
return err
2018-02-22 01:04:22 +02:00
}
2018-03-11 17:00:47 +02:00
ctx , cancel := context . NewWithTimeout ( cfg , options . Timeout )
2018-02-22 01:04:22 +02:00
defer cancel ( )
2018-03-11 17:00:47 +02:00
ctx . Parallelism = options . Parallelism
ctx . Debug = options . Debug
2018-02-22 01:04:22 +02:00
log . Debugf ( "parallelism: %v" , ctx . Parallelism )
2018-03-11 17:00:47 +02:00
if options . ReleaseNotes != "" {
bts , err := ioutil . ReadFile ( options . ReleaseNotes )
2018-02-22 01:04:22 +02:00
if err != nil {
return err
}
2018-03-11 17:00:47 +02:00
log . WithField ( "file" , options . ReleaseNotes ) . Info ( "loaded custom release notes" )
log . WithField ( "file" , options . ReleaseNotes ) . Debugf ( "custom release notes: \n%s" , string ( bts ) )
2018-02-22 01:04:22 +02:00
ctx . ReleaseNotes = string ( bts )
}
2018-03-11 17:00:47 +02:00
ctx . Snapshot = options . Snapshot
ctx . SkipPublish = ctx . Snapshot || options . SkipPublish
ctx . SkipValidate = ctx . Snapshot || options . SkipValidate
2018-05-24 10:50:14 +02:00
ctx . SkipSign = options . SkipSign
2018-03-11 17:00:47 +02:00
ctx . RmDist = options . RmDist
2018-02-22 01:04:22 +02:00
return doRelease ( ctx )
}
func doRelease ( ctx * context . Context ) error {
2018-03-11 17:00:47 +02:00
defer func ( ) { cli . Default . Padding = 3 } ( )
var release = func ( ) error {
2018-02-22 01:04:22 +02:00
for _ , pipe := range pipes {
2018-03-11 17:00:47 +02:00
cli . Default . Padding = 3
2018-02-22 01:04:22 +02:00
log . Infof ( color . New ( color . Bold ) . Sprint ( strings . ToUpper ( pipe . String ( ) ) ) )
2018-03-11 17:00:47 +02:00
cli . Default . Padding = 6
2018-02-22 01:04:22 +02:00
if err := handle ( pipe . Run ( ctx ) ) ; err != nil {
return err
}
}
return nil
2018-03-11 17:00:47 +02:00
}
return ctrlc . Default . Run ( ctx , release )
2018-02-22 01:04:22 +02:00
}
func handle ( err error ) error {
if err == nil {
return nil
}
if pipeline . 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 )
}
2018-03-11 17:00:47 +02:00
log . Infof ( color . New ( color . Bold ) . Sprintf ( "Generating %s file" , filename ) )
2018-03-02 04:37:15 +02:00
return ioutil . WriteFile ( filename , [ ] byte ( exampleConfig ) , 0644 )
2018-02-22 01:04:22 +02:00
}
2018-03-11 17:00:47 +02:00
func loadConfig ( path string ) ( config . Project , error ) {
if path != "" {
return config . Load ( path )
2018-02-22 01:04:22 +02:00
}
2018-03-11 17:00:47 +02:00
for _ , f := range [ 4 ] string {
2018-02-22 01:04:22 +02:00
".goreleaser.yml" ,
".goreleaser.yaml" ,
"goreleaser.yml" ,
"goreleaser.yaml" ,
} {
2018-03-11 17:00:47 +02:00
proj , err := config . Load ( f )
if err != nil && os . IsNotExist ( err ) {
continue
2018-02-22 01:04:22 +02:00
}
2018-03-11 17:00:47 +02:00
return proj , err
2018-02-22 01:04:22 +02:00
}
2018-03-11 17:00:47 +02:00
// 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
2018-02-22 01:04:22 +02:00
}
2018-03-02 04:37:15 +02:00
var exampleConfig = ` # This is an example goreleaser . yaml file with some sane defaults .
# Make sure to check the documentation at http : //goreleaser.com
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 : '
`