github.com/goreleaser/goreleaser@v1.25.1/internal/pipe/artifactory/artifactory.go (about)

     1  // Package artifactory provides a Pipe that push to artifactory
     2  package artifactory
     3  
     4  import (
     5  	"encoding/json"
     6  	"fmt"
     7  	"io"
     8  	h "net/http"
     9  
    10  	"github.com/goreleaser/goreleaser/internal/http"
    11  	"github.com/goreleaser/goreleaser/internal/pipe"
    12  	"github.com/goreleaser/goreleaser/pkg/context"
    13  )
    14  
    15  // Pipe for Artifactory.
    16  type Pipe struct{}
    17  
    18  func (Pipe) String() string                 { return "artifactory" }
    19  func (Pipe) Skip(ctx *context.Context) bool { return len(ctx.Config.Artifactories) == 0 }
    20  
    21  // Default sets the pipe defaults.
    22  func (Pipe) Default(ctx *context.Context) error {
    23  	for i := range ctx.Config.Artifactories {
    24  		if ctx.Config.Artifactories[i].ChecksumHeader == "" {
    25  			ctx.Config.Artifactories[i].ChecksumHeader = "X-Checksum-SHA256"
    26  		}
    27  		ctx.Config.Artifactories[i].Method = h.MethodPut
    28  	}
    29  	return http.Defaults(ctx.Config.Artifactories)
    30  }
    31  
    32  // Publish artifacts to artifactory.
    33  //
    34  // Docs: https://www.jfrog.com/confluence/display/RTF/Artifactory+REST+API#ArtifactoryRESTAPI-Example-DeployinganArtifact
    35  func (Pipe) Publish(ctx *context.Context) error {
    36  	// Check requirements for every instance we have configured.
    37  	// If not fulfilled, we can skip this pipeline
    38  	for _, instance := range ctx.Config.Artifactories {
    39  		instance := instance
    40  		if skip := http.CheckConfig(ctx, &instance, "artifactory"); skip != nil {
    41  			return pipe.Skip(skip.Error())
    42  		}
    43  	}
    44  
    45  	return http.Upload(ctx, ctx.Config.Artifactories, "artifactory", checkResponse)
    46  }
    47  
    48  // An ErrorResponse reports one or more errors caused by an API request.
    49  type errorResponse struct {
    50  	Response *h.Response // HTTP response that caused this error
    51  	Errors   []Error     `json:"errors"` // more detail on individual errors
    52  }
    53  
    54  func (r *errorResponse) Error() string {
    55  	return fmt.Sprintf("%v %v: %d %+v",
    56  		r.Response.Request.Method, r.Response.Request.URL,
    57  		r.Response.StatusCode, r.Errors)
    58  }
    59  
    60  // An Error reports more details on an individual error in an ErrorResponse.
    61  type Error struct {
    62  	Status  int    `json:"status"`  // Error code
    63  	Message string `json:"message"` // Message describing the error.
    64  }
    65  
    66  // checkResponse checks the API response for errors, and returns them if
    67  // present. A response is considered an error if it has a status code outside
    68  // the 200 range.
    69  // API error responses are expected to have either no response
    70  // body, or a JSON response body that maps to ErrorResponse. Any other
    71  // response body will be silently ignored.
    72  func checkResponse(r *h.Response) error {
    73  	defer r.Body.Close()
    74  	if c := r.StatusCode; 200 <= c && c <= 299 {
    75  		return nil
    76  	}
    77  	errorResponse := &errorResponse{Response: r}
    78  	data, err := io.ReadAll(r.Body)
    79  	if err == nil && data != nil {
    80  		err := json.Unmarshal(data, errorResponse)
    81  		if err != nil {
    82  			return fmt.Errorf("unexpected error: %w: %s", err, string(data))
    83  		}
    84  	}
    85  	return errorResponse
    86  }