// Copyright 2023 Harness, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package metadata import ( "context" "net/http" "net/url" "strings" apiauth "github.com/harness/gitness/app/api/auth" "github.com/harness/gitness/app/api/request" "github.com/harness/gitness/app/paths" "github.com/harness/gitness/registry/app/api/openapi/contracts/artifact" "github.com/harness/gitness/registry/app/common" "github.com/harness/gitness/types/enum" ) func (c *APIController) GetClientSetupDetails( ctx context.Context, r artifact.GetClientSetupDetailsRequestObject, ) (artifact.GetClientSetupDetailsResponseObject, error) { regRefParam := r.RegistryRef imageParam := r.Params.Artifact tagParam := r.Params.Version regInfo, _ := c.RegistryMetadataHelper.GetRegistryRequestBaseInfo(ctx, "", string(regRefParam)) space, err := c.SpaceFinder.FindByRef(ctx, regInfo.ParentRef) if err != nil { return artifact.GetClientSetupDetails400JSONResponse{ BadRequestJSONResponse: artifact.BadRequestJSONResponse( *GetErrorResponse(http.StatusBadRequest, err.Error()), ), }, nil } session, _ := request.AuthSessionFrom(ctx) permissionChecks := c.RegistryMetadataHelper.GetPermissionChecks(space, regInfo.RegistryIdentifier, enum.PermissionRegistryView) if err = apiauth.CheckRegistry( ctx, c.Authorizer, session, permissionChecks..., ); err != nil { return artifact.GetClientSetupDetails403JSONResponse{ UnauthorizedJSONResponse: artifact.UnauthorizedJSONResponse( *GetErrorResponse(http.StatusForbidden, err.Error()), ), }, nil } reg, err := c.RegistryRepository.GetByParentIDAndName(ctx, regInfo.parentID, regInfo.RegistryIdentifier) if err != nil { return artifact.GetClientSetupDetails404JSONResponse{ NotFoundJSONResponse: artifact.NotFoundJSONResponse( *GetErrorResponse(http.StatusNotFound, "registry doesn't exist with this ref"), ), }, err } packageType := string(reg.PackageType) return artifact.GetClientSetupDetails200JSONResponse{ ClientSetupDetailsResponseJSONResponse: *c.GenerateClientSetupDetails( ctx, packageType, imageParam, tagParam, regInfo.RegistryRef, ), }, nil } func (c *APIController) GenerateClientSetupDetails( ctx context.Context, packageType string, image *artifact.ArtifactParam, tag *artifact.VersionParam, registryRef string, ) *artifact.ClientSetupDetailsResponseJSONResponse { session, _ := request.AuthSessionFrom(ctx) username := session.Principal.Email loginUsernameLabel := "Username: " loginUsernameValue := "" loginPasswordLabel := "Password: *see step 2*" blankString := "" switch packageType { case string(artifact.PackageTypeMAVEN): return c.generateMavenClientSetupDetail(ctx, image, tag, registryRef, username) case string(artifact.PackageTypeHELM): return c.generateHelmClientSetupDetail(ctx, blankString, loginUsernameLabel, loginUsernameValue, loginPasswordLabel, username, registryRef, image, tag) case string(artifact.PackageTypeGENERIC): return c.generateGenericClientSetupDetail(ctx, blankString, registryRef, image, tag) case string(artifact.PackageTypePYTHON): return c.generatePythonClientSetupDetail(ctx, registryRef, username, image, tag) } header1 := "Login to Docker" section1step1Header := "Run this Docker command in your terminal to authenticate the client." dockerLoginValue := "docker login " section1step1Commands := []artifact.ClientSetupStepCommand{ {Label: &blankString, Value: &dockerLoginValue}, {Label: &loginUsernameLabel, Value: &loginUsernameValue}, {Label: &loginPasswordLabel, Value: &blankString}, } section1step1Type := artifact.ClientSetupStepTypeStatic section1step2Header := "For the Password field above, generate an identity token" section1step2Type := artifact.ClientSetupStepTypeGenerateToken section1Steps := []artifact.ClientSetupStep{ { Header: §ion1step1Header, Commands: §ion1step1Commands, Type: §ion1step1Type, }, { Header: §ion1step2Header, Type: §ion1step2Type, }, } section1 := artifact.ClientSetupSection{ Header: &header1, } _ = section1.FromClientSetupStepConfig(artifact.ClientSetupStepConfig{ Steps: §ion1Steps, }) header2 := "Pull an image" section2step1Header := "Run this Docker command in your terminal to pull image." dockerPullValue := "docker pull //:" section2step1Commands := []artifact.ClientSetupStepCommand{ {Label: &blankString, Value: &dockerPullValue}, } section2step1Type := artifact.ClientSetupStepTypeStatic section2Steps := []artifact.ClientSetupStep{ { Header: §ion2step1Header, Commands: §ion2step1Commands, Type: §ion2step1Type, }, } section2 := artifact.ClientSetupSection{ Header: &header2, } _ = section2.FromClientSetupStepConfig(artifact.ClientSetupStepConfig{ Steps: §ion2Steps, }) header3 := "Retag and Push the image" section3step1Header := "Run this Docker command in your terminal to tag the image." dockerTagValue := "docker tag : //:" section3step1Commands := []artifact.ClientSetupStepCommand{ {Label: &blankString, Value: &dockerTagValue}, } section3step1Type := artifact.ClientSetupStepTypeStatic section3step2Header := "Run this Docker command in your terminal to push the image." dockerPushValue := "docker push //:" section3step2Commands := []artifact.ClientSetupStepCommand{ {Label: &blankString, Value: &dockerPushValue}, } section3step2Type := artifact.ClientSetupStepTypeStatic section3Steps := []artifact.ClientSetupStep{ { Header: §ion3step1Header, Commands: §ion3step1Commands, Type: §ion3step1Type, }, { Header: §ion3step2Header, Commands: §ion3step2Commands, Type: §ion3step2Type, }, } section3 := artifact.ClientSetupSection{ Header: &header3, } _ = section3.FromClientSetupStepConfig(artifact.ClientSetupStepConfig{ Steps: §ion3Steps, }) clientSetupDetails := artifact.ClientSetupDetails{ MainHeader: "Docker Client Setup", SecHeader: "Follow these instructions to install/use Docker artifacts or compatible packages.", Sections: []artifact.ClientSetupSection{ section1, section2, section3, }, } c.replacePlaceholders(ctx, &clientSetupDetails.Sections, username, registryRef, image, tag, "", "", "") return &artifact.ClientSetupDetailsResponseJSONResponse{ Data: clientSetupDetails, Status: artifact.StatusSUCCESS, } } //nolint:lll func (c *APIController) generateGenericClientSetupDetail( ctx context.Context, blankString string, registryRef string, image *artifact.ArtifactParam, tag *artifact.VersionParam, ) *artifact.ClientSetupDetailsResponseJSONResponse { header1 := "Generate identity token" section1Header := "An identity token will serve as the password for uploading and downloading artifact." section1Type := artifact.ClientSetupStepTypeGenerateToken section1steps := []artifact.ClientSetupStep{ { Header: §ion1Header, Type: §ion1Type, }, } section1 := artifact.ClientSetupSection{ Header: &header1, } _ = section1.FromClientSetupStepConfig(artifact.ClientSetupStepConfig{ Steps: §ion1steps, }) header2 := "Upload Artifact" section2step1Header := "Run this curl command in your terminal to push the artifact." //nolint:lll pushValue := "curl --location --request PUT '///' \\\n--form 'filename=\"\"' \\\n--form 'file=@\"\"' \\\n--form 'description=\"\"' \\\n--header 'x-api-key: '" section2step1Commands := []artifact.ClientSetupStepCommand{ {Label: &blankString, Value: &pushValue}, } section2step1Type := artifact.ClientSetupStepTypeStatic section2steps := []artifact.ClientSetupStep{ { Header: §ion2step1Header, Commands: §ion2step1Commands, Type: §ion2step1Type, }, } section2 := artifact.ClientSetupSection{ Header: &header2, } _ = section2.FromClientSetupStepConfig(artifact.ClientSetupStepConfig{ Steps: §ion2steps, }) header3 := "Download Artifact" section3step1Header := "Run this command in your terminal to download the artifact." //nolint:lll pullValue := "curl --location '//::' --header 'x-api-key: ' " + "-J -O" section3step1Commands := []artifact.ClientSetupStepCommand{ {Label: &blankString, Value: &pullValue}, } section3step1Type := artifact.ClientSetupStepTypeStatic section3steps := []artifact.ClientSetupStep{ { Header: §ion3step1Header, Commands: §ion3step1Commands, Type: §ion3step1Type, }, } section3 := artifact.ClientSetupSection{ Header: &header3, } _ = section3.FromClientSetupStepConfig(artifact.ClientSetupStepConfig{ Steps: §ion3steps, }) clientSetupDetails := artifact.ClientSetupDetails{ MainHeader: "Generic Client Setup", SecHeader: "Follow these instructions to install/use Generic artifacts or compatible packages.", Sections: []artifact.ClientSetupSection{ section1, section2, section3, }, } //nolint:lll c.replacePlaceholders(ctx, &clientSetupDetails.Sections, "", registryRef, image, tag, "", "", string(artifact.PackageTypeGENERIC)) return &artifact.ClientSetupDetailsResponseJSONResponse{ Data: clientSetupDetails, Status: artifact.StatusSUCCESS, } } //nolint:lll func (c *APIController) generateHelmClientSetupDetail( ctx context.Context, blankString string, loginUsernameLabel string, loginUsernameValue string, loginPasswordLabel string, username string, registryRef string, image *artifact.ArtifactParam, tag *artifact.VersionParam, ) *artifact.ClientSetupDetailsResponseJSONResponse { header1 := "Login to Helm" section1step1Header := "Run this Helm command in your terminal to authenticate the client." helmLoginValue := "helm registry login " section1step1Commands := []artifact.ClientSetupStepCommand{ {Label: &blankString, Value: &helmLoginValue}, {Label: &loginUsernameLabel, Value: &loginUsernameValue}, {Label: &loginPasswordLabel, Value: &blankString}, } section1step1Type := artifact.ClientSetupStepTypeStatic section1step2Header := "For the Password field above, generate an identity token" section1step2Type := artifact.ClientSetupStepTypeGenerateToken section1Steps := []artifact.ClientSetupStep{ { Header: §ion1step1Header, Commands: §ion1step1Commands, Type: §ion1step1Type, }, { Header: §ion1step2Header, Type: §ion1step2Type, }, } section1 := artifact.ClientSetupSection{ Header: &header1, } _ = section1.FromClientSetupStepConfig(artifact.ClientSetupStepConfig{ Steps: §ion1Steps, }) header2 := "Push a version" section2step1Header := "Run this Helm push command in your terminal to push a chart in OCI form." + " Note: Make sure you add oci:// prefix to the repository URL." helmPushValue := "helm push oci:///" section2step1Commands := []artifact.ClientSetupStepCommand{ {Label: &blankString, Value: &helmPushValue}, } section2step1Type := artifact.ClientSetupStepTypeStatic section2Steps := []artifact.ClientSetupStep{ { Header: §ion2step1Header, Commands: §ion2step1Commands, Type: §ion2step1Type, }, } section2 := artifact.ClientSetupSection{ Header: &header2, } _ = section2.FromClientSetupStepConfig(artifact.ClientSetupStepConfig{ Steps: §ion2Steps, }) header3 := "Pull a version" section3step1Header := "Run this Helm command in your terminal to pull a specific chart version." helmPullValue := "helm pull oci://// --version " section3step1Commands := []artifact.ClientSetupStepCommand{ {Label: &blankString, Value: &helmPullValue}, } section3step1Type := artifact.ClientSetupStepTypeStatic section3Steps := []artifact.ClientSetupStep{ { Header: §ion3step1Header, Commands: §ion3step1Commands, Type: §ion3step1Type, }, } section3 := artifact.ClientSetupSection{ Header: &header3, } _ = section3.FromClientSetupStepConfig(artifact.ClientSetupStepConfig{ Steps: §ion3Steps, }) clientSetupDetails := artifact.ClientSetupDetails{ MainHeader: "Helm Client Setup", SecHeader: "Follow these instructions to install/use Helm artifacts or compatible packages.", Sections: []artifact.ClientSetupSection{ section1, section2, section3, }, } c.replacePlaceholders(ctx, &clientSetupDetails.Sections, username, registryRef, image, tag, "", "", "") return &artifact.ClientSetupDetailsResponseJSONResponse{ Data: clientSetupDetails, Status: artifact.StatusSUCCESS, } } func (c *APIController) generateMavenClientSetupDetail( ctx context.Context, artifactName *artifact.ArtifactParam, version *artifact.VersionParam, registryRef string, username string, ) *artifact.ClientSetupDetailsResponseJSONResponse { staticStepType := artifact.ClientSetupStepTypeStatic generateTokenStepType := artifact.ClientSetupStepTypeGenerateToken section1 := artifact.ClientSetupSection{ Header: stringPtr("1. Generate Identity Token"), SecHeader: stringPtr("An identity token will serve as the password for uploading and downloading artifacts."), } _ = section1.FromClientSetupStepConfig(artifact.ClientSetupStepConfig{ Steps: &[]artifact.ClientSetupStep{ { Header: stringPtr("Generate an identity token"), Type: &generateTokenStepType, }, }, }) mavenSection1 := artifact.ClientSetupSection{ Header: stringPtr("2. Pull a Maven Package"), SecHeader: stringPtr("Set default repository in your pom.xml file."), } _ = mavenSection1.FromClientSetupStepConfig(artifact.ClientSetupStepConfig{ Steps: &[]artifact.ClientSetupStep{ { Header: stringPtr("To set default registry in your pom.xml file by adding the following:"), Type: &staticStepType, Commands: &[]artifact.ClientSetupStepCommand{ { //nolint:lll Value: stringPtr("\n \n maven-dev\n /\n \n true\n always\n \n \n true\n always\n \n \n"), }, }, }, { //nolint:lll Header: stringPtr("Copy the following your ~/ .m2/settings.xml file for MacOs, or $USERPROFILE$\\ .m2\\settings.xml for Windows to authenticate with token to pull from your Maven registry."), Type: &staticStepType, Commands: &[]artifact.ClientSetupStepCommand{ { //nolint:lll Value: stringPtr("\n \n \n maven-dev\n \n identity-token\n \n \n"), }, }, }, { //nolint:lll Header: stringPtr("Add a dependency to the project's pom.xml (replace , & with your own):"), Type: &staticStepType, Commands: &[]artifact.ClientSetupStepCommand{ { //nolint:lll Value: stringPtr("\n \n \n \n"), }, }, }, { Header: stringPtr("Install dependencies in pom.xml file"), Type: &staticStepType, Commands: &[]artifact.ClientSetupStepCommand{ { Value: stringPtr("mvn install"), }, }, }, }, }) mavenSection2 := artifact.ClientSetupSection{ Header: stringPtr("3. Push a Maven Package"), SecHeader: stringPtr("Set default repository in your pom.xml file."), } _ = mavenSection2.FromClientSetupStepConfig(artifact.ClientSetupStepConfig{ Steps: &[]artifact.ClientSetupStep{ { Header: stringPtr("To set default registry in your pom.xml file by adding the following:"), Type: &staticStepType, Commands: &[]artifact.ClientSetupStepCommand{ { //nolint:lll Value: stringPtr("\n \n maven-dev\n /\n \n \n maven-dev\n /\n \n"), }, }, }, { //nolint:lll Header: stringPtr("Copy the following your ~/ .m2/setting.xml file for MacOs, or $USERPROFILE$\\ .m2\\settings.xml for Windows to authenticate with token to push to your Maven registry."), Type: &staticStepType, Commands: &[]artifact.ClientSetupStepCommand{ { //nolint:lll Value: stringPtr("\n \n \n maven-dev\n \n identity-token\n \n \n"), }, }, }, { Header: stringPtr("Publish package to your Maven registry."), Type: &staticStepType, Commands: &[]artifact.ClientSetupStepCommand{ { Value: stringPtr("mvn deploy"), }, }, }, }, }) gradleSection1 := artifact.ClientSetupSection{ Header: stringPtr("2. Pull a Gradle Package"), SecHeader: stringPtr("Set default repository in your build.gradle file."), } _ = gradleSection1.FromClientSetupStepConfig(artifact.ClientSetupStepConfig{ Steps: &[]artifact.ClientSetupStep{ { Header: stringPtr("Set the default registry in your project’s build.gradle by adding the following:"), Type: &staticStepType, Commands: &[]artifact.ClientSetupStepCommand{ { //nolint:lll Value: stringPtr("repositories{\n maven{\n url \"/\"\n\n credentials {\n username \"\"\n password \"identity-token\"\n }\n }\n}"), }, }, }, { //nolint:lll Header: stringPtr("As this is a private registry, you’ll need to authenticate. Create or add to the ~/.gradle/gradle.properties file with the following:"), Type: &staticStepType, Commands: &[]artifact.ClientSetupStepCommand{ { Value: stringPtr("repositoryUser=\nrepositoryPassword={{identity-token}}"), }, }, }, { Header: stringPtr("Add a dependency to the project’s build.gradle"), Type: &staticStepType, Commands: &[]artifact.ClientSetupStepCommand{ { Value: stringPtr("dependencies {\n implementation '::'\n}"), }, }, }, { Header: stringPtr("Install dependencies in build.gradle file"), Type: &staticStepType, Commands: &[]artifact.ClientSetupStepCommand{ { Value: stringPtr("gradlew build // Linux or OSX\n gradlew.bat build // Windows"), }, }, }, }, }) gradleSection2 := artifact.ClientSetupSection{ Header: stringPtr("3. Push a Gradle Package"), SecHeader: stringPtr("Set default repository in your build.gradle file."), } _ = gradleSection2.FromClientSetupStepConfig(artifact.ClientSetupStepConfig{ Steps: &[]artifact.ClientSetupStep{ { Header: stringPtr("Add a maven publish plugin configuration to the project's build.gradle."), Type: &staticStepType, Commands: &[]artifact.ClientSetupStepCommand{ { //nolint:lll Value: stringPtr("publishing {\n publications {\n maven(MavenPublication) {\n groupId = 'GROUP_ID'\n artifactId = 'ARTIFACT_ID'\n version = 'VERSION'\n\n from components.java\n }\n }\n}"), }, }, }, { Header: stringPtr("Publish package to your Maven registry."), Type: &staticStepType, Commands: &[]artifact.ClientSetupStepCommand{ { Value: stringPtr("gradlew publish"), }, }, }, }, }) sbtSection1 := artifact.ClientSetupSection{ Header: stringPtr("2. Pull a Sbt/Scala Package"), SecHeader: stringPtr("Set default repository in your build.sbt file."), } _ = sbtSection1.FromClientSetupStepConfig(artifact.ClientSetupStepConfig{ Steps: &[]artifact.ClientSetupStep{ { Header: stringPtr("Set the default registry in your project’s build.sbt by adding the following:"), Type: &staticStepType, Commands: &[]artifact.ClientSetupStepCommand{ { //nolint:lll Value: stringPtr("resolver += \"Harness Registry\" at \"/\"\ncredentials += Credentials(Path.userHome / \".sbt\" / \".Credentials\")"), }, }, }, { //nolint:lll Header: stringPtr("As this is a private registry, you’ll need to authenticate. Create or add to the ~/.sbt/.credentials file with the following:"), Type: &staticStepType, Commands: &[]artifact.ClientSetupStepCommand{ { //nolint:lll Value: stringPtr("realm=Harness Registry\nhost=\nuser=\npassword={{identity-token}}"), }, }, }, { Header: stringPtr("Add a dependency to the project’s build.sbt"), Type: &staticStepType, Commands: &[]artifact.ClientSetupStepCommand{ { Value: stringPtr("libraryDependencies += \"\" % \"\" % \"\""), }, }, }, { Header: stringPtr("Install dependencies in build.sbt file"), Type: &staticStepType, Commands: &[]artifact.ClientSetupStepCommand{ { Value: stringPtr("sbt update"), }, }, }, }, }) sbtSection2 := artifact.ClientSetupSection{ Header: stringPtr("3. Push a Sbt/Scala Package"), SecHeader: stringPtr("Set default repository in your build.sbt file."), } _ = sbtSection2.FromClientSetupStepConfig(artifact.ClientSetupStepConfig{ Steps: &[]artifact.ClientSetupStep{ { Header: stringPtr("Add publish configuration to the project’s build.sbt."), Type: &staticStepType, Commands: &[]artifact.ClientSetupStepCommand{ { Value: stringPtr("publishTo := Some(\"Harness Registry\" at \"/\")"), }, }, }, { Header: stringPtr("Publish package to your Maven registry."), Type: &staticStepType, Commands: &[]artifact.ClientSetupStepCommand{ { Value: stringPtr("sbt publish"), }, }, }, }, }) section2 := artifact.ClientSetupSection{} _ = section2.FromTabSetupStepConfig(artifact.TabSetupStepConfig{ Tabs: &[]artifact.TabSetupStep{ { Header: stringPtr("Maven"), Sections: &[]artifact.ClientSetupSection{ mavenSection1, mavenSection2, }, }, { Header: stringPtr("Gradle"), Sections: &[]artifact.ClientSetupSection{ gradleSection1, gradleSection2, }, }, { Header: stringPtr("Sbt/Scala"), Sections: &[]artifact.ClientSetupSection{ sbtSection1, sbtSection2, }, }, }, }) clientSetupDetails := artifact.ClientSetupDetails{ MainHeader: "Maven Client Setup", SecHeader: "Follow these instructions to install/use Maven artifacts or compatible packages.", Sections: []artifact.ClientSetupSection{ section1, section2, }, } groupID := "" if artifactName != nil { parts := strings.Split(string(*artifactName), ":") if len(parts) == 2 { groupID = parts[0] *artifactName = artifact.ArtifactParam(parts[1]) } } rootSpace, _, _ := paths.DisectRoot(registryRef) registryURL := c.URLProvider.RegistryURL(ctx, "maven", rootSpace) //nolint:lll c.replacePlaceholders(ctx, &clientSetupDetails.Sections, username, registryRef, artifactName, version, registryURL, groupID, "") return &artifact.ClientSetupDetailsResponseJSONResponse{ Data: clientSetupDetails, Status: artifact.StatusSUCCESS, } } func (c *APIController) generatePythonClientSetupDetail( ctx context.Context, registryRef string, username string, image *artifact.ArtifactParam, tag *artifact.VersionParam, ) *artifact.ClientSetupDetailsResponseJSONResponse { staticStepType := artifact.ClientSetupStepTypeStatic generateTokenType := artifact.ClientSetupStepTypeGenerateToken // Authentication section section1 := artifact.ClientSetupSection{ Header: stringPtr("Configure Authentication"), } _ = section1.FromClientSetupStepConfig(artifact.ClientSetupStepConfig{ Steps: &[]artifact.ClientSetupStep{ { Header: stringPtr("Create or update your ~/.pypirc file with the following content:"), Type: &staticStepType, Commands: &[]artifact.ClientSetupStepCommand{ { Value: stringPtr("[distutils]\n" + "index-servers = harness\n\n" + "[harness]\n" + "repository = \n" + "username = \n" + "password = *see step 2*"), }, }, }, { Header: stringPtr("Generate an identity token for authentication"), Type: &generateTokenType, }, }, }) // Publish section section2 := artifact.ClientSetupSection{ Header: stringPtr("Publish Package"), } _ = section2.FromClientSetupStepConfig(artifact.ClientSetupStepConfig{ Steps: &[]artifact.ClientSetupStep{ { Header: stringPtr("Build and publish your package:"), Type: &staticStepType, Commands: &[]artifact.ClientSetupStepCommand{ { Value: stringPtr("python -m build"), }, { Value: stringPtr("python -m twine upload --repository harness /path/to/files/*"), }, }, }, }, }) // Install section section3 := artifact.ClientSetupSection{ Header: stringPtr("Install Package"), } _ = section3.FromClientSetupStepConfig(artifact.ClientSetupStepConfig{ Steps: &[]artifact.ClientSetupStep{ { Header: stringPtr("Install a package using pip:"), Type: &staticStepType, Commands: &[]artifact.ClientSetupStepCommand{ { Value: stringPtr("pip install --index-url /simple --no-deps =="), }, }, }, }, }) clientSetupDetails := artifact.ClientSetupDetails{ MainHeader: "Python Client Setup", SecHeader: "Follow these instructions to install/use Python packages from this registry.", Sections: []artifact.ClientSetupSection{ section1, section2, section3, }, } registryURL := c.URLProvider.PackageURL(ctx, registryRef, "python") c.replacePlaceholders(ctx, &clientSetupDetails.Sections, username, registryRef, image, tag, registryURL, "", string(artifact.PackageTypePYTHON)) return &artifact.ClientSetupDetailsResponseJSONResponse{ Data: clientSetupDetails, Status: artifact.StatusSUCCESS, } } func (c *APIController) replacePlaceholders( ctx context.Context, clientSetupSections *[]artifact.ClientSetupSection, username string, regRef string, image *artifact.ArtifactParam, tag *artifact.VersionParam, registryURL string, groupID string, pkgType string, ) { uploadURL := "" if pkgType == string(artifact.PackageTypePYTHON) { regURL, _ := url.Parse(registryURL) // append username:password to the host regURL.User = url.UserPassword(username, "identity-token") uploadURL = regURL.String() } for i := range *clientSetupSections { tab, err := (*clientSetupSections)[i].AsTabSetupStepConfig() if err != nil || tab.Tabs == nil { //nolint:lll c.replacePlaceholdersInSection(ctx, &(*clientSetupSections)[i], username, regRef, image, tag, pkgType, registryURL, groupID, uploadURL) } else { for j := range *tab.Tabs { c.replacePlaceholders(ctx, (*tab.Tabs)[j].Sections, username, regRef, image, tag, registryURL, groupID, pkgType) } _ = (*clientSetupSections)[i].FromTabSetupStepConfig(tab) } } } func (c *APIController) replacePlaceholdersInSection( ctx context.Context, clientSetupSection *artifact.ClientSetupSection, username string, regRef string, image *artifact.ArtifactParam, tag *artifact.VersionParam, pkgType string, registryURL string, groupID string, uploadURL string, ) { rootSpace, _, _ := paths.DisectRoot(regRef) _, registryName, _ := paths.DisectLeaf(regRef) var hostname string if pkgType == string(artifact.PackageTypeGENERIC) { hostname = c.URLProvider.RegistryURL(ctx, rootSpace, "generic") } else { hostname = common.TrimURLScheme(c.URLProvider.RegistryURL(ctx, rootSpace)) } sec, err := clientSetupSection.AsClientSetupStepConfig() if err != nil || sec.Steps == nil { return } for _, st := range *sec.Steps { if st.Commands == nil { continue } for j := range *st.Commands { replaceText(username, st, j, hostname, registryName, image, tag, registryURL, groupID, uploadURL) } } _ = clientSetupSection.FromClientSetupStepConfig(sec) } func replaceText( username string, st artifact.ClientSetupStep, i int, hostname string, repoName string, image *artifact.ArtifactParam, tag *artifact.VersionParam, registryURL string, groupID string, uploadURL string, ) { if username != "" { (*st.Commands)[i].Value = stringPtr(strings.ReplaceAll(*(*st.Commands)[i].Value, "", username)) if (*st.Commands)[i].Label != nil { (*st.Commands)[i].Label = stringPtr(strings.ReplaceAll(*(*st.Commands)[i].Label, "", username)) } } if groupID != "" { (*st.Commands)[i].Value = stringPtr(strings.ReplaceAll(*(*st.Commands)[i].Value, "", groupID)) } if registryURL != "" { (*st.Commands)[i].Value = stringPtr(strings.ReplaceAll(*(*st.Commands)[i].Value, "", registryURL)) } if uploadURL != "" { (*st.Commands)[i].Value = stringPtr(strings.ReplaceAll(*(*st.Commands)[i].Value, "", uploadURL)) } if hostname != "" { (*st.Commands)[i].Value = stringPtr(strings.ReplaceAll(*(*st.Commands)[i].Value, "", hostname)) } if hostname != "" { (*st.Commands)[i].Value = stringPtr(strings.ReplaceAll(*(*st.Commands)[i].Value, "", common.GetHost(hostname))) } if repoName != "" { (*st.Commands)[i].Value = stringPtr(strings.ReplaceAll(*(*st.Commands)[i].Value, "", repoName)) } if image != nil { (*st.Commands)[i].Value = stringPtr(strings.ReplaceAll(*(*st.Commands)[i].Value, "", string(*image))) (*st.Commands)[i].Value = stringPtr(strings.ReplaceAll(*(*st.Commands)[i].Value, "", string(*image))) (*st.Commands)[i].Value = stringPtr(strings.ReplaceAll(*(*st.Commands)[i].Value, "", string(*image))) } if tag != nil { (*st.Commands)[i].Value = stringPtr(strings.ReplaceAll(*(*st.Commands)[i].Value, "", string(*tag))) (*st.Commands)[i].Value = stringPtr(strings.ReplaceAll(*(*st.Commands)[i].Value, "", string(*tag))) } } func stringPtr(s string) *string { return &s }