diff --git a/.goreleaser.yml b/.goreleaser.yml index 2e55e801b..01e2a4487 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -23,12 +23,12 @@ changelog: - '^test:' - Merge pull request - Merge branch -dockers: -- image: goreleaser/goreleaser - tag_templates: - - '{{ .Tag }}' - - 'v{{ .Major }}.{{ .Minor }}' - - 'latest' +# dockers: +# - image: goreleaser/goreleaser +# tag_templates: +# - '{{ .Tag }}' +# - 'v{{ .Major }}.{{ .Minor }}' +# - 'latest' archive: name_template: '{{ .ProjectName }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}' replacements: @@ -79,3 +79,6 @@ snapcraft: wrapped in your favorite CI. grade: stable confinement: classic +s3: +- bucket: goreleaser + diff --git a/Gopkg.lock b/Gopkg.lock index de1f7d517..82c03480a 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -31,6 +31,40 @@ ] revision = "ff0f66940b829dc66c81dad34746d4349b83eb9e" +[[projects]] + name = "github.com/aws/aws-sdk-go" + packages = [ + "aws", + "aws/awserr", + "aws/awsutil", + "aws/client", + "aws/client/metadata", + "aws/corehandlers", + "aws/credentials", + "aws/credentials/ec2rolecreds", + "aws/credentials/endpointcreds", + "aws/credentials/stscreds", + "aws/defaults", + "aws/ec2metadata", + "aws/endpoints", + "aws/request", + "aws/session", + "aws/signer/v4", + "internal/sdkio", + "internal/sdkrand", + "internal/shareddefaults", + "private/protocol", + "private/protocol/query", + "private/protocol/query/queryutil", + "private/protocol/rest", + "private/protocol/restxml", + "private/protocol/xml/xmlutil", + "service/s3", + "service/sts" + ] + revision = "31a85efbe3bc741eb539d6310c8e66030b7c5cb7" + version = "v1.13.47" + [[projects]] branch = "master" name = "github.com/blakesmith/ar" @@ -61,6 +95,12 @@ revision = "507f6050b8568533fb3f5504de8e5205fa62a114" version = "v1.6.0" +[[projects]] + name = "github.com/go-ini/ini" + packages = ["."] + revision = "6529cf7c58879c08d927016dde4477f18a0634cb" + version = "v1.36.0" + [[projects]] name = "github.com/golang/protobuf" packages = ["proto"] @@ -106,6 +146,11 @@ revision = "9d5f1277e9a8ed20c3684bda8fde67c05628518c" version = "v0.3.4" +[[projects]] + name = "github.com/jmespath/go-jmespath" + packages = ["."] + revision = "0b12d6b5" + [[projects]] name = "github.com/masterminds/semver" packages = ["."] @@ -213,6 +258,6 @@ [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "f59fe644e96665a3145d68ef6c134f664ef0b0b4f11c37dd6569cc66ffdee16b" + inputs-digest = "a47b30326926a615c574552d71af84d17e5198486ad61bed0d63672ebaba696d" solver-name = "gps-cdcl" solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml index 45322a7d2..398b4adae 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -53,3 +53,7 @@ [[constraint]] name = "github.com/alecthomas/kingpin" version = "~2.2.6" + +[[constraint]] + name = "github.com/aws/aws-sdk-go" + version = "1.13.47" diff --git a/config/config.go b/config/config.go index 43fbc9221..1bc998956 100644 --- a/config/config.go +++ b/config/config.go @@ -244,6 +244,13 @@ type Before struct { Hooks []string `yaml:",omitempty"` } +// S3 contains s3 config +type S3 struct { + Region string + Bucket string + Folder string +} + // Project includes all project configuration type Project struct { ProjectName string `yaml:"project_name,omitempty"` @@ -259,6 +266,7 @@ type Project struct { Checksum Checksum `yaml:",omitempty"` Dockers []Docker `yaml:",omitempty"` Artifactories []Artifactory `yaml:",omitempty"` + S3 []S3 `yaml:"s3,omitempty"` Changelog Changelog `yaml:",omitempty"` Dist string `yaml:",omitempty"` Sign Sign `yaml:",omitempty"` diff --git a/internal/client/github.go b/internal/client/github.go index abb80c078..800abcc22 100644 --- a/internal/client/github.go +++ b/internal/client/github.go @@ -9,6 +9,7 @@ import ( "github.com/google/go-github/github" "github.com/goreleaser/goreleaser/config" "github.com/goreleaser/goreleaser/context" + "github.com/goreleaser/goreleaser/internal/nametemplate" "golang.org/x/oauth2" ) @@ -89,7 +90,7 @@ func (c *githubClient) CreateFile( func (c *githubClient) CreateRelease(ctx *context.Context, body string) (int64, error) { var release *github.RepositoryRelease - title, err := releaseTitle(ctx) + title, err := nametemplate.Apply(ctx, "github", ctx.Config.Release.NameTemplate) if err != nil { return 0, err } diff --git a/internal/client/name.go b/internal/nametemplate/name.go similarity index 78% rename from internal/client/name.go rename to internal/nametemplate/name.go index 236f307bf..7a813f200 100644 --- a/internal/client/name.go +++ b/internal/nametemplate/name.go @@ -1,4 +1,4 @@ -package client +package nametemplate import ( "bytes" @@ -8,12 +8,12 @@ import ( "github.com/goreleaser/goreleaser/context" ) -func releaseTitle(ctx *context.Context) (string, error) { +func Apply(ctx *context.Context, name, tmpl string) (string, error) { var out bytes.Buffer - t, err := template.New("github"). + t, err := template.New(name). Option("missingkey=error"). Funcs(mkFuncMap()). - Parse(ctx.Config.Release.NameTemplate) + Parse(tmpl) if err != nil { return "", err } diff --git a/internal/client/name_test.go b/internal/nametemplate/name_test.go similarity index 86% rename from internal/client/name_test.go rename to internal/nametemplate/name_test.go index 7c5e13979..8aeb41936 100644 --- a/internal/client/name_test.go +++ b/internal/nametemplate/name_test.go @@ -1,4 +1,4 @@ -package client +package nametemplate import ( "testing" @@ -29,8 +29,7 @@ func TestFuncMap(t *testing.T) { Name: "MM/DD/YYYY", }, } { - ctx.Config.Release.NameTemplate = tc.Template - out, err := releaseTitle(ctx) + out, err := Apply(ctx, "foo", tc.Template) assert.NoError(t, err) assert.NotEmpty(t, out) } diff --git a/main.go b/main.go index cbac21034..ab0f0db62 100644 --- a/main.go +++ b/main.go @@ -31,6 +31,7 @@ import ( "github.com/goreleaser/goreleaser/pipeline/git" "github.com/goreleaser/goreleaser/pipeline/nfpm" "github.com/goreleaser/goreleaser/pipeline/release" + "github.com/goreleaser/goreleaser/pipeline/s3" "github.com/goreleaser/goreleaser/pipeline/scoop" "github.com/goreleaser/goreleaser/pipeline/sign" "github.com/goreleaser/goreleaser/pipeline/snapcraft" @@ -58,6 +59,7 @@ var pipes = []Piper{ sign.Pipe{}, // sign artifacts docker.Pipe{}, // create and push docker images artifactory.Pipe{}, // push to artifactory + s3.Pipe{}, // push to s3/minio release.Pipe{}, // release to github brew.Pipe{}, // push to brew tap scoop.Pipe{}, // push to scoop bucket diff --git a/pipeline/defaults/defaults.go b/pipeline/defaults/defaults.go index bc02501ae..026857bf7 100644 --- a/pipeline/defaults/defaults.go +++ b/pipeline/defaults/defaults.go @@ -18,6 +18,7 @@ import ( "github.com/goreleaser/goreleaser/pipeline/nfpm" "github.com/goreleaser/goreleaser/pipeline/project" "github.com/goreleaser/goreleaser/pipeline/release" + "github.com/goreleaser/goreleaser/pipeline/s3" "github.com/goreleaser/goreleaser/pipeline/scoop" "github.com/goreleaser/goreleaser/pipeline/sign" "github.com/goreleaser/goreleaser/pipeline/snapcraft" @@ -54,6 +55,7 @@ var defaulters = []Defaulter{ sign.Pipe{}, docker.Pipe{}, artifactory.Pipe{}, + s3.Pipe{}, brew.Pipe{}, scoop.Pipe{}, } diff --git a/pipeline/s3/s3.go b/pipeline/s3/s3.go new file mode 100644 index 000000000..c06579881 --- /dev/null +++ b/pipeline/s3/s3.go @@ -0,0 +1,103 @@ +// Package s3 provides a Pipe that push artifacts to s3/minio +package s3 + +import ( + "os" + "path/filepath" + + "github.com/apex/log" + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/s3" + "github.com/goreleaser/goreleaser/config" + "github.com/goreleaser/goreleaser/context" + "github.com/goreleaser/goreleaser/internal/artifact" + "github.com/goreleaser/goreleaser/internal/nametemplate" + "golang.org/x/sync/errgroup" +) + +// Pipe for Artifactory +type Pipe struct{} + +// String returns the description of the pipe +func (Pipe) String() string { + return "releasing to s3" +} + +// Default sets the pipe defaults +func (Pipe) Default(ctx *context.Context) error { + for i := range ctx.Config.S3 { + s3 := &ctx.Config.S3[i] + if s3.Folder == "" { + s3.Folder = "{{ .ProjectName }}/{{ .Tag }}" + } + if s3.Region == "" { + s3.Region = "us-east-1" + } + } + return nil +} + +// Run the pipe +func (Pipe) Run(ctx *context.Context) error { + var g errgroup.Group + sem := make(chan bool, ctx.Parallelism) + for _, conf := range ctx.Config.S3 { + conf := conf + sem <- true + g.Go(func() error { + defer func() { + <-sem + }() + return upload(ctx, conf) + }) + } + return g.Wait() +} + +func upload(ctx *context.Context, conf config.S3) error { + sess := session.Must(session.NewSession()) + svc := s3.New(sess, &aws.Config{ + Region: aws.String(conf.Region), + }) + folder, err := nametemplate.Apply(ctx, "s3", conf.Folder) + if err != nil { + return err + } + + var g errgroup.Group + sem := make(chan bool, ctx.Parallelism) + for _, artifact := range ctx.Artifacts.Filter( + artifact.Or( + artifact.ByType(artifact.UploadableArchive), + artifact.ByType(artifact.UploadableBinary), + artifact.ByType(artifact.Checksum), + artifact.ByType(artifact.Signature), + artifact.ByType(artifact.LinuxPackage), + ), + ).List() { + sem <- true + artifact := artifact + g.Go(func() error { + defer func() { + <-sem + }() + f, err := os.Open(artifact.Path) + if err != nil { + return err + } + log.WithFields(log.Fields{ + "bucket": conf.Bucket, + "folder": folder, + "artifact": artifact.Name, + }).Info("uploading") + _, err = svc.PutObjectWithContext(ctx, &s3.PutObjectInput{ + Bucket: aws.String(conf.Bucket), + Key: aws.String(filepath.Join(folder, artifact.Name)), + Body: f, + }) + return err + }) + } + return g.Wait() +} diff --git a/pipeline/s3/s3_test.go b/pipeline/s3/s3_test.go new file mode 100644 index 000000000..cb2149250 --- /dev/null +++ b/pipeline/s3/s3_test.go @@ -0,0 +1,17 @@ +package s3 + +import ( + "testing" + + "github.com/goreleaser/goreleaser/config" + "github.com/goreleaser/goreleaser/context" + "github.com/stretchr/testify/assert" +) + +func TestDescription(t *testing.T) { + assert.NotEmpty(t, Pipe{}.String()) +} + +func TestNoS3(t *testing.T) { + assert.NoError(t, Pipe{}.Run(context.New(config.Project{}))) +}