mirror of
https://github.com/goreleaser/goreleaser.git
synced 2025-04-15 11:56:56 +02:00
feat: remove deprecated s3 pipe (#1291)
* feat: remove deprecated s3 pipe Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com> * fix: go mod tidy Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com>
This commit is contained in:
parent
f1fc2c03bd
commit
5f2cf501e8
2
go.mod
2
go.mod
@ -6,7 +6,7 @@ require (
|
|||||||
code.gitea.io/sdk/gitea v0.0.0-20191013013401-e41e9ea72caa
|
code.gitea.io/sdk/gitea v0.0.0-20191013013401-e41e9ea72caa
|
||||||
github.com/Masterminds/semver/v3 v3.0.3
|
github.com/Masterminds/semver/v3 v3.0.3
|
||||||
github.com/apex/log v1.1.1
|
github.com/apex/log v1.1.1
|
||||||
github.com/aws/aws-sdk-go v1.25.11
|
github.com/aws/aws-sdk-go v1.25.11 // indirect
|
||||||
github.com/caarlos0/ctrlc v1.0.0
|
github.com/caarlos0/ctrlc v1.0.0
|
||||||
github.com/campoy/unique v0.0.0-20180121183637-88950e537e7e
|
github.com/campoy/unique v0.0.0-20180121183637-88950e537e7e
|
||||||
github.com/fatih/color v1.9.0
|
github.com/fatih/color v1.9.0
|
||||||
|
@ -11,7 +11,6 @@ import (
|
|||||||
"github.com/goreleaser/goreleaser/internal/pipe/brew"
|
"github.com/goreleaser/goreleaser/internal/pipe/brew"
|
||||||
"github.com/goreleaser/goreleaser/internal/pipe/docker"
|
"github.com/goreleaser/goreleaser/internal/pipe/docker"
|
||||||
"github.com/goreleaser/goreleaser/internal/pipe/release"
|
"github.com/goreleaser/goreleaser/internal/pipe/release"
|
||||||
"github.com/goreleaser/goreleaser/internal/pipe/s3"
|
|
||||||
"github.com/goreleaser/goreleaser/internal/pipe/scoop"
|
"github.com/goreleaser/goreleaser/internal/pipe/scoop"
|
||||||
"github.com/goreleaser/goreleaser/internal/pipe/snapcraft"
|
"github.com/goreleaser/goreleaser/internal/pipe/snapcraft"
|
||||||
"github.com/goreleaser/goreleaser/internal/pipe/upload"
|
"github.com/goreleaser/goreleaser/internal/pipe/upload"
|
||||||
@ -36,7 +35,6 @@ type Publisher interface {
|
|||||||
|
|
||||||
// nolint: gochecknoglobals
|
// nolint: gochecknoglobals
|
||||||
var publishers = []Publisher{
|
var publishers = []Publisher{
|
||||||
s3.Pipe{},
|
|
||||||
blob.Pipe{},
|
blob.Pipe{},
|
||||||
upload.Pipe{},
|
upload.Pipe{},
|
||||||
artifactory.Pipe{},
|
artifactory.Pipe{},
|
||||||
|
@ -1,86 +0,0 @@
|
|||||||
package s3
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
|
||||||
"github.com/aws/aws-sdk-go/aws/credentials"
|
|
||||||
"github.com/aws/aws-sdk-go/aws/credentials/stscreds"
|
|
||||||
"github.com/aws/aws-sdk-go/aws/session"
|
|
||||||
)
|
|
||||||
|
|
||||||
type SessionBuilder interface {
|
|
||||||
Config(*aws.Config) SessionBuilder
|
|
||||||
Profile(string) SessionBuilder
|
|
||||||
Options(*session.Options) SessionBuilder
|
|
||||||
Endpoint(string) SessionBuilder
|
|
||||||
S3ForcePathStyle(bool) SessionBuilder
|
|
||||||
Build() *session.Session
|
|
||||||
}
|
|
||||||
|
|
||||||
type sessionBuilder struct {
|
|
||||||
awsConfig *aws.Config
|
|
||||||
profile string
|
|
||||||
options *session.Options
|
|
||||||
endpoint *string
|
|
||||||
forcePathStyle *bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sb *sessionBuilder) Config(c *aws.Config) SessionBuilder {
|
|
||||||
sb.awsConfig = c
|
|
||||||
return sb
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sb *sessionBuilder) Profile(p string) SessionBuilder {
|
|
||||||
sb.profile = p
|
|
||||||
return sb
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sb *sessionBuilder) Endpoint(e string) SessionBuilder {
|
|
||||||
sb.endpoint = aws.String(e)
|
|
||||||
return sb
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sb *sessionBuilder) S3ForcePathStyle(b bool) SessionBuilder {
|
|
||||||
sb.forcePathStyle = aws.Bool(b)
|
|
||||||
return sb
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sb *sessionBuilder) Options(o *session.Options) SessionBuilder {
|
|
||||||
sb.options = o
|
|
||||||
return sb
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sb *sessionBuilder) Build() *session.Session {
|
|
||||||
if sb.awsConfig == nil {
|
|
||||||
sb.awsConfig = aws.NewConfig()
|
|
||||||
}
|
|
||||||
|
|
||||||
if sb.endpoint != nil {
|
|
||||||
sb.awsConfig.Endpoint = sb.endpoint
|
|
||||||
sb.awsConfig.S3ForcePathStyle = sb.forcePathStyle
|
|
||||||
}
|
|
||||||
|
|
||||||
sb.awsConfig.Credentials = credentials.NewChainCredentials([]credentials.Provider{
|
|
||||||
&credentials.EnvProvider{},
|
|
||||||
&credentials.SharedCredentialsProvider{
|
|
||||||
Profile: sb.profile,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
_, err := sb.awsConfig.Credentials.Get()
|
|
||||||
if err == nil {
|
|
||||||
return session.Must(session.NewSession(sb.awsConfig))
|
|
||||||
}
|
|
||||||
if sb.options == nil {
|
|
||||||
sb.options = &session.Options{
|
|
||||||
AssumeRoleTokenProvider: stscreds.StdinTokenProvider,
|
|
||||||
SharedConfigState: session.SharedConfigEnable,
|
|
||||||
Profile: sb.profile,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return session.Must(session.NewSessionWithOptions(*sb.options))
|
|
||||||
}
|
|
||||||
|
|
||||||
func newSessionBuilder() SessionBuilder {
|
|
||||||
return &sessionBuilder{}
|
|
||||||
}
|
|
@ -1,202 +0,0 @@
|
|||||||
package s3
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
"net/http/httptest"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
|
||||||
"github.com/aws/aws-sdk-go/aws/session"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func setEnv() {
|
|
||||||
os.Setenv("AWS_ACCESS_KEY_ID", "accessKey")
|
|
||||||
os.Setenv("AWS_SECRET_ACCESS_KEY", "secret")
|
|
||||||
}
|
|
||||||
|
|
||||||
func clearnEnv() {
|
|
||||||
os.Unsetenv("AWS_ACCESS_KEY_ID")
|
|
||||||
os.Unsetenv("AWS_SECRET_ACCESS_KEY")
|
|
||||||
os.Unsetenv("AWS_SHARED_CREDENTIALS_FILE")
|
|
||||||
os.Unsetenv("AWS_CONFIG_FILE")
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_awsSession(t *testing.T) {
|
|
||||||
type args struct {
|
|
||||||
profile string
|
|
||||||
}
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
args args
|
|
||||||
want *session.Session
|
|
||||||
before func()
|
|
||||||
expectToken string
|
|
||||||
endpoint string
|
|
||||||
S3ForcePathStyle bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "test endpoint",
|
|
||||||
before: setEnv,
|
|
||||||
endpoint: "test",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "test S3ForcePathStyle",
|
|
||||||
before: setEnv,
|
|
||||||
S3ForcePathStyle: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "test env provider",
|
|
||||||
args: args{
|
|
||||||
profile: "test1",
|
|
||||||
},
|
|
||||||
before: setEnv,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "test default shared credentials provider",
|
|
||||||
before: func() {
|
|
||||||
clearnEnv()
|
|
||||||
os.Setenv("AWS_SHARED_CREDENTIALS_FILE", filepath.Join("testdata", "credentials.ini"))
|
|
||||||
},
|
|
||||||
expectToken: "token",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "test default shared credentials provider",
|
|
||||||
before: func() {
|
|
||||||
clearnEnv()
|
|
||||||
os.Setenv("AWS_SHARED_CREDENTIALS_FILE", filepath.Join("testdata", "credentials.ini"))
|
|
||||||
},
|
|
||||||
expectToken: "token",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "test profile with shared credentials provider",
|
|
||||||
before: func() {
|
|
||||||
clearnEnv()
|
|
||||||
os.Setenv("AWS_SHARED_CREDENTIALS_FILE", filepath.Join("testdata", "credentials.ini"))
|
|
||||||
},
|
|
||||||
args: args{
|
|
||||||
profile: "no_token",
|
|
||||||
},
|
|
||||||
expectToken: "",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
clearnEnv()
|
|
||||||
defer clearnEnv()
|
|
||||||
if tt.before != nil {
|
|
||||||
tt.before()
|
|
||||||
}
|
|
||||||
|
|
||||||
builder := newSessionBuilder()
|
|
||||||
builder.Profile(tt.args.profile)
|
|
||||||
builder.Endpoint(tt.endpoint)
|
|
||||||
builder.S3ForcePathStyle(tt.S3ForcePathStyle)
|
|
||||||
sess := builder.Build()
|
|
||||||
assert.NotNil(t, sess)
|
|
||||||
|
|
||||||
creds, err := sess.Config.Credentials.Get()
|
|
||||||
assert.Nil(t, err)
|
|
||||||
|
|
||||||
assert.Equal(t, "accessKey", creds.AccessKeyID, "Expect access key ID to match")
|
|
||||||
assert.Equal(t, "secret", creds.SecretAccessKey, "Expect secret access key to match")
|
|
||||||
assert.Equal(t, tt.expectToken, creds.SessionToken, "Expect token to match")
|
|
||||||
|
|
||||||
assert.Equal(t, aws.String(tt.endpoint), sess.Config.Endpoint, "Expect endpoint to match")
|
|
||||||
assert.Equal(t, aws.Bool(tt.S3ForcePathStyle), sess.Config.S3ForcePathStyle, "Expect S3ForcePathStyle to match")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const assumeRoleRespMsg = `
|
|
||||||
<AssumeRoleResponse xmlns="https://sts.amazonaws.com/doc/2011-06-15/">
|
|
||||||
<AssumeRoleResult>
|
|
||||||
<AssumedRoleUser>
|
|
||||||
<Arn>arn:aws:sts::account_id:assumed-role/role/session_name</Arn>
|
|
||||||
<AssumedRoleId>AKID:session_name</AssumedRoleId>
|
|
||||||
</AssumedRoleUser>
|
|
||||||
<Credentials>
|
|
||||||
<AccessKeyId>AKID</AccessKeyId>
|
|
||||||
<SecretAccessKey>SECRET</SecretAccessKey>
|
|
||||||
<SessionToken>SESSION_TOKEN</SessionToken>
|
|
||||||
<Expiration>%s</Expiration>
|
|
||||||
</Credentials>
|
|
||||||
</AssumeRoleResult>
|
|
||||||
<ResponseMetadata>
|
|
||||||
<RequestId>request-id</RequestId>
|
|
||||||
</ResponseMetadata>
|
|
||||||
</AssumeRoleResponse>
|
|
||||||
`
|
|
||||||
|
|
||||||
func Test_awsSession_mfa(t *testing.T) {
|
|
||||||
clearnEnv()
|
|
||||||
defer clearnEnv()
|
|
||||||
os.Setenv("AWS_SHARED_CREDENTIALS_FILE", filepath.Join("testdata", "credentials.ini"))
|
|
||||||
os.Setenv("AWS_CONFIG_FILE", filepath.Join("testdata", "config.ini"))
|
|
||||||
|
|
||||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
assert.Equal(t, r.FormValue("SerialNumber"), "arn:aws:iam::1111111111:mfa/test")
|
|
||||||
assert.Equal(t, r.FormValue("TokenCode"), "tokencode")
|
|
||||||
|
|
||||||
_, err := w.Write([]byte(fmt.Sprintf(assumeRoleRespMsg, time.Now().Add(15*time.Minute).Format("2006-01-02T15:04:05Z"))))
|
|
||||||
assert.NoError(t, err)
|
|
||||||
}))
|
|
||||||
|
|
||||||
customProviderCalled := false
|
|
||||||
|
|
||||||
options := &session.Options{
|
|
||||||
Profile: "cloudformation@flowlab-dev",
|
|
||||||
Config: aws.Config{
|
|
||||||
Region: aws.String("eu-west-1"),
|
|
||||||
Endpoint: aws.String(server.URL),
|
|
||||||
DisableSSL: aws.Bool(true),
|
|
||||||
},
|
|
||||||
SharedConfigState: session.SharedConfigEnable,
|
|
||||||
AssumeRoleTokenProvider: func() (string, error) {
|
|
||||||
customProviderCalled = true
|
|
||||||
return "tokencode", nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
builder := newSessionBuilder()
|
|
||||||
builder.Profile("cloudformation@flowlab-dev")
|
|
||||||
builder.Options(options)
|
|
||||||
sess := builder.Build()
|
|
||||||
|
|
||||||
creds, err := sess.Config.Credentials.Get()
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.True(t, customProviderCalled)
|
|
||||||
assert.Contains(t, creds.ProviderName, "AssumeRoleProvider")
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_awsSession_fail(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "should fail with no credentials",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
clearnEnv()
|
|
||||||
defer clearnEnv()
|
|
||||||
os.Setenv("AWS_SHARED_CREDENTIALS_FILE", "/nope")
|
|
||||||
|
|
||||||
builder := newSessionBuilder()
|
|
||||||
sess := builder.Build()
|
|
||||||
assert.NotNil(t, sess)
|
|
||||||
|
|
||||||
_, err := sess.Config.Credentials.Get()
|
|
||||||
assert.NotNil(t, err)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,126 +0,0 @@
|
|||||||
// 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/service/s3"
|
|
||||||
"github.com/goreleaser/goreleaser/internal/artifact"
|
|
||||||
"github.com/goreleaser/goreleaser/internal/deprecate"
|
|
||||||
"github.com/goreleaser/goreleaser/internal/pipe"
|
|
||||||
"github.com/goreleaser/goreleaser/internal/semerrgroup"
|
|
||||||
"github.com/goreleaser/goreleaser/internal/tmpl"
|
|
||||||
"github.com/goreleaser/goreleaser/pkg/config"
|
|
||||||
"github.com/goreleaser/goreleaser/pkg/context"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Pipe for Artifactory
|
|
||||||
type Pipe struct{}
|
|
||||||
|
|
||||||
// String returns the description of the pipe
|
|
||||||
func (Pipe) String() string {
|
|
||||||
return "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.Bucket == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
deprecate.Notice("s3")
|
|
||||||
if s3.Folder == "" {
|
|
||||||
s3.Folder = "{{ .ProjectName }}/{{ .Tag }}"
|
|
||||||
}
|
|
||||||
if s3.Region == "" {
|
|
||||||
s3.Region = "us-east-1"
|
|
||||||
}
|
|
||||||
if s3.ACL == "" {
|
|
||||||
s3.ACL = "private"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Publish to S3
|
|
||||||
func (Pipe) Publish(ctx *context.Context) error {
|
|
||||||
if len(ctx.Config.S3) == 0 {
|
|
||||||
return pipe.Skip("s3 section is not configured")
|
|
||||||
}
|
|
||||||
var g = semerrgroup.New(ctx.Parallelism)
|
|
||||||
for _, conf := range ctx.Config.S3 {
|
|
||||||
conf := conf
|
|
||||||
g.Go(func() error {
|
|
||||||
return upload(ctx, conf)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return g.Wait()
|
|
||||||
}
|
|
||||||
|
|
||||||
func newS3Svc(conf config.S3) *s3.S3 {
|
|
||||||
builder := newSessionBuilder()
|
|
||||||
builder.Profile(conf.Profile)
|
|
||||||
if conf.Endpoint != "" {
|
|
||||||
builder.Endpoint(conf.Endpoint)
|
|
||||||
builder.S3ForcePathStyle(true)
|
|
||||||
}
|
|
||||||
sess := builder.Build()
|
|
||||||
|
|
||||||
return s3.New(sess, &aws.Config{
|
|
||||||
Region: aws.String(conf.Region),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func upload(ctx *context.Context, conf config.S3) error {
|
|
||||||
var svc = newS3Svc(conf)
|
|
||||||
|
|
||||||
template := tmpl.New(ctx)
|
|
||||||
bucket, err := template.Apply(conf.Bucket)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
folder, err := template.Apply(conf.Folder)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
var filter = artifact.Or(
|
|
||||||
artifact.ByType(artifact.UploadableArchive),
|
|
||||||
artifact.ByType(artifact.UploadableBinary),
|
|
||||||
artifact.ByType(artifact.Checksum),
|
|
||||||
artifact.ByType(artifact.Signature),
|
|
||||||
artifact.ByType(artifact.LinuxPackage),
|
|
||||||
)
|
|
||||||
if len(conf.IDs) > 0 {
|
|
||||||
filter = artifact.And(filter, artifact.ByIDs(conf.IDs...))
|
|
||||||
}
|
|
||||||
|
|
||||||
var g = semerrgroup.New(ctx.Parallelism)
|
|
||||||
for _, artifact := range ctx.Artifacts.Filter(filter).List() {
|
|
||||||
artifact := artifact
|
|
||||||
g.Go(func() error {
|
|
||||||
f, err := os.Open(artifact.Path)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
log.WithFields(log.Fields{
|
|
||||||
"bucket": bucket,
|
|
||||||
"folder": folder,
|
|
||||||
"artifact": artifact.Name,
|
|
||||||
}).Info("uploading")
|
|
||||||
_, err = svc.PutObjectWithContext(ctx, &s3.PutObjectInput{
|
|
||||||
Bucket: aws.String(bucket),
|
|
||||||
Key: aws.String(filepath.Join(folder, artifact.Name)),
|
|
||||||
Body: f,
|
|
||||||
ACL: aws.String(conf.ACL),
|
|
||||||
})
|
|
||||||
return err
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return g.Wait()
|
|
||||||
}
|
|
@ -1,236 +0,0 @@
|
|||||||
package s3
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io/ioutil"
|
|
||||||
"net"
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
|
||||||
"github.com/aws/aws-sdk-go/service/s3"
|
|
||||||
"github.com/goreleaser/goreleaser/internal/artifact"
|
|
||||||
"github.com/goreleaser/goreleaser/internal/testlib"
|
|
||||||
"github.com/goreleaser/goreleaser/pkg/config"
|
|
||||||
"github.com/goreleaser/goreleaser/pkg/context"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestDescription(t *testing.T) {
|
|
||||||
assert.NotEmpty(t, Pipe{}.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNoS3(t *testing.T) {
|
|
||||||
testlib.AssertSkipped(t, Pipe{}.Publish(context.New(config.Project{})))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDefaultsNoS3(t *testing.T) {
|
|
||||||
var assert = assert.New(t)
|
|
||||||
var ctx = context.New(config.Project{
|
|
||||||
S3: []config.S3{
|
|
||||||
{},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
assert.NoError(Pipe{}.Default(ctx))
|
|
||||||
assert.Equal([]config.S3{{}}, ctx.Config.S3)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDefaults(t *testing.T) {
|
|
||||||
var assert = assert.New(t)
|
|
||||||
var ctx = context.New(config.Project{
|
|
||||||
S3: []config.S3{
|
|
||||||
{
|
|
||||||
Bucket: "foo",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
assert.NoError(Pipe{}.Default(ctx))
|
|
||||||
assert.Equal([]config.S3{{
|
|
||||||
Bucket: "foo",
|
|
||||||
Region: "us-east-1",
|
|
||||||
Folder: "{{ .ProjectName }}/{{ .Tag }}",
|
|
||||||
ACL: "private",
|
|
||||||
}}, ctx.Config.S3)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUpload(t *testing.T) {
|
|
||||||
var listen = randomListen(t)
|
|
||||||
folder, err := ioutil.TempDir("", "goreleasertest")
|
|
||||||
assert.NoError(t, err)
|
|
||||||
tgzpath := filepath.Join(folder, "bin.tar.gz")
|
|
||||||
debpath := filepath.Join(folder, "bin.deb")
|
|
||||||
assert.NoError(t, ioutil.WriteFile(tgzpath, []byte("fake\ntargz"), 0744))
|
|
||||||
assert.NoError(t, ioutil.WriteFile(debpath, []byte("fake\ndeb"), 0744))
|
|
||||||
var ctx = context.New(config.Project{
|
|
||||||
Dist: folder,
|
|
||||||
ProjectName: "testupload",
|
|
||||||
S3: []config.S3{
|
|
||||||
{
|
|
||||||
Bucket: "test",
|
|
||||||
Endpoint: "http://" + listen,
|
|
||||||
IDs: []string{"foo", "bar"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
ctx.Git = context.GitInfo{CurrentTag: "v1.0.0"}
|
|
||||||
ctx.Artifacts.Add(&artifact.Artifact{
|
|
||||||
Type: artifact.UploadableArchive,
|
|
||||||
Name: "bin.tar.gz",
|
|
||||||
Path: tgzpath,
|
|
||||||
Extra: map[string]interface{}{
|
|
||||||
"ID": "foo",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
ctx.Artifacts.Add(&artifact.Artifact{
|
|
||||||
Type: artifact.LinuxPackage,
|
|
||||||
Name: "bin.deb",
|
|
||||||
Path: debpath,
|
|
||||||
Extra: map[string]interface{}{
|
|
||||||
"ID": "bar",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
var name = "test_upload"
|
|
||||||
defer stop(t, name)
|
|
||||||
start(t, name, listen)
|
|
||||||
prepareEnv(t, listen)
|
|
||||||
assert.NoError(t, Pipe{}.Default(ctx))
|
|
||||||
assert.NoError(t, Pipe{}.Publish(ctx))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUploadCustomBucketID(t *testing.T) {
|
|
||||||
var listen = randomListen(t)
|
|
||||||
folder, err := ioutil.TempDir("", "goreleasertest")
|
|
||||||
assert.NoError(t, err)
|
|
||||||
tgzpath := filepath.Join(folder, "bin.tar.gz")
|
|
||||||
debpath := filepath.Join(folder, "bin.deb")
|
|
||||||
assert.NoError(t, ioutil.WriteFile(tgzpath, []byte("fake\ntargz"), 0744))
|
|
||||||
assert.NoError(t, ioutil.WriteFile(debpath, []byte("fake\ndeb"), 0744))
|
|
||||||
// Set custom BUCKET_ID env variable.
|
|
||||||
err = os.Setenv("BUCKET_ID", "test")
|
|
||||||
assert.NoError(t, err)
|
|
||||||
var ctx = context.New(config.Project{
|
|
||||||
Dist: folder,
|
|
||||||
ProjectName: "testupload",
|
|
||||||
S3: []config.S3{
|
|
||||||
{
|
|
||||||
Bucket: "{{.Env.BUCKET_ID}}",
|
|
||||||
Endpoint: "http://" + listen,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
ctx.Git = context.GitInfo{CurrentTag: "v1.0.0"}
|
|
||||||
ctx.Artifacts.Add(&artifact.Artifact{
|
|
||||||
Type: artifact.UploadableArchive,
|
|
||||||
Name: "bin.tar.gz",
|
|
||||||
Path: tgzpath,
|
|
||||||
})
|
|
||||||
ctx.Artifacts.Add(&artifact.Artifact{
|
|
||||||
Type: artifact.LinuxPackage,
|
|
||||||
Name: "bin.deb",
|
|
||||||
Path: debpath,
|
|
||||||
})
|
|
||||||
var name = "custom_bucket_id"
|
|
||||||
defer stop(t, name)
|
|
||||||
start(t, name, listen)
|
|
||||||
prepareEnv(t, listen)
|
|
||||||
assert.NoError(t, Pipe{}.Default(ctx))
|
|
||||||
assert.NoError(t, Pipe{}.Publish(ctx))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUploadInvalidCustomBucketID(t *testing.T) {
|
|
||||||
var listen = randomListen(t)
|
|
||||||
folder, err := ioutil.TempDir("", "goreleasertest")
|
|
||||||
assert.NoError(t, err)
|
|
||||||
tgzpath := filepath.Join(folder, "bin.tar.gz")
|
|
||||||
debpath := filepath.Join(folder, "bin.deb")
|
|
||||||
assert.NoError(t, ioutil.WriteFile(tgzpath, []byte("fake\ntargz"), 0744))
|
|
||||||
assert.NoError(t, ioutil.WriteFile(debpath, []byte("fake\ndeb"), 0744))
|
|
||||||
// Set custom BUCKET_ID env variable.
|
|
||||||
assert.NoError(t, err)
|
|
||||||
var ctx = context.New(config.Project{
|
|
||||||
Dist: folder,
|
|
||||||
ProjectName: "testupload",
|
|
||||||
S3: []config.S3{
|
|
||||||
{
|
|
||||||
Bucket: "{{.Bad}}",
|
|
||||||
Endpoint: "http://" + listen,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
ctx.Git = context.GitInfo{CurrentTag: "v1.0.0"}
|
|
||||||
ctx.Artifacts.Add(&artifact.Artifact{
|
|
||||||
Type: artifact.UploadableArchive,
|
|
||||||
Name: "bin.tar.gz",
|
|
||||||
Path: tgzpath,
|
|
||||||
})
|
|
||||||
ctx.Artifacts.Add(&artifact.Artifact{
|
|
||||||
Type: artifact.LinuxPackage,
|
|
||||||
Name: "bin.deb",
|
|
||||||
Path: debpath,
|
|
||||||
})
|
|
||||||
var name = "invalid_bucket_id"
|
|
||||||
defer stop(t, name)
|
|
||||||
start(t, name, listen)
|
|
||||||
prepareEnv(t, listen)
|
|
||||||
assert.NoError(t, Pipe{}.Default(ctx))
|
|
||||||
assert.Error(t, Pipe{}.Publish(ctx))
|
|
||||||
}
|
|
||||||
|
|
||||||
func randomListen(t *testing.T) string {
|
|
||||||
listener, err := net.Listen("tcp", "127.0.0.1:0")
|
|
||||||
require.NoError(t, err)
|
|
||||||
listener.Close()
|
|
||||||
return listener.Addr().String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func prepareEnv(t *testing.T, listen string) {
|
|
||||||
os.Setenv("AWS_ACCESS_KEY_ID", "minio")
|
|
||||||
os.Setenv("AWS_SECRET_ACCESS_KEY", "miniostorage")
|
|
||||||
os.Setenv("AWS_REGION", "us-east-1")
|
|
||||||
|
|
||||||
t.Log("creating test bucket")
|
|
||||||
_, err := newS3Svc(config.S3{
|
|
||||||
Endpoint: "http://" + listen,
|
|
||||||
Region: "us-east-1",
|
|
||||||
}).CreateBucket(&s3.CreateBucketInput{
|
|
||||||
Bucket: aws.String("test"),
|
|
||||||
})
|
|
||||||
require.NoError(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func start(t *testing.T, name, listen string) {
|
|
||||||
if out, err := exec.Command(
|
|
||||||
"docker", "run", "-d", "--rm",
|
|
||||||
"--name", name,
|
|
||||||
"-p", listen+":9000",
|
|
||||||
"-e", "MINIO_ACCESS_KEY=minio",
|
|
||||||
"-e", "MINIO_SECRET_KEY=miniostorage",
|
|
||||||
"--health-interval", "1s",
|
|
||||||
"minio/minio:RELEASE.2019-05-14T23-57-45Z",
|
|
||||||
"server", "/data",
|
|
||||||
).CombinedOutput(); err != nil {
|
|
||||||
t.Fatalf("failed to start minio: %s", string(out))
|
|
||||||
}
|
|
||||||
|
|
||||||
for range time.Tick(time.Second) {
|
|
||||||
out, err := exec.Command("docker", "inspect", "--format='{{json .State.Health}}'", name).CombinedOutput()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("failed to check minio status: %s", string(out))
|
|
||||||
}
|
|
||||||
if strings.Contains(string(out), `"Status":"healthy"`) {
|
|
||||||
t.Log("minio is healthy")
|
|
||||||
break
|
|
||||||
}
|
|
||||||
t.Log("waiting for minio to be healthy")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func stop(t *testing.T, name string) {
|
|
||||||
if out, err := exec.Command("docker", "stop", name).CombinedOutput(); err != nil {
|
|
||||||
t.Fatalf("failed to stop minio: %s", string(out))
|
|
||||||
}
|
|
||||||
}
|
|
4
internal/pipe/s3/testdata/config.ini
vendored
4
internal/pipe/s3/testdata/config.ini
vendored
@ -1,4 +0,0 @@
|
|||||||
[profile cloudformation@flowlab-dev]
|
|
||||||
role_arn = arn:aws:iam::1111111111:role/CloudFormation
|
|
||||||
source_profile = user
|
|
||||||
mfa_serial = arn:aws:iam::1111111111:mfa/test
|
|
12
internal/pipe/s3/testdata/credentials.ini
vendored
12
internal/pipe/s3/testdata/credentials.ini
vendored
@ -1,12 +0,0 @@
|
|||||||
[default]
|
|
||||||
aws_access_key_id = accessKey
|
|
||||||
aws_secret_access_key = secret
|
|
||||||
aws_session_token = token
|
|
||||||
|
|
||||||
[no_token]
|
|
||||||
aws_access_key_id = accessKey
|
|
||||||
aws_secret_access_key = secret
|
|
||||||
|
|
||||||
[user]
|
|
||||||
aws_access_key_id = accesKey
|
|
||||||
aws_secret_access_key = secret
|
|
@ -313,17 +313,6 @@ type Before struct {
|
|||||||
Hooks []string `yaml:",omitempty"`
|
Hooks []string `yaml:",omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// S3 contains s3 config
|
|
||||||
type S3 struct {
|
|
||||||
Region string `yaml:",omitempty"`
|
|
||||||
Bucket string `yaml:",omitempty"`
|
|
||||||
Folder string `yaml:",omitempty"`
|
|
||||||
Profile string `yaml:",omitempty"`
|
|
||||||
Endpoint string `yaml:",omitempty"` // used for minio for example
|
|
||||||
ACL string `yaml:",omitempty"`
|
|
||||||
IDs []string `yaml:"ids,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Blob contains config for GO CDK blob
|
// Blob contains config for GO CDK blob
|
||||||
type Blob struct {
|
type Blob struct {
|
||||||
Bucket string `yaml:",omitempty"`
|
Bucket string `yaml:",omitempty"`
|
||||||
@ -367,8 +356,7 @@ type Project struct {
|
|||||||
Dockers []Docker `yaml:",omitempty"`
|
Dockers []Docker `yaml:",omitempty"`
|
||||||
Artifactories []Upload `yaml:",omitempty"`
|
Artifactories []Upload `yaml:",omitempty"`
|
||||||
Uploads []Upload `yaml:",omitempty"`
|
Uploads []Upload `yaml:",omitempty"`
|
||||||
Puts []Upload `yaml:",omitempty"` // TODO: remove this
|
Puts []Upload `yaml:",omitempty"` // TODO: remove this
|
||||||
S3 []S3 `yaml:"s3,omitempty"`
|
|
||||||
Blob []Blob `yaml:"blob,omitempty"` // TODO: remove this
|
Blob []Blob `yaml:"blob,omitempty"` // TODO: remove this
|
||||||
Blobs []Blob `yaml:"blobs,omitempty"`
|
Blobs []Blob `yaml:"blobs,omitempty"`
|
||||||
Changelog Changelog `yaml:",omitempty"`
|
Changelog Changelog `yaml:",omitempty"`
|
||||||
|
@ -16,7 +16,6 @@ import (
|
|||||||
"github.com/goreleaser/goreleaser/internal/pipe/nfpm"
|
"github.com/goreleaser/goreleaser/internal/pipe/nfpm"
|
||||||
"github.com/goreleaser/goreleaser/internal/pipe/project"
|
"github.com/goreleaser/goreleaser/internal/pipe/project"
|
||||||
"github.com/goreleaser/goreleaser/internal/pipe/release"
|
"github.com/goreleaser/goreleaser/internal/pipe/release"
|
||||||
"github.com/goreleaser/goreleaser/internal/pipe/s3"
|
|
||||||
"github.com/goreleaser/goreleaser/internal/pipe/scoop"
|
"github.com/goreleaser/goreleaser/internal/pipe/scoop"
|
||||||
"github.com/goreleaser/goreleaser/internal/pipe/sign"
|
"github.com/goreleaser/goreleaser/internal/pipe/sign"
|
||||||
"github.com/goreleaser/goreleaser/internal/pipe/snapcraft"
|
"github.com/goreleaser/goreleaser/internal/pipe/snapcraft"
|
||||||
@ -48,7 +47,6 @@ var Defaulters = []Defaulter{
|
|||||||
sign.Pipe{},
|
sign.Pipe{},
|
||||||
docker.Pipe{},
|
docker.Pipe{},
|
||||||
artifactory.Pipe{},
|
artifactory.Pipe{},
|
||||||
s3.Pipe{},
|
|
||||||
blob.Pipe{},
|
blob.Pipe{},
|
||||||
brew.Pipe{},
|
brew.Pipe{},
|
||||||
scoop.Pipe{},
|
scoop.Pipe{},
|
||||||
|
@ -84,3 +84,13 @@ GCS provider uses [Application Default Credentials](https://cloud.google.com/doc
|
|||||||
- Environment Variable (GOOGLE_APPLICATION_CREDENTIALS)
|
- Environment Variable (GOOGLE_APPLICATION_CREDENTIALS)
|
||||||
- Default Service Account from the compute instance(Compute Engine, Kubernetes Engine, Cloud function etc).
|
- Default Service Account from the compute instance(Compute Engine, Kubernetes Engine, Cloud function etc).
|
||||||
|
|
||||||
|
### ACLs
|
||||||
|
|
||||||
|
There is no common way to set ACLs across all bucket providers, so, [go-cloud][]
|
||||||
|
[does not support it yet][issue1108].
|
||||||
|
|
||||||
|
You are expected to set the ACLs on the bucket/folder/etc, depending on your
|
||||||
|
provider.
|
||||||
|
|
||||||
|
[go-cloud]: https://gocloud.dev/howto/blob/
|
||||||
|
[issue1108]: https://github.com/google/go-cloud/issues/1108
|
||||||
|
@ -151,9 +151,13 @@ brews:
|
|||||||
# etc
|
# etc
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Expired deprecation notices
|
||||||
|
|
||||||
|
The following options were deprecated for ~6 months and are now fully removed.
|
||||||
|
|
||||||
### s3
|
### s3
|
||||||
|
|
||||||
> since 2019-06-09
|
> since 2019-06-09, removed 2020-01-07
|
||||||
|
|
||||||
S3 was deprecated in favor of the new `blob`, which supports S3, Azure Blob and
|
S3 was deprecated in favor of the new `blob`, which supports S3, Azure Blob and
|
||||||
GCS.
|
GCS.
|
||||||
@ -175,9 +179,7 @@ blobs:
|
|||||||
# etc
|
# etc
|
||||||
```
|
```
|
||||||
|
|
||||||
## Expired deprecation notices
|
ACLs should be set on the bucket, the `acl` option does not exist anymore.
|
||||||
|
|
||||||
The following options were deprecated for ~6 months and are now fully removed.
|
|
||||||
|
|
||||||
### archive
|
### archive
|
||||||
|
|
||||||
|
@ -1,87 +0,0 @@
|
|||||||
---
|
|
||||||
title: S3
|
|
||||||
series: customization
|
|
||||||
hideFromIndex: true
|
|
||||||
weight: 115
|
|
||||||
---
|
|
||||||
|
|
||||||
Since [v0.74.0](https://github.com/goreleaser/goreleaser/releases/tag/v0.74.0),
|
|
||||||
GoReleaser supports pushing artifacts to Amazon S3 and other API-compatible
|
|
||||||
object storages ([minio][] for example).
|
|
||||||
|
|
||||||
[minio]: https://www.minio.io
|
|
||||||
|
|
||||||
Right now, the implementation is quite simple and probably won't cover all
|
|
||||||
use cases. If you need one of such use cases, please open an issue/pull request.
|
|
||||||
|
|
||||||
Here is what you can customize:
|
|
||||||
|
|
||||||
## Customization
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
# .goreleaser.yml
|
|
||||||
s3:
|
|
||||||
# You can have multiple s3 configs
|
|
||||||
-
|
|
||||||
# Template for the bucket name(without the s3:// prefix)
|
|
||||||
# Default is empty.
|
|
||||||
bucket: my-bucket
|
|
||||||
|
|
||||||
# IDs of the artifacts you want to upload.
|
|
||||||
ids:
|
|
||||||
- foo
|
|
||||||
- bar
|
|
||||||
|
|
||||||
# AWS Region to use.
|
|
||||||
# Defaults is us-east-1
|
|
||||||
region: us-east-1
|
|
||||||
|
|
||||||
# Template for the path/name inside the bucket.
|
|
||||||
# Default is `{{ .ProjectName }}/{{ .Tag }}`
|
|
||||||
folder: "foo/bar/{{.Version}}"
|
|
||||||
|
|
||||||
# Set a custom profile to use for this s3 config. If you have multiple
|
|
||||||
# profiles setup in you ~/.aws config, this shall help defining which
|
|
||||||
# profile to use in which s3 bucket.
|
|
||||||
# Default is empty.
|
|
||||||
profile: my-profile
|
|
||||||
|
|
||||||
# Endpoint allows you to set a custom endpoint, which is useful if you
|
|
||||||
# want to push your artifacts to a minio server for example.
|
|
||||||
# Default is AWS S3 URL.
|
|
||||||
endpoint: "http://minio.foo.com"
|
|
||||||
|
|
||||||
# Sets the ACL of the object using the specified canned ACL.
|
|
||||||
# Default is private.
|
|
||||||
acl: public-read
|
|
||||||
```
|
|
||||||
|
|
||||||
> Learn more about the [name template engine](/templates).
|
|
||||||
> Learn more about the [acl](https://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectPUTacl.html).
|
|
||||||
|
|
||||||
## Authentication
|
|
||||||
|
|
||||||
GoReleaser will authenticate using the [same methods defined by aws-cli][auth].
|
|
||||||
You can read the [docs][auth] to find out more about it.
|
|
||||||
|
|
||||||
Currently it supports authentication with:
|
|
||||||
|
|
||||||
- An [EnvProvider][EnvProvider] which retrieves credentials from the environment
|
|
||||||
variables of the running process. Environment credentials never expire.
|
|
||||||
Environment variables used:
|
|
||||||
- Access Key ID: `AWS_ACCESS_KEY_ID` or `AWS_ACCESS_KEY`
|
|
||||||
- Secret Access Key: `AWS_SECRET_ACCESS_KEY` or `AWS_SECRET_KEY`
|
|
||||||
- A [SharedCredentialsProvider][SharedCredentialsProvider] which retrieves
|
|
||||||
credentials from the current user's home directory, and keeps track if those
|
|
||||||
credentials are expired. Profile ini file example: `$HOME/.aws/credentials`
|
|
||||||
- A AssumeRoleTokenProvider with enabled SharedConfigState which uses MFA
|
|
||||||
prompting for token code on stdin. Go to [session doc][session] for more
|
|
||||||
details.
|
|
||||||
|
|
||||||
You can also set different profile names for each S3 config, so you may be able
|
|
||||||
to push to buckets in different accounts, for example.
|
|
||||||
|
|
||||||
[auth]: https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-started.html
|
|
||||||
[envProvider]: https://docs.aws.amazon.com/sdk-for-go/api/aws/credentials/#EnvProvider
|
|
||||||
[sharedCredentialsProvider]: https://docs.aws.amazon.com/sdk-for-go/api/aws/credentials/#SharedCredentialsProvider
|
|
||||||
[session]: https://docs.aws.amazon.com/sdk-for-go/api/aws/session/
|
|
Loading…
x
Reference in New Issue
Block a user