1
0
mirror of https://github.com/goreleaser/goreleaser.git synced 2025-02-05 13:15:26 +02:00

fix: makes username and password optional for http uploads (#2057)

* fix: makes username and password optional for http uploads

* update upload doc
This commit is contained in:
Deepak S 2021-02-13 00:31:06 +05:30 committed by GitHub
parent f6f448d84e
commit 89c220c4c6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 53 additions and 34 deletions

View File

@ -96,12 +96,16 @@ func CheckConfig(ctx *context.Context, upload *config.Upload, kind string) error
return misconfigured(kind, upload, "mode must be 'binary' or 'archive'")
}
if _, err := getUsername(ctx, upload, kind); err != nil {
return err
username := getUsername(ctx, upload, kind)
password := getPassword(ctx, upload, kind)
passwordEnv := fmt.Sprintf("%s_%s_SECRET", strings.ToUpper(kind), strings.ToUpper(upload.Name))
if password != "" && username == "" {
return misconfigured(kind, upload, fmt.Sprintf("'username' is required when '%s' environment variable is set", passwordEnv))
}
if _, err := getPassword(ctx, upload, kind); err != nil {
return err
if username != "" && password == "" {
return misconfigured(kind, upload, fmt.Sprintf("environment variable '%s' is required when 'username' is set", passwordEnv))
}
if upload.TrustedCerts != "" && !x509.NewCertPool().AppendCertsFromPEM([]byte(upload.TrustedCerts)) {
@ -111,25 +115,20 @@ func CheckConfig(ctx *context.Context, upload *config.Upload, kind string) error
return nil
}
func getUsername(ctx *context.Context, upload *config.Upload, kind string) (string, error) {
// username is optional
func getUsername(ctx *context.Context, upload *config.Upload, kind string) string {
if upload.Username != "" {
return upload.Username, nil
return upload.Username
}
var key = fmt.Sprintf("%s_%s_USERNAME", strings.ToUpper(kind), strings.ToUpper(upload.Name))
user, ok := ctx.Env[key]
if !ok {
return "", misconfigured(kind, upload, fmt.Sprintf("missing username or %s environment variable", key))
}
return user, nil
return ctx.Env[key]
}
func getPassword(ctx *context.Context, upload *config.Upload, kind string) (string, error) {
// password is optional
func getPassword(ctx *context.Context, upload *config.Upload, kind string) string {
var key = fmt.Sprintf("%s_%s_SECRET", strings.ToUpper(kind), strings.ToUpper(upload.Name))
pwd, ok := ctx.Env[key]
if !ok {
return "", misconfigured(kind, upload, fmt.Sprintf("missing %s environment variable", key))
}
return pwd, nil
return ctx.Env[key]
}
func misconfigured(kind string, upload *config.Upload, reason string) error {
@ -204,15 +203,10 @@ func uploadWithFilter(ctx *context.Context, upload *config.Upload, filter artifa
// uploadAsset uploads file to target and logs all actions.
func uploadAsset(ctx *context.Context, upload *config.Upload, artifact *artifact.Artifact, kind string, check ResponseChecker) error {
username, err := getUsername(ctx, upload, kind)
if err != nil {
return err
}
secret, err := getPassword(ctx, upload, kind)
if err != nil {
return err
}
// username and secret are optional since the server may not support/need
// basic authentication always
username := getUsername(ctx, upload, kind)
secret := getPassword(ctx, upload, kind)
// Generate the target url
targetURL, err := resolveTargetTemplate(ctx, upload, artifact)
@ -268,7 +262,6 @@ func uploadAsset(ctx *context.Context, upload *config.Upload, artifact *artifact
msg := fmt.Sprintf("%s: upload failed", kind)
log.WithError(err).WithFields(log.Fields{
"instance": upload.Name,
"username": username,
}).Error(msg)
return fmt.Errorf("%s: %w", msg, err)
}
@ -301,7 +294,10 @@ func newUploadRequest(ctx *context.Context, method, target, username, secret str
return nil, err
}
req.ContentLength = a.Size
req.SetBasicAuth(username, secret)
if username != "" && secret != "" {
req.SetBasicAuth(username, secret)
}
for k, v := range headers {
req.Header.Add(k, v)

View File

@ -93,6 +93,8 @@ func TestCheckConfig(t *testing.T) {
{"secret missing", args{ctx, &config.Upload{Name: "b", Target: "http://blabla", Username: "pepe", Mode: ModeArchive}, "test"}, true},
{"target missing", args{ctx, &config.Upload{Name: "a", Username: "pepe", Mode: ModeArchive}, "test"}, true},
{"name missing", args{ctx, &config.Upload{Target: "http://blabla", Username: "pepe", Mode: ModeArchive}, "test"}, true},
{"username missing", args{ctx, &config.Upload{Name: "a", Target: "http://blabla", Mode: ModeArchive}, "test"}, true},
{"username present", args{ctx, &config.Upload{Name: "a", Target: "http://blabla", Username: "pepe", Mode: ModeArchive}, "test"}, false},
{"mode missing", args{ctx, &config.Upload{Name: "a", Target: "http://blabla", Username: "pepe"}, "test"}, true},
{"mode invalid", args{ctx, &config.Upload{Name: "a", Target: "http://blabla", Username: "pepe", Mode: "blabla"}, "test"}, true},
{"cert invalid", args{ctx, &config.Upload{Name: "a", Target: "http://blabla", Username: "pepe", Mode: ModeBinary, TrustedCerts: "bad cert!"}, "test"}, true},
@ -104,6 +106,24 @@ func TestCheckConfig(t *testing.T) {
}
})
}
delete(ctx.Env, "TEST_A_SECRET")
tests = []struct {
name string
args args
wantErr bool
}{
{"username missing", args{ctx, &config.Upload{Name: "a", Target: "http://blabla", Mode: ModeArchive}, "test"}, false},
{"username present", args{ctx, &config.Upload{Name: "a", Target: "http://blabla", Username: "pepe", Mode: ModeArchive}, "test"}, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if err := CheckConfig(tt.args.ctx, tt.args.upload, tt.args.kind); (err != nil) != tt.wantErr {
t.Errorf("CheckConfig() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
type check struct {

View File

@ -11,19 +11,18 @@ You can declare multiple `uploads` instances. All binaries generated by your
`builds` section will be pushed to each configured upload.
If you have only one `uploads` instance, the configuration is as easy as adding
the upload target and a username to your `.goreleaser.yml` file:
the upload target and a name to your `.goreleaser.yml` file:
```yaml
uploads:
- name: production
target: http://some.server/some/path/example-repo-local/{{ .ProjectName }}/{{ .Version }}/
username: goreleaser
```
Prerequisites:
- An HTTP server accepting HTTP requests
- A user + password with grants to upload an artifact using HTTP requests (if the server requires it)
- A user + password with grants to upload an artifact using HTTP requests for basic authentication (only if the server requires it)
### Target
@ -60,7 +59,7 @@ generated by `nfpm` and the like.
Your configured username needs to be valid against your HTTP server.
You can have the username set in the configuration file as shown above
or you can have it read from and environment variable.
or you can have it read from an environment variable.
The configured name of your HTTP server will be used to build the environment
variable name.
This way we support auth for multiple instances.
@ -75,9 +74,11 @@ The name will be transformed to uppercase.
If a configured username is found in the configuration file, then the
environment variable is not used at all.
This field is optional and is used only for basic http authentication.
### Password
The password will be stored in a environment variable.
The password will be stored in an environment variable.
The configured name of your HTTP server will be used.
This way we support auth for multiple instances.
This also means that the `name` per configured instance needs to be unique
@ -88,6 +89,8 @@ If your instance is named `production`, you need to store the secret in the
environment variable `UPLOAD_PRODUCTION_SECRET`.
The name will be transformed to uppercase.
This field is optional and is used only for basic http authentication.
### Server authentication
You can authenticate your TLS server adding a trusted X.509 certificate chain
@ -153,7 +156,7 @@ uploads:
# target: https://some.server/some/path/example-repo-local/{{ .ArtifactName }};deb.distribution=xenial
custom_artifact_name: true
# User that will be used for the deployment
# An optional username that will be used for the deployment for basic authn
username: deployuser
# An optional header you can use to tell GoReleaser to pass the artifact's