From 6a8be5913a67bcb65d1a98395d0165eed0215092 Mon Sep 17 00:00:00 2001 From: oshratz Date: Thu, 9 Jan 2025 09:02:53 +0200 Subject: [PATCH] Enhance download functionality to support RBV2 --- artifactory/cli.go | 94 ++++++++++++++++++++++++++++++++++++++++++++- artifactory_test.go | 37 ++++++++++++++++-- go.mod | 4 ++ go.sum | 8 ++-- 4 files changed, 135 insertions(+), 8 deletions(-) diff --git a/artifactory/cli.go b/artifactory/cli.go index 89a8aab0c..922f06e07 100644 --- a/artifactory/cli.go +++ b/artifactory/cli.go @@ -118,6 +118,7 @@ import ( buildinfocmd "github.com/jfrog/jfrog-client-go/artifactory/buildinfo" "github.com/jfrog/jfrog-client-go/artifactory/services" clientutils "github.com/jfrog/jfrog-client-go/utils" + utilsForLC "github.com/jfrog/jfrog-client-go/utils" "github.com/jfrog/jfrog-client-go/utils/errorutils" "github.com/jfrog/jfrog-client-go/utils/log" "github.com/jszwec/csvutil" @@ -133,6 +134,7 @@ const ( userCategory = "User Management" transferCategory = "Transfer Between Artifactory Instances" otherCategory = "Other" + releaseBundlesV2 = "release-bundles-v2" ) func GetCommands() []cli.Command { @@ -1269,14 +1271,17 @@ func prepareDownloadCommand(c *cli.Context) (*spec.SpecFiles, error) { var downloadSpec *spec.SpecFiles var err error + if c.IsSet("spec") { downloadSpec, err = cliutils.GetSpec(c, true, true) } else { downloadSpec, err = createDefaultDownloadSpec(c) } + if err != nil { return nil, err } + setTransitiveInDownloadSpec(downloadSpec) err = spec.ValidateSpec(downloadSpec.Files, false, true) if err != nil { @@ -1290,6 +1295,7 @@ func downloadCmd(c *cli.Context) error { if err != nil { return err } + fixWinPathsForDownloadCmd(downloadSpec, c) configuration, err := cliutils.CreateDownloadConfiguration(c) if err != nil { @@ -1330,6 +1336,54 @@ func downloadCmd(c *cli.Context) error { return cliutils.GetCliError(err, result.SuccessCount(), result.FailCount(), cliutils.IsFailNoOp(c)) } +func checkRbExistenceInV2(c *cli.Context) (bool, error) { + bundleNameAndVersion := c.String("bundle") + parts := strings.Split(bundleNameAndVersion, "/") + rbName := parts[0] + rbVersion := parts[1] + + lcDetails, err := createLifecycleDetailsByFlags(c) + if err != nil { + return false, err + } + + lcServicesManager, err := utils.CreateLifecycleServiceManager(lcDetails, false) + if err != nil { + return false, err + } + + return lcServicesManager.IsReleaseBundleExist(rbName, rbVersion, c.String("project")) +} + +func createLifecycleDetailsByFlags(c *cli.Context) (*coreConfig.ServerDetails, error) { + lcDetails, err := cliutils.CreateServerDetailsWithConfigOffer(c, true, commonCliUtils.Platform) + if err != nil { + return nil, err + } + if lcDetails.Url == "" { + return nil, errors.New("platform URL is mandatory for lifecycle commands") + } + PlatformToLifecycleUrls(lcDetails) + return lcDetails, nil +} + +func PlatformToLifecycleUrls(lcDetails *coreConfig.ServerDetails) { + // For tests only. in prod - this "if" will always return false + if strings.Contains(lcDetails.Url, "artifactory/") { + lcDetails.ArtifactoryUrl = utilsForLC.AddTrailingSlashIfNeeded(lcDetails.Url) + lcDetails.LifecycleUrl = strings.Replace( + utilsForLC.AddTrailingSlashIfNeeded(lcDetails.Url), + "artifactory/", + "lifecycle/", + 1, + ) + } else { + lcDetails.ArtifactoryUrl = utilsForLC.AddTrailingSlashIfNeeded(lcDetails.Url) + "artifactory/" + lcDetails.LifecycleUrl = utilsForLC.AddTrailingSlashIfNeeded(lcDetails.Url) + "lifecycle/" + } + lcDetails.Url = "" +} + func uploadCmd(c *cli.Context) (err error) { if c.NArg() > 0 && c.IsSet("spec") { return cliutils.PrintHelpAndReturnError("No arguments should be sent when the spec option is used.", c) @@ -2648,8 +2702,27 @@ func createDefaultDownloadSpec(c *cli.Context) (*spec.SpecFiles, error) { if err != nil { return nil, err } + + var isRbv2 bool + var source string + if c.IsSet("bundle") { + isRbv2, err = checkRbExistenceInV2(c) + if err != nil { + log.Debug("Error occurred while checking if the bundle exists in rbv2:", err.Error()) + } + } + + if isRbv2 { + source, err = buildSourceForRbv2(c) + if err != nil { + return nil, err + } + } else { + source = strings.TrimPrefix(c.Args().Get(0), "/") + } + return spec.NewBuilder(). - Pattern(strings.TrimPrefix(c.Args().Get(0), "/")). + Pattern(source). Props(c.String("props")). ExcludeProps(c.String("exclude-props")). Build(c.String("build")). @@ -2674,6 +2747,25 @@ func createDefaultDownloadSpec(c *cli.Context) (*spec.SpecFiles, error) { BuildSpec(), nil } +func buildSourceForRbv2(c *cli.Context) (string, error) { + bundleNameAndVersion := c.String("bundle") + projectKey := c.String("project") + source := projectKey + + // reset bundle flag + err := c.Set("bundle", "") + if err != nil { + return "", err + } + + // If projectKey is not empty, append "-" to it + if projectKey != "" { + source += "-" + } + source += releaseBundlesV2 + "/" + bundleNameAndVersion + "/" + return source, nil +} + func setTransitiveInDownloadSpec(downloadSpec *spec.SpecFiles) { transitive := os.Getenv(coreutils.TransitiveDownload) if transitive == "" { diff --git a/artifactory_test.go b/artifactory_test.go index 9ab9cd2b1..5806b2800 100644 --- a/artifactory_test.go +++ b/artifactory_test.go @@ -67,6 +67,8 @@ import ( // https://jira.jfrog.org/browse/JA-2620 // Minimum Artifactory version with Terraform support const terraformMinArtifactoryVersion = "7.38.4" +const deleteReleaseBundleV1ApiUrl = "artifactory/api/release/bundles/" +const deleteReleaseBundleV2ApiUrl = "lifecycle/api/v2/release_bundle/records/" // JFrog CLI for Artifactory sub-commands (jfrog rt ...) var artifactoryCli *coretests.JfrogCli @@ -223,10 +225,11 @@ func TestArtifactorySimpleUploadSpecUsingConfig(t *testing.T) { inttestutils.VerifyExistInArtifactory(tests.GetSimpleUploadExpectedRepo1(), searchFilePath, serverDetails, t) cleanArtifactoryTest() } + func TestReleaseBundleImportOnPrem(t *testing.T) { // Cleanup defer func() { - deleteReceivedReleaseBundle(t, "cli-tests", "2") + deleteReceivedReleaseBundle(t, deleteReleaseBundleV1ApiUrl, "cli-tests", "2") cleanArtifactoryTest() }() initArtifactoryTest(t, "") @@ -240,6 +243,34 @@ func TestReleaseBundleImportOnPrem(t *testing.T) { assert.NoError(t, lcCli.Exec("rbi", testFilePath)) } +func TestReleaseBundleV2Download(t *testing.T) { + buildNumber := "5" + defer func() { + deleteReceivedReleaseBundle(t, deleteReleaseBundleV2ApiUrl, tests.LcRbName1, buildNumber) + inttestutils.DeleteBuild(serverDetails.ArtifactoryUrl, tests.RtBuildName1, artHttpDetails) + cleanArtifactoryTest() + }() + initArtifactoryTest(t, "") + initLifecycleCli() + + runRt(t, "upload", "testdata/a/a1.in", tests.RtRepo1, "--build-name="+tests.RtBuildName1, "--build-number="+buildNumber) + runRt(t, "build-publish", tests.RtBuildName1, buildNumber) + + // Create RBV2 + err := lcCli.Exec("rbc", tests.LcRbName1, buildNumber, "--build-name="+tests.RtBuildName1, "--build-number="+buildNumber) + assert.NoError(t, err) + + runRt(t, "download", "--bundle="+tests.LcRbName1+"/"+buildNumber) + + wd, err := os.Getwd() + exists, err := fileutils.IsDirExists(filepath.Join(wd, tests.LcRbName1), false) + + assert.NoError(t, err) + assert.True(t, exists) + + clientTestUtils.RemoveAllAndAssert(t, filepath.Join(wd, tests.LcRbName1)) +} + func TestArtifactoryUploadPathWithSpecialCharsAsNoRegex(t *testing.T) { initArtifactoryTest(t, "") filePath := getSpecialCharFilePath() @@ -5654,10 +5685,10 @@ func sendArtifactoryTrustedPublicKey(t *testing.T, artHttpDetails httputils.Http assert.NoError(t, err) } -func deleteReceivedReleaseBundle(t *testing.T, bundleName, bundleVersion string) { +func deleteReceivedReleaseBundle(t *testing.T, url, bundleName, bundleVersion string) { client, err := httpclient.ClientBuilder().Build() assert.NoError(t, err) - deleteApi := path.Join("artifactory/api/release/bundles/", bundleName, bundleVersion) + deleteApi := path.Join(url, bundleName, bundleVersion) _, _, err = client.SendDelete(*tests.JfrogUrl+deleteApi, []byte{}, artHttpDetails, "Deleting release bundle") assert.NoError(t, err) } diff --git a/go.mod b/go.mod index c51dc8755..b0a2c6133 100644 --- a/go.mod +++ b/go.mod @@ -179,3 +179,7 @@ require ( // replace github.com/jfrog/build-info-go => github.com/jfrog/build-info-go v1.8.9-0.20241220065541-91828d43d8b9 // replace github.com/jfrog/gofrog => github.com/jfrog/gofrog dev + +replace github.com/jfrog/jfrog-cli-core/v2 => github.com/oshratZairi/jfrog-cli-core/v2 v2.31.1-0.20250106074438-b6a8987b66f8 + +replace github.com/jfrog/jfrog-client-go => github.com/oshratZairi/jfrog-client-go v0.0.0-20250109065511-6d1a1d3d9c84 diff --git a/go.sum b/go.sum index a14ac0b20..000853c6f 100644 --- a/go.sum +++ b/go.sum @@ -173,14 +173,10 @@ github.com/jfrog/jfrog-apps-config v1.0.1 h1:mtv6k7g8A8BVhlHGlSveapqf4mJfonwvXYL github.com/jfrog/jfrog-apps-config v1.0.1/go.mod h1:8AIIr1oY9JuH5dylz2S6f8Ym2MaadPLR6noCBO4C22w= github.com/jfrog/jfrog-cli-artifactory v0.1.11 h1:tYGQpkGZVHwYxApKhMXgY1V25QaLFaTbjsoq0l3Bf4w= github.com/jfrog/jfrog-cli-artifactory v0.1.11/go.mod h1:rVBTbanRnG9CXyAYRJ2O6h2fJMa+fsGB+3swUB/qEt0= -github.com/jfrog/jfrog-cli-core/v2 v2.57.6 h1:kI5BqDW8Q4R5HkTUPSAObTqyIgQ9z7DqeFYGOEC1zPk= -github.com/jfrog/jfrog-cli-core/v2 v2.57.6/go.mod h1:h5pzOZUb5ChGcGrXCYr3nPyXcTZjeGW2Rm1Zceo8Afg= github.com/jfrog/jfrog-cli-platform-services v1.6.0 h1:2fBIDxnQaFWStZqMEUI2I3nkNjDmknxxHcmpeLxtocc= github.com/jfrog/jfrog-cli-platform-services v1.6.0/go.mod h1:u3lMRG7XC8MeUy/OPkHkZnsgCMIi0br4sjk2/W1Pm8I= github.com/jfrog/jfrog-cli-security v1.14.0 h1:mGm/ya0Qh0YdBS7ATgmQi1r8MEkbM8Hh/ml4UPkuu/4= github.com/jfrog/jfrog-cli-security v1.14.0/go.mod h1:KGfZVb4je4fiWm+OTNWdyi8V3c2TAkNDZHxsUUEM2GE= -github.com/jfrog/jfrog-client-go v1.49.0 h1:NaTK6+LQBEJafL//6ntnS/eVx1dZMJnxydALwWHKORQ= -github.com/jfrog/jfrog-client-go v1.49.0/go.mod h1:ohIfKpMBCQsE9kunrKQ1wvoExpqsPLaluRFO186B5EM= github.com/jszwec/csvutil v1.10.0 h1:upMDUxhQKqZ5ZDCs/wy+8Kib8rZR8I8lOR34yJkdqhI= github.com/jszwec/csvutil v1.10.0/go.mod h1:/E4ONrmGkwmWsk9ae9jpXnv9QT8pLHEPcCirMFhxG9I= github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= @@ -262,6 +258,10 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= +github.com/oshratZairi/jfrog-cli-core/v2 v2.31.1-0.20250106074438-b6a8987b66f8 h1:qwQWN2V0FU5/uEiE605TvAQ4lJP+HsMBnOKhpW/ihXM= +github.com/oshratZairi/jfrog-cli-core/v2 v2.31.1-0.20250106074438-b6a8987b66f8/go.mod h1:qjd+OS20B/GKSk0/Wi1dZQC8O4d3qLYqLCQyvby4ZT4= +github.com/oshratZairi/jfrog-client-go v0.0.0-20250109065511-6d1a1d3d9c84 h1:fF3ppQZbgbu7lRuZRBJCbYqdtMbcgi2peeSLEkMVoFU= +github.com/oshratZairi/jfrog-client-go v0.0.0-20250109065511-6d1a1d3d9c84/go.mod h1:ohIfKpMBCQsE9kunrKQ1wvoExpqsPLaluRFO186B5EM= github.com/owenrumney/go-sarif v1.1.1/go.mod h1:dNDiPlF04ESR/6fHlPyq7gHKmrM0sHUvAGjsoh8ZH0U= github.com/owenrumney/go-sarif/v2 v2.3.0 h1:wP5yEpI53zr0v5cBmagXzLbHZp9Oylyo3AJDpfLBITs= github.com/owenrumney/go-sarif/v2 v2.3.0/go.mod h1:MSqMMx9WqlBSY7pXoOZWgEsVB4FDNfhcaXDA1j6Sr+w=