From 65190e6b1370d1107057850c6e496b8ebf71cc6b Mon Sep 17 00:00:00 2001 From: Gai Lazar Date: Wed, 3 Apr 2024 16:30:29 +0300 Subject: [PATCH 1/5] New XSC analytics metrics capabilities (#1165) --- go.mod | 4 ++-- go.sum | 4 ++-- utils/config/config.go | 15 +++++++++++++++ 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index a4c898986..c7f5382ac 100644 --- a/go.mod +++ b/go.mod @@ -96,8 +96,8 @@ require ( gopkg.in/warnings.v0 v0.1.2 // indirect ) -// replace github.com/jfrog/jfrog-client-go => github.com/jfrog/jfrog-client-go v1.28.1-0.20240327154209-77a304635e42 +replace github.com/jfrog/jfrog-client-go => github.com/jfrog/jfrog-client-go v1.28.1-0.20240403100335-8292671b7cc4 -// replace github.com/jfrog/build-info-go => github.com/jfrog/build-info-go v1.8.9-0.20240319160313-0093dee91fc1 +// replace github.com/jfrog/build-info-go => github.com/jfrog/build-info-go dev // replace github.com/jfrog/gofrog => github.com/jfrog/gofrog v1.3.3-0.20231223133729-ef57bd08cedc diff --git a/go.sum b/go.sum index a5a38cc2e..7aa3a37b6 100644 --- a/go.sum +++ b/go.sum @@ -87,8 +87,8 @@ github.com/jfrog/build-info-go v1.9.25 h1:IkjydGQA/HjOWjRaoKq1hOEgCCyBEJwQgXJSo4 github.com/jfrog/build-info-go v1.9.25/go.mod h1:doFB4bFDVHeGulD6GF9LzsrRaIOrSoklV9DgIAEqHgc= github.com/jfrog/gofrog v1.6.3 h1:F7He0+75HcgCe6SGTSHLFCBDxiE2Ja0tekvvcktW6wc= github.com/jfrog/gofrog v1.6.3/go.mod h1:SZ1EPJUruxrVGndOzHd+LTiwWYKMlHqhKD+eu+v5Hqg= -github.com/jfrog/jfrog-client-go v1.39.0 h1:GZ1qbpUDzYz8ZEycYicDkbVMN2H0VSCuz8mUNTyf7tc= -github.com/jfrog/jfrog-client-go v1.39.0/go.mod h1:tUyEmxznphh0nwAGo6xz9Sps7RRW/TBMxIJZteo+j2k= +github.com/jfrog/jfrog-client-go v1.28.1-0.20240403100335-8292671b7cc4 h1:A67yoFRYjRzg+xhLYhH0QN7b4/wggRa/lSQKSjzOwNQ= +github.com/jfrog/jfrog-client-go v1.28.1-0.20240403100335-8292671b7cc4/go.mod h1:tUyEmxznphh0nwAGo6xz9Sps7RRW/TBMxIJZteo+j2k= github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= diff --git a/utils/config/config.go b/utils/config/config.go index 392342210..2a073da63 100644 --- a/utils/config/config.go +++ b/utils/config/config.go @@ -19,6 +19,7 @@ import ( "github.com/jfrog/jfrog-client-go/utils/io/fileutils" "github.com/jfrog/jfrog-client-go/utils/log" xrayAuth "github.com/jfrog/jfrog-client-go/xray/auth" + xscAuth "github.com/jfrog/jfrog-client-go/xsc/auth" "os" "path/filepath" "strconv" @@ -572,6 +573,7 @@ type ServerDetails struct { ArtifactoryUrl string `json:"artifactoryUrl,omitempty"` DistributionUrl string `json:"distributionUrl,omitempty"` XrayUrl string `json:"xrayUrl,omitempty"` + XscUrl string `json:"xscUrl,omitempty"` MissionControlUrl string `json:"missionControlUrl,omitempty"` PipelinesUrl string `json:"pipelinesUrl,omitempty"` AccessUrl string `json:"accessUrl,omitempty"` @@ -708,6 +710,19 @@ func (serverDetails *ServerDetails) CreateXrayAuthConfig() (auth.ServiceDetails, return serverDetails.createAuthConfig(artAuth) } +func (serverDetails *ServerDetails) CreateXscAuthConfig() (auth.ServiceDetails, error) { + ascAuth := xscAuth.NewXscDetails() + ascAuth.SetUrl(serverDetails.convertXrayUrlToXscUrl()) + return serverDetails.createAuthConfig(ascAuth) +} + +// Xray and Xsc will always have the same platform url. +func (serverDetails *ServerDetails) convertXrayUrlToXscUrl() string { + xscUrl := strings.TrimSuffix(serverDetails.XrayUrl, "/") + xscUrl = strings.TrimSuffix(xscUrl, "/xray") + return xscUrl + "/xsc/" +} + func (serverDetails *ServerDetails) CreatePipelinesAuthConfig() (auth.ServiceDetails, error) { pAuth := pipelinesAuth.NewPipelinesDetails() pAuth.SetUrl(serverDetails.PipelinesUrl) From 2fbfc02df7d357f47829144a70907ff7263d6ea5 Mon Sep 17 00:00:00 2001 From: Assaf Attias <49212512+attiasas@users.noreply.github.com> Date: Wed, 3 Apr 2024 17:17:10 +0300 Subject: [PATCH 2/5] Duplicate App Name at plugins help usage (#1167) --- plugins/components/conversionlayer.go | 4 ++-- plugins/components/conversionlayer_test.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/components/conversionlayer.go b/plugins/components/conversionlayer.go index fafe2d9bd..dae7da806 100644 --- a/plugins/components/conversionlayer.go +++ b/plugins/components/conversionlayer.go @@ -18,7 +18,7 @@ func ConvertApp(jfrogApp App) (*cli.App, error) { app.Name = jfrogApp.Name app.Description = jfrogApp.Description app.Version = jfrogApp.Version - app.Commands, err = ConvertAppCommands(jfrogApp, jfrogApp.Name) + app.Commands, err = ConvertAppCommands(jfrogApp) if err != nil { return nil, err } @@ -127,7 +127,7 @@ func createCommandUsages(cmd Command, convertedStringFlags map[string]StringFlag } func getCmdUsageString(cmd Command, namespaces ...string) string { - return coreutils.GetCliExecutableName() + " " + strings.Join(append(removeEmptyValues(namespaces), cmd.Name), " ") + return strings.Join(append(removeEmptyValues(namespaces), cmd.Name), " ") } // Generated usages are based on the command's flags and arguments: diff --git a/plugins/components/conversionlayer_test.go b/plugins/components/conversionlayer_test.go index 2e237fc9d..018e65502 100644 --- a/plugins/components/conversionlayer_test.go +++ b/plugins/components/conversionlayer_test.go @@ -11,7 +11,7 @@ import ( func TestCreateCommandUsages(t *testing.T) { appNameSpace := "test-app" cmdName := "test-command" - expectedPrefix := fmt.Sprintf("%s %s %s", coreutils.GetCliExecutableName(), appNameSpace, cmdName) + expectedPrefix := fmt.Sprintf("%s %s", appNameSpace, cmdName) optFlag := NewBoolFlag("dummyFlag", "") optStrFlag := NewStringFlag("optFlag", "", WithHelpValue("alias")) From 3df49e9a9d645281123d865d2af7eb1a1d940548 Mon Sep 17 00:00:00 2001 From: Asaf Ambar Date: Thu, 4 Apr 2024 10:56:04 +0300 Subject: [PATCH 3/5] Support pip for curation (#1155) --- artifactory/commands/python/poetry.go | 2 +- artifactory/commands/python/python.go | 2 +- utils/coreutils/coreconsts.go | 2 ++ utils/python/utils.go | 11 ++++++++--- utils/python/utils_test.go | 28 +++++++++++++++++++++++++++ 5 files changed, 40 insertions(+), 5 deletions(-) diff --git a/artifactory/commands/python/poetry.go b/artifactory/commands/python/poetry.go index da91baa99..33152c9cf 100644 --- a/artifactory/commands/python/poetry.go +++ b/artifactory/commands/python/poetry.go @@ -126,7 +126,7 @@ func (pc *PoetryCommand) SetCommandName(commandName string) *PoetryCommand { } func (pc *PoetryCommand) SetPypiRepoUrlWithCredentials() error { - rtUrl, username, password, err := python.GetPypiRepoUrlWithCredentials(pc.serverDetails, pc.repository) + rtUrl, username, password, err := python.GetPypiRepoUrlWithCredentials(pc.serverDetails, pc.repository, false) if err != nil { return err } diff --git a/artifactory/commands/python/python.go b/artifactory/commands/python/python.go index 8d2de8b3e..fa0c37718 100644 --- a/artifactory/commands/python/python.go +++ b/artifactory/commands/python/python.go @@ -100,7 +100,7 @@ func (pc *PythonCommand) SetCommandName(commandName string) *PythonCommand { } func (pc *PythonCommand) SetPypiRepoUrlWithCredentials() error { - rtUrl, err := python.GetPypiRepoUrl(pc.serverDetails, pc.repository) + rtUrl, err := python.GetPypiRepoUrl(pc.serverDetails, pc.repository, false) if err != nil { return err } diff --git a/utils/coreutils/coreconsts.go b/utils/coreutils/coreconsts.go index 31855afcd..e9d64a753 100644 --- a/utils/coreutils/coreconsts.go +++ b/utils/coreutils/coreconsts.go @@ -34,6 +34,8 @@ const ( JfrogTransferStateFileName = "state.json" PluginsExecDirName = "bin" PluginsResourcesDirName = "resources" + //#nosec G101 + CurationPassThroughApi = "api/curation/audit/" //#nosec G101 ErrorHandling = "JFROG_CLI_ERROR_HANDLING" diff --git a/utils/python/utils.go b/utils/python/utils.go index 83d880575..7a599b28a 100644 --- a/utils/python/utils.go +++ b/utils/python/utils.go @@ -2,6 +2,7 @@ package utils import ( "fmt" + "github.com/jfrog/jfrog-cli-core/v2/utils/coreutils" "net/url" "os" "path/filepath" @@ -25,7 +26,7 @@ const ( pyproject = "pyproject.toml" ) -func GetPypiRepoUrlWithCredentials(serverDetails *config.ServerDetails, repository string) (*url.URL, string, string, error) { +func GetPypiRepoUrlWithCredentials(serverDetails *config.ServerDetails, repository string, isCurationCmd bool) (*url.URL, string, string, error) { rtUrl, err := url.Parse(serverDetails.GetArtifactoryUrl()) if err != nil { return nil, "", "", errorutils.CheckError(err) @@ -41,6 +42,10 @@ func GetPypiRepoUrlWithCredentials(serverDetails *config.ServerDetails, reposito } password = serverDetails.GetAccessToken() } + // In case of curation command, the download urls should be routed through a dedicated api. + if isCurationCmd { + rtUrl.Path += coreutils.CurationPassThroughApi + } rtUrl.Path += "api/pypi/" + repository + "/simple" return rtUrl, username, password, err } @@ -52,8 +57,8 @@ func GetPypiRemoteRegistryFlag(tool pythonutils.PythonTool) string { return pipenvRemoteRegistryFlag } -func GetPypiRepoUrl(serverDetails *config.ServerDetails, repository string) (string, error) { - rtUrl, username, password, err := GetPypiRepoUrlWithCredentials(serverDetails, repository) +func GetPypiRepoUrl(serverDetails *config.ServerDetails, repository string, isCurationCmd bool) (string, error) { + rtUrl, username, password, err := GetPypiRepoUrlWithCredentials(serverDetails, repository, isCurationCmd) if err != nil { return "", err } diff --git a/utils/python/utils_test.go b/utils/python/utils_test.go index 45b2b19be..3ef8785c8 100644 --- a/utils/python/utils_test.go +++ b/utils/python/utils_test.go @@ -1,7 +1,11 @@ package utils import ( + "github.com/jfrog/jfrog-cli-core/v2/utils/config" + "github.com/jfrog/jfrog-cli-core/v2/utils/coreutils" + "github.com/stretchr/testify/require" "path/filepath" + "strings" "testing" "github.com/jfrog/jfrog-cli-core/v2/utils/tests" @@ -31,3 +35,27 @@ func initPoetryTest(t *testing.T) (string, func()) { poetryProjectPath, cleanUp := tests.CreateTestWorkspace(t, testAbs) return poetryProjectPath, cleanUp } + +func TestGetPypiRepoUrlWithCredentials(t *testing.T) { + tests := []struct { + name string + curationCmd bool + }{ + { + name: "test curation command true", + curationCmd: true, + }, + { + name: "test curation command false", + curationCmd: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + url, _, _, err := GetPypiRepoUrlWithCredentials(&config.ServerDetails{}, "test", tt.curationCmd) + require.NoError(t, err) + assert.Equal(t, tt.curationCmd, strings.Contains(url.Path, coreutils.CurationPassThroughApi)) + }) + } +} From 13680c04f22e69a9e63aa00ba2602a54c7c41038 Mon Sep 17 00:00:00 2001 From: Eyal Delarea Date: Mon, 8 Apr 2024 10:41:56 +0300 Subject: [PATCH 4/5] Artifactory Release Lifecycle Management - Add Import bundle function (#1153) --- go.mod | 2 +- go.sum | 4 +-- lifecycle/import.go | 60 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 63 insertions(+), 3 deletions(-) create mode 100644 lifecycle/import.go diff --git a/go.mod b/go.mod index c7f5382ac..4bee0e1b0 100644 --- a/go.mod +++ b/go.mod @@ -96,7 +96,7 @@ require ( gopkg.in/warnings.v0 v0.1.2 // indirect ) -replace github.com/jfrog/jfrog-client-go => github.com/jfrog/jfrog-client-go v1.28.1-0.20240403100335-8292671b7cc4 +replace github.com/jfrog/jfrog-client-go => github.com/jfrog/jfrog-client-go v1.28.1-0.20240408071430-62ee0279ac58 // replace github.com/jfrog/build-info-go => github.com/jfrog/build-info-go dev diff --git a/go.sum b/go.sum index 7aa3a37b6..2ffd059e2 100644 --- a/go.sum +++ b/go.sum @@ -87,8 +87,8 @@ github.com/jfrog/build-info-go v1.9.25 h1:IkjydGQA/HjOWjRaoKq1hOEgCCyBEJwQgXJSo4 github.com/jfrog/build-info-go v1.9.25/go.mod h1:doFB4bFDVHeGulD6GF9LzsrRaIOrSoklV9DgIAEqHgc= github.com/jfrog/gofrog v1.6.3 h1:F7He0+75HcgCe6SGTSHLFCBDxiE2Ja0tekvvcktW6wc= github.com/jfrog/gofrog v1.6.3/go.mod h1:SZ1EPJUruxrVGndOzHd+LTiwWYKMlHqhKD+eu+v5Hqg= -github.com/jfrog/jfrog-client-go v1.28.1-0.20240403100335-8292671b7cc4 h1:A67yoFRYjRzg+xhLYhH0QN7b4/wggRa/lSQKSjzOwNQ= -github.com/jfrog/jfrog-client-go v1.28.1-0.20240403100335-8292671b7cc4/go.mod h1:tUyEmxznphh0nwAGo6xz9Sps7RRW/TBMxIJZteo+j2k= +github.com/jfrog/jfrog-client-go v1.28.1-0.20240408071430-62ee0279ac58 h1:yyhOfECY3WGs6MsnJQWm/U7DYNIzxBiVlEwQ3RvqxwQ= +github.com/jfrog/jfrog-client-go v1.28.1-0.20240408071430-62ee0279ac58/go.mod h1:tUyEmxznphh0nwAGo6xz9Sps7RRW/TBMxIJZteo+j2k= github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= diff --git a/lifecycle/import.go b/lifecycle/import.go new file mode 100644 index 000000000..084ca6205 --- /dev/null +++ b/lifecycle/import.go @@ -0,0 +1,60 @@ +package lifecycle + +import ( + "fmt" + "github.com/jfrog/jfrog-cli-core/v2/artifactory/utils" + "github.com/jfrog/jfrog-cli-core/v2/utils/config" + "github.com/jfrog/jfrog-client-go/utils/io/fileutils" + "github.com/jfrog/jfrog-client-go/utils/log" +) + +type ReleaseBundleImportCommand struct { + releaseBundleCmd + filePath string +} + +func (rbi *ReleaseBundleImportCommand) ServerDetails() (*config.ServerDetails, error) { + return rbi.serverDetails, nil +} + +func (rbi *ReleaseBundleImportCommand) CommandName() string { + return "rb_import" +} + +func NewReleaseBundleImportCommand() *ReleaseBundleImportCommand { + return &ReleaseBundleImportCommand{} +} +func (rbi *ReleaseBundleImportCommand) SetServerDetails(serverDetails *config.ServerDetails) *ReleaseBundleImportCommand { + rbi.serverDetails = serverDetails + return rbi +} + +func (rbi *ReleaseBundleImportCommand) SetFilepath(filePath string) *ReleaseBundleImportCommand { + rbi.filePath = filePath + return rbi +} + +func (rbi *ReleaseBundleImportCommand) Run() (err error) { + if err = validateArtifactoryVersionSupported(rbi.serverDetails); err != nil { + return + } + artService, err := utils.CreateServiceManager(rbi.serverDetails, 3, 0, false) + if err != nil { + return + } + + exists, err := fileutils.IsFileExists(rbi.filePath, false) + if err != nil { + return + } + if !exists { + return fmt.Errorf("file not found: %s", rbi.filePath) + } + + log.Info("Importing the release bundle archive...") + if err = artService.ImportReleaseBundle(rbi.filePath); err != nil { + return + } + log.Info("Successfully imported the release bundle archive") + return +} From d5bc1eb525b6767393ccd9b3e9b7be6e96c06bd6 Mon Sep 17 00:00:00 2001 From: Eyal Delarea Date: Mon, 15 Apr 2024 12:05:13 +0300 Subject: [PATCH 5/5] Fix npm packed tarball files identification (#1171) --- artifactory/utils/npm/pack.go | 34 +++++++++++++++++++--- tests/testdata/npm-workspaces/package.json | 3 +- 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/artifactory/utils/npm/pack.go b/artifactory/utils/npm/pack.go index 9f9686709..24ff8e289 100644 --- a/artifactory/utils/npm/pack.go +++ b/artifactory/utils/npm/pack.go @@ -1,6 +1,8 @@ package npm import ( + "fmt" + "github.com/jfrog/jfrog-client-go/utils/io/fileutils" "strings" gofrogcmd "github.com/jfrog/gofrog/io" @@ -8,13 +10,17 @@ import ( "github.com/jfrog/jfrog-client-go/utils/errorutils" ) +const ( + npmPackedTarballSuffix = ".tgz" +) + func Pack(npmFlags []string, executablePath string) ([]string, error) { configListCmdConfig := createPackCmdConfig(executablePath, npmFlags) output, err := gofrogcmd.RunCmdOutput(configListCmdConfig) if err != nil { return []string{}, errorutils.CheckError(err) } - return getPackageFileNameFromOutput(output), nil + return getPackageFileNameFromOutput(output) } func createPackCmdConfig(executablePath string, splitFlags []string) *npmutils.NpmConfig { @@ -27,7 +33,27 @@ func createPackCmdConfig(executablePath string, splitFlags []string) *npmutils.N } } -func getPackageFileNameFromOutput(output string) []string { - output = strings.TrimSpace(output) - return strings.Split(output, "\n") +// Extracts packed file names from npm pack command output +// The output can differ when a prePack script exists, +// This function will filter the output and search for the .tgz files +// To avoid misidentifying files, we will verify file exists +func getPackageFileNameFromOutput(output string) (packedTgzFiles []string, err error) { + lines := strings.Split(output, "\n") + var packedFileNamesFromOutput []string + for _, line := range lines { + line = strings.TrimSpace(line) + if strings.HasSuffix(line, npmPackedTarballSuffix) { + packedFileNamesFromOutput = append(packedFileNamesFromOutput, line) + } + } + for _, file := range packedFileNamesFromOutput { + exists, err := fileutils.IsFileExists(file, true) + if err != nil { + return nil, fmt.Errorf("error occurred while checking packed npm tarball exists: %w", err) + } + if exists { + packedTgzFiles = append(packedTgzFiles, file) + } + } + return } diff --git a/tests/testdata/npm-workspaces/package.json b/tests/testdata/npm-workspaces/package.json index 59c936a79..ee7c9c0a6 100644 --- a/tests/testdata/npm-workspaces/package.json +++ b/tests/testdata/npm-workspaces/package.json @@ -4,7 +4,8 @@ "description": "", "main": "index.js", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "test": "echo \"Error: no test specified\" && exit 1", + "prepack": "echo 'prepack script executed'" }, "keywords": [], "author": "",