package nexus import ( "errors" "fmt" "github.com/SAP/jenkins-library/pkg/log" "strings" ) // ArtifactDescription describes a single artifact that can be uploaded to a Nexus repository manager. // The File string must point to an existing file. The Classifier can be empty. type ArtifactDescription struct { Classifier string `json:"classifier"` Type string `json:"type"` File string `json:"file"` } // Upload combines information about an artifact and its sub-artifacts which are supposed to be uploaded together. // Call SetRepoURL(), SetArtifactsVersion(), SetArtifactID(), and add at least one artifact via AddArtifact(). type Upload struct { protocol string mavenRepoURL string npmRepoURL string groupID string version string artifactID string artifacts []ArtifactDescription } // Uploader provides an interface for configuring the target Nexus Repository and adding artifacts. type Uploader interface { SetRepoURL(nexusURL, nexusVersion, mavenRepository, npmRepository string) error GetNexusURLProtocol() string GetMavenRepoURL() string GetNpmRepoURL() string SetInfo(groupID, artifactsID, version string) error GetGroupID() string GetArtifactsID() string GetArtifactsVersion() string AddArtifact(artifact ArtifactDescription) error GetArtifacts() []ArtifactDescription Clear() } // SetRepoURL constructs the base URL to the Nexus repository. mavenRepository or npmRepository may be empty. func (nexusUpload *Upload) SetRepoURL(nexusURL, nexusVersion, mavenRepository, npmRepository string) error { protocol, err := _GetNexusURLProtocol(nexusURL) if err != nil { return err } nexusUpload.protocol = protocol mavenRepoURL, err := getBaseURL(nexusURL, nexusVersion, mavenRepository) if err != nil { return err } nexusUpload.mavenRepoURL = mavenRepoURL npmRepositoryURL, err := getBaseURL(nexusURL, nexusVersion, npmRepository) if err != nil { return err } nexusUpload.npmRepoURL = npmRepositoryURL return nil } func getBaseURL(nexusURL, nexusVersion, repository string) (string, error) { if nexusURL == "" { return "", errors.New("nexusURL must not be empty") } nexusURL = strings.ToLower(nexusURL) var protocols = []string{"http://", "https://"} for _, protocol := range protocols { if strings.HasPrefix(nexusURL, protocol) { nexusURL = strings.TrimPrefix(nexusURL, protocol) break } } baseURL := nexusURL if repository != "" { switch nexusVersion { case "nexus2": baseURL += "/content/repositories/" case "nexus3": baseURL += "/repository/" default: return "", fmt.Errorf("unsupported Nexus version '%s', must be 'nexus2' or 'nexus3'", nexusVersion) } baseURL += repository + "/" // Replace any double slashes, as nexus does not like them baseURL = strings.ReplaceAll(baseURL, "//", "/") } return baseURL, nil } // _GetNexusURLProtocol returns the protocol specified in the nexusUrl which was set thru setNexusUrl (internal method) func _GetNexusURLProtocol(nexusURL string) (string, error) { if nexusURL == "" { return "", errors.New("nexusURL must not be empty") } nexusURL = strings.ToLower(nexusURL) var protocols = []string{"http://", "https://"} for _, protocol := range protocols { if strings.HasPrefix(nexusURL, protocol) { return strings.ReplaceAll(protocol, "://", ""), nil } } return "http", nil } // GetMavenRepoURL returns the base URL for the nexus-maven repository. func (nexusUpload *Upload) GetMavenRepoURL() string { return nexusUpload.mavenRepoURL } // GetNexusURLProtocol returns the protocol specified in the nexusUrl which was set thru setNexusUrl func (nexusUpload *Upload) GetNexusURLProtocol() string { if nexusUpload.protocol == "" { return "http" } return nexusUpload.protocol } // GetNpmRepoURL returns the base URL for the nexus-npm repository. func (nexusUpload *Upload) GetNpmRepoURL() string { return nexusUpload.npmRepoURL } // ErrEmptyGroupID is returned from SetInfo, if groupID is empty. var ErrEmptyGroupID = errors.New("groupID must not be empty") // ErrEmptyArtifactID is returned from SetInfo, if artifactID is empty. var ErrEmptyArtifactID = errors.New("artifactID must not be empty") // ErrInvalidArtifactID is returned from SetInfo, if artifactID contains slashes. var ErrInvalidArtifactID = errors.New("artifactID may not include slashes") // ErrEmptyVersion is returned from SetInfo, if version is empty. var ErrEmptyVersion = errors.New("version must not be empty") // SetInfo sets the common info for all uploaded artifacts. This info is external to // the artifact descriptions so that it is consistent for all of them. func (nexusUpload *Upload) SetInfo(groupID, artifactID, version string) error { if groupID == "" { return ErrEmptyGroupID } if artifactID == "" { return ErrEmptyArtifactID } if strings.Contains(artifactID, "/") { return ErrInvalidArtifactID } if version == "" { return ErrEmptyVersion } nexusUpload.groupID = groupID nexusUpload.artifactID = artifactID nexusUpload.version = version return nil } // GetArtifactsVersion returns the common version for all artifacts. func (nexusUpload *Upload) GetArtifactsVersion() string { return nexusUpload.version } // GetGroupID returns the common groupId for all artifacts. func (nexusUpload *Upload) GetGroupID() string { return nexusUpload.groupID } // GetArtifactsID returns the common artifactId for all artifacts. func (nexusUpload *Upload) GetArtifactsID() string { return nexusUpload.artifactID } // AddArtifact adds a single artifact to be uploaded later via UploadArtifacts(). If an identical artifact // description is already contained in the Upload, the function does nothing and returns no error. func (nexusUpload *Upload) AddArtifact(artifact ArtifactDescription) error { err := validateArtifact(artifact) if err != nil { return err } if nexusUpload.containsArtifact(artifact) { log.Entry().Infof("Nexus Upload already contains artifact %v\n", artifact) return nil } nexusUpload.artifacts = append(nexusUpload.artifacts, artifact) return nil } func validateArtifact(artifact ArtifactDescription) error { if artifact.File == "" || artifact.Type == "" { return fmt.Errorf("Artifact.File (%v) or Type (%v) is empty", artifact.File, artifact.Type) } return nil } func (nexusUpload *Upload) containsArtifact(artifact ArtifactDescription) bool { for _, n := range nexusUpload.artifacts { if artifact == n { return true } } return false } // GetArtifacts returns a copy of the artifact descriptions array stored in the Upload. func (nexusUpload *Upload) GetArtifacts() []ArtifactDescription { artifacts := make([]ArtifactDescription, len(nexusUpload.artifacts)) copy(artifacts, nexusUpload.artifacts) return artifacts } // Clear removes any contained artifact descriptions. func (nexusUpload *Upload) Clear() { nexusUpload.artifacts = []ArtifactDescription{} }