You've already forked goreleaser
							
							
				mirror of
				https://github.com/goreleaser/goreleaser.git
				synced 2025-10-30 23:58:09 +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:
		
				
					committed by
					
						 GitHub
						GitHub
					
				
			
			
				
	
			
			
			
						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 | ||||
| 	github.com/Masterminds/semver/v3 v3.0.3 | ||||
| 	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/campoy/unique v0.0.0-20180121183637-88950e537e7e | ||||
| 	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/docker" | ||||
| 	"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/snapcraft" | ||||
| 	"github.com/goreleaser/goreleaser/internal/pipe/upload" | ||||
| @@ -36,7 +35,6 @@ type Publisher interface { | ||||
|  | ||||
| // nolint: gochecknoglobals | ||||
| var publishers = []Publisher{ | ||||
| 	s3.Pipe{}, | ||||
| 	blob.Pipe{}, | ||||
| 	upload.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"` | ||||
| } | ||||
|  | ||||
| // 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 | ||||
| type Blob struct { | ||||
| 	Bucket     string   `yaml:",omitempty"` | ||||
| @@ -368,7 +357,6 @@ type Project struct { | ||||
| 	Artifactories []Upload    `yaml:",omitempty"` | ||||
| 	Uploads       []Upload    `yaml:",omitempty"` | ||||
| 	Puts          []Upload    `yaml:",omitempty"`     // TODO: remove this | ||||
| 	S3            []S3        `yaml:"s3,omitempty"` | ||||
| 	Blob          []Blob      `yaml:"blob,omitempty"` // TODO: remove this | ||||
| 	Blobs         []Blob      `yaml:"blobs,omitempty"` | ||||
| 	Changelog     Changelog   `yaml:",omitempty"` | ||||
|   | ||||
| @@ -16,7 +16,6 @@ import ( | ||||
| 	"github.com/goreleaser/goreleaser/internal/pipe/nfpm" | ||||
| 	"github.com/goreleaser/goreleaser/internal/pipe/project" | ||||
| 	"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/sign" | ||||
| 	"github.com/goreleaser/goreleaser/internal/pipe/snapcraft" | ||||
| @@ -48,7 +47,6 @@ var Defaulters = []Defaulter{ | ||||
| 	sign.Pipe{}, | ||||
| 	docker.Pipe{}, | ||||
| 	artifactory.Pipe{}, | ||||
| 	s3.Pipe{}, | ||||
| 	blob.Pipe{}, | ||||
| 	brew.Pipe{}, | ||||
| 	scoop.Pipe{}, | ||||
|   | ||||
| @@ -84,3 +84,13 @@ GCS provider uses [Application Default Credentials](https://cloud.google.com/doc | ||||
| - Environment Variable (GOOGLE_APPLICATION_CREDENTIALS) | ||||
| - 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 | ||||
| ``` | ||||
|  | ||||
| ## Expired deprecation notices | ||||
|  | ||||
| The following options were deprecated for ~6 months and are now fully removed. | ||||
|  | ||||
| ### 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 | ||||
| GCS. | ||||
| @@ -175,9 +179,7 @@ blobs: | ||||
|   # etc | ||||
| ``` | ||||
|  | ||||
| ## Expired deprecation notices | ||||
|  | ||||
| The following options were deprecated for ~6 months and are now fully removed. | ||||
| ACLs should be set on the bucket, the `acl` option does not exist anymore. | ||||
|  | ||||
| ### 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/ | ||||
		Reference in New Issue
	
	Block a user