diff --git a/artifactory/cli.go b/artifactory/cli.go index 89a8aab0c..2aaac4838 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 { @@ -1268,15 +1270,25 @@ func prepareDownloadCommand(c *cli.Context) (*spec.SpecFiles, error) { } var downloadSpec *spec.SpecFiles + var isRbv2 bool var err error + if c.IsSet("spec") { downloadSpec, err = cliutils.GetSpec(c, true, true) } else { - downloadSpec, err = createDefaultDownloadSpec(c) + 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()) + } + } + downloadSpec, err = createDefaultDownloadSpec(c, isRbv2) } + if err != nil { return nil, err } + setTransitiveInDownloadSpec(downloadSpec) err = spec.ValidateSpec(downloadSpec.Files, false, true) if err != nil { @@ -1290,6 +1302,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 +1343,37 @@ 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") + 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(c.String("project"), bundleNameAndVersion) +} + +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") + } + // Set PlatformToLifecycleUrls + lcDetails.ArtifactoryUrl = utilsForLC.AddTrailingSlashIfNeeded(lcDetails.Url) + "artifactory/" + lcDetails.LifecycleUrl = utilsForLC.AddTrailingSlashIfNeeded(lcDetails.Url) + "lifecycle/" + lcDetails.Url = "" + + return lcDetails, nil +} + 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) @@ -2643,13 +2687,21 @@ func createGitLfsCleanConfiguration(c *cli.Context) (gitLfsCleanConfiguration *g return } -func createDefaultDownloadSpec(c *cli.Context) (*spec.SpecFiles, error) { +func createDefaultDownloadSpec(c *cli.Context, isRbv2 bool) (*spec.SpecFiles, error) { offset, limit, err := getOffsetAndLimitValues(c) if err != nil { return nil, err } + var source string + if isRbv2 { + source = buildSourceForRbv2(c) + c.Set("bundle", "") + } 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 +2726,18 @@ func createDefaultDownloadSpec(c *cli.Context) (*spec.SpecFiles, error) { BuildSpec(), nil } +func buildSourceForRbv2(c *cli.Context) string { + bundleNameAndVersion := c.String("bundle") + projectKey := c.String("project") + source := projectKey + // If projectKey is not empty, append "-" to it + if projectKey != "" { + source += "-" + } + source += releaseBundlesV2 + "/" + bundleNameAndVersion + "/" + return source +} + func setTransitiveInDownloadSpec(downloadSpec *spec.SpecFiles) { transitive := os.Getenv(coreutils.TransitiveDownload) if transitive == "" { diff --git a/artifactory_test.go b/artifactory_test.go index 9ab9cd2b1..a5e755423 100644 --- a/artifactory_test.go +++ b/artifactory_test.go @@ -223,10 +223,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, "artifactory/api/release/bundles/", "cli-tests", "2") cleanArtifactoryTest() }() initArtifactoryTest(t, "") @@ -240,6 +241,29 @@ func TestReleaseBundleImportOnPrem(t *testing.T) { assert.NoError(t, lcCli.Exec("rbi", testFilePath)) } +func TestReleaseBundleV2Download(t *testing.T) { + buildNumber := "5" + defer func() { + deleteReceivedReleaseBundle(t, "lifecycle/api/v2/release_bundle/records/", tests.LcRbName1, buildNumber) + cleanArtifactoryTest() + inttestutils.DeleteBuild(serverDetails.ArtifactoryUrl, tests.RtBuildName1, artHttpDetails) + }() + initArtifactoryTest(t, "") + initLifecycleCli() + + // Create and publish build + 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) + + // Download RBV2 + err = artifactoryCli.Exec([]string{"download", "--bundle=" + tests.LcRbName1 + "/" + buildNumber}...) + assert.NoError(t, err) +} + func TestArtifactoryUploadPathWithSpecialCharsAsNoRegex(t *testing.T) { initArtifactoryTest(t, "") filePath := getSpecialCharFilePath() @@ -5654,10 +5678,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 a7bb3bb69..4b169e67d 100644 --- a/go.mod +++ b/go.mod @@ -178,3 +178,8 @@ 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 => ../jfrog-cli-core + +replace github.com/jfrog/jfrog-client-go => ../jfrog-client-go diff --git a/go.sum b/go.sum index 10861dc21..b4ce81483 100644 --- a/go.sum +++ b/go.sum @@ -407,8 +407,6 @@ golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDf golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= -golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f h1:XdNn9LlyWAhLVp6P/i8QYBW+hlyhrhei9uErw2B5GJo= -golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f/go.mod h1:D5SMRVC3C2/4+F/DB1wZsLRnSNimn2Sp/NPsCrsv8ak= golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67 h1:1UoZQm6f0P/ZO0w1Ri+f+ifG/gXhegadRdwBIXEFWDo= golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= @@ -507,9 +505,8 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.27.0 h1:qEKojBykQkQ4EynWy4S8Weg69NumxKdn40Fce3uc/8o= -golang.org/x/tools v0.27.0/go.mod h1:sUi0ZgbwW9ZPAq26Ekut+weQPR5eIM6GQLQ1Yjm1H0Q= golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8= +golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=