diff --git a/artifactory/commands/container/containermanagercommand.go b/artifactory/commands/container/containermanagercommand.go index 6190a5693..eb4e74efe 100644 --- a/artifactory/commands/container/containermanagercommand.go +++ b/artifactory/commands/container/containermanagercommand.go @@ -41,7 +41,11 @@ func (cm *ContainerCommand) PerformLogin(serverDetails *config.ServerDetails, co } } loginConfig := &container.ContainerManagerLoginConfig{ServerDetails: serverDetails} - return container.ContainerManagerLogin(cm.image, loginConfig, containerManagerType) + imageRegistry, err := cm.image.GetRegistry() + if err != nil { + return err + } + return container.ContainerManagerLogin(imageRegistry, loginConfig, containerManagerType) } return nil } diff --git a/artifactory/commands/packagemanagerlogin/packagemanagerlogin.go b/artifactory/commands/packagemanagerlogin/packagemanagerlogin.go index 2a3958669..e462724a2 100644 --- a/artifactory/commands/packagemanagerlogin/packagemanagerlogin.go +++ b/artifactory/commands/packagemanagerlogin/packagemanagerlogin.go @@ -10,21 +10,50 @@ import ( "github.com/jfrog/jfrog-cli-core/v2/artifactory/commands/repository" commandsutils "github.com/jfrog/jfrog-cli-core/v2/artifactory/commands/utils" "github.com/jfrog/jfrog-cli-core/v2/artifactory/utils" + "github.com/jfrog/jfrog-cli-core/v2/artifactory/utils/container" "github.com/jfrog/jfrog-cli-core/v2/artifactory/utils/npm" "github.com/jfrog/jfrog-cli-core/v2/artifactory/utils/yarn" "github.com/jfrog/jfrog-cli-core/v2/common/project" "github.com/jfrog/jfrog-cli-core/v2/utils/config" + "github.com/jfrog/jfrog-cli-core/v2/utils/coreutils" "github.com/jfrog/jfrog-client-go/artifactory/services" "github.com/jfrog/jfrog-client-go/utils/errorutils" "github.com/jfrog/jfrog-client-go/utils/log" + "golang.org/x/exp/maps" + "net/url" + "slices" ) +// packageManagerToRepositoryPackageType maps project types to corresponding Artifactory repository package types. +var packageManagerToRepositoryPackageType = map[project.ProjectType]string{ + // Npm package managers + project.Npm: repository.Npm, + project.Yarn: repository.Npm, + + // Python (pypi) package managers + project.Pip: repository.Pypi, + project.Pipenv: repository.Pypi, + project.Poetry: repository.Pypi, + + // Nuget package managers + project.Nuget: repository.Nuget, + project.Dotnet: repository.Nuget, + + // Docker package managers + project.Docker: repository.Docker, + project.Podman: repository.Docker, + + project.Go: repository.Go, +} + // PackageManagerLoginCommand configures registries and authentication for various package manager (npm, Yarn, Pip, Pipenv, Poetry, Go) type PackageManagerLoginCommand struct { // packageManager represents the type of package manager (e.g., NPM, Yarn). packageManager project.ProjectType // repoName is the name of the repository used for configuration. repoName string + // projectKey is the JFrog Project key in JFrog Platform. + projectKey string // serverDetails contains Artifactory server configuration. serverDetails *config.ServerDetails // commandName specifies the command for this instance. @@ -40,20 +69,19 @@ func NewPackageManagerLoginCommand(packageManager project.ProjectType) *PackageM } } -// packageManagerToPackageType maps project types to corresponding Artifactory package types (e.g., npm, pypi). -func packageManagerToPackageType(packageManager project.ProjectType) (string, error) { - switch packageManager { - case project.Npm, project.Yarn: - return repository.Npm, nil - case project.Pip, project.Pipenv, project.Poetry: - return repository.Pypi, nil - case project.Go: - return repository.Go, nil - case project.Nuget, project.Dotnet: - return repository.Nuget, nil - default: - return "", errorutils.CheckErrorf("unsupported package manager: %s", packageManager) - } +// GetSupportedPackageManagersList returns a sorted list of supported package managers. +func GetSupportedPackageManagersList() []project.ProjectType { + allSupportedPackageManagers := maps.Keys(packageManagerToRepositoryPackageType) + // Sort keys based on their natural enum order + slices.SortFunc(allSupportedPackageManagers, func(a, b project.ProjectType) int { + return int(a) - int(b) + }) + return allSupportedPackageManagers +} + +func IsSupportedPackageManager(packageManager project.ProjectType) bool { + _, exists := packageManagerToRepositoryPackageType[packageManager] + return exists } // CommandName returns the name of the login command. @@ -72,8 +100,24 @@ func (pmlc *PackageManagerLoginCommand) ServerDetails() (*config.ServerDetails, return pmlc.serverDetails, nil } +// SetRepoName assigns the repository name to the command. +func (pmlc *PackageManagerLoginCommand) SetRepoName(repoName string) *PackageManagerLoginCommand { + pmlc.repoName = repoName + return pmlc +} + +// SetProjectKey assigns the project key to the command. +func (pmlc *PackageManagerLoginCommand) SetProjectKey(projectKey string) *PackageManagerLoginCommand { + pmlc.projectKey = projectKey + return pmlc +} + // Run executes the configuration method corresponding to the package manager specified for the command. func (pmlc *PackageManagerLoginCommand) Run() (err error) { + if !IsSupportedPackageManager(pmlc.packageManager) { + return errorutils.CheckErrorf("unsupported package manager: %s", pmlc.packageManager) + } + if pmlc.repoName == "" { // Prompt the user to select a virtual repository that matches the package manager. if err = pmlc.promptUserToSelectRepository(); err != nil { @@ -95,6 +139,8 @@ func (pmlc *PackageManagerLoginCommand) Run() (err error) { err = pmlc.configureGo() case project.Nuget, project.Dotnet: err = pmlc.configureDotnetNuget() + case project.Docker, project.Podman: + err = pmlc.configureContainer() default: err = errorutils.CheckErrorf("unsupported package manager: %s", pmlc.packageManager) } @@ -102,20 +148,16 @@ func (pmlc *PackageManagerLoginCommand) Run() (err error) { return fmt.Errorf("failed to configure %s: %w", pmlc.packageManager.String(), err) } - log.Info(fmt.Sprintf("Successfully configured %s to use JFrog Artifactory repository '%s'.", pmlc.packageManager.String(), pmlc.repoName)) + log.Output(fmt.Sprintf("Successfully configured %s to use JFrog Artifactory repository '%s'.", coreutils.PrintBoldTitle(pmlc.packageManager.String()), coreutils.PrintBoldTitle(pmlc.repoName))) return nil } // promptUserToSelectRepository prompts the user to select a compatible virtual repository. -func (pmlc *PackageManagerLoginCommand) promptUserToSelectRepository() error { - // Map the package manager to its corresponding package type. - packageType, err := packageManagerToPackageType(pmlc.packageManager) - if err != nil { - return err - } +func (pmlc *PackageManagerLoginCommand) promptUserToSelectRepository() (err error) { repoFilterParams := services.RepositoriesFilterParams{ RepoType: utils.Virtual.String(), - PackageType: packageType, + PackageType: packageManagerToRepositoryPackageType[pmlc.packageManager], + ProjectKey: pmlc.projectKey, } // Prompt for repository selection based on filter parameters. @@ -239,3 +281,36 @@ func (pmlc *PackageManagerLoginCommand) configureDotnetNuget() error { // Add the repository as a source in the NuGet configuration with credentials for authentication. return dotnet.AddSourceToNugetConfig(toolchainType, sourceUrl, user, password) } + +// configureContainer configures container managers like Docker or Podman to authenticate with JFrog Artifactory. +// It performs a login using the container manager's CLI command. +// +// For Docker: +// +// echo | docker login -u --password-stdin +// +// For Podman: +// +// echo | podman login -u --password-stdin +func (pmlc *PackageManagerLoginCommand) configureContainer() error { + var containerManagerType container.ContainerManagerType + switch pmlc.packageManager { + case project.Docker: + containerManagerType = container.DockerClient + case project.Podman: + containerManagerType = container.Podman + default: + return errorutils.CheckErrorf("unsupported container manager: %s", pmlc.packageManager) + } + // Parse the URL to remove the scheme (https:// or http://) + parsedPlatformURL, err := url.Parse(pmlc.serverDetails.GetUrl()) + if err != nil { + return err + } + urlWithoutScheme := parsedPlatformURL.Host + parsedPlatformURL.Path + return container.ContainerManagerLogin( + urlWithoutScheme, + &container.ContainerManagerLoginConfig{ServerDetails: pmlc.serverDetails}, + containerManagerType, + ) +} diff --git a/artifactory/commands/packagemanagerlogin/packagemanagerlogin_test.go b/artifactory/commands/packagemanagerlogin/packagemanagerlogin_test.go index 8bb95abde..9783e8133 100644 --- a/artifactory/commands/packagemanagerlogin/packagemanagerlogin_test.go +++ b/artifactory/commands/packagemanagerlogin/packagemanagerlogin_test.go @@ -13,6 +13,7 @@ import ( clientTestUtils "github.com/jfrog/jfrog-client-go/utils/tests" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "golang.org/x/exp/slices" "os" "os/exec" "path/filepath" @@ -52,6 +53,13 @@ func createTestPackageManagerLoginCommand(packageManager project.ProjectType) *P return cmd } +func TestPackageManagerLoginCommand_NotSupported(t *testing.T) { + notSupportedLoginCmd := createTestPackageManagerLoginCommand(project.Cocoapods) + err := notSupportedLoginCmd.Run() + assert.Error(t, err) + assert.ErrorContains(t, err, "unsupported package manager") +} + func TestPackageManagerLoginCommand_Npm(t *testing.T) { // Create a temporary directory to act as the environment's npmrc file location. tempDir := t.TempDir() @@ -381,3 +389,20 @@ func testBuildToolLoginCommandConfigureDotnetNuget(t *testing.T, packageManager }) } } + +func TestGetSupportedPackageManagersList(t *testing.T) { + result := GetSupportedPackageManagersList() + // Check that Go is before Pip, and Pip is before Npm using GreaterOrEqual + assert.GreaterOrEqual(t, slices.Index(result, project.Pip), slices.Index(result, project.Go), "Go should come before Pip") + assert.GreaterOrEqual(t, slices.Index(result, project.Npm), slices.Index(result, project.Pip), "Pip should come before Npm") +} + +func TestIsSupportedPackageManager(t *testing.T) { + // Test valid package managers + for pm := range packageManagerToRepositoryPackageType { + assert.True(t, IsSupportedPackageManager(pm), "Package manager %s should be supported", pm) + } + + // Test unsupported package manager + assert.False(t, IsSupportedPackageManager(project.Cocoapods), "Package manager Cocoapods should not be supported") +} diff --git a/artifactory/utils/commandsummary/utils.go b/artifactory/utils/commandsummary/utils.go index f9e51b3ec..75b9541e9 100644 --- a/artifactory/utils/commandsummary/utils.go +++ b/artifactory/utils/commandsummary/utils.go @@ -49,26 +49,38 @@ const ( buildInfoSection summarySection = "buildInfo" ) -// addGitHubTrackingToUrl adds GitHub-related query parameters to a given URL if the GITHUB_WORKFLOW environment variable is set. +const ( + // The source of the request + sourceParamKey = "s" + githubSourceValue = "1" + // The metric to track + metricParamKey = "m" + githubMetricValue = "3" + + jobIDKey = "gh_job_id" + sectionKey = "gh_section" + workflowEnvKey = "GITHUB_WORKFLOW" +) + func addGitHubTrackingToUrl(urlStr string, section summarySection) (string, error) { // Check if GITHUB_WORKFLOW environment variable is set - githubWorkflow := os.Getenv("GITHUB_WORKFLOW") + githubWorkflow := os.Getenv(workflowEnvKey) if githubWorkflow == "" { - // Return the original URL if the variable is not set return urlStr, nil } // Parse the input URL parsedUrl, err := url.Parse(urlStr) if errorutils.CheckError(err) != nil { - // Return an error if the URL is invalid return "", err } // Get the query parameters and add the GitHub tracking parameters queryParams := parsedUrl.Query() - queryParams.Set("gh_job_id", githubWorkflow) - queryParams.Set("gh_section", string(section)) + queryParams.Set(sourceParamKey, githubSourceValue) + queryParams.Set(metricParamKey, githubMetricValue) + queryParams.Set(jobIDKey, githubWorkflow) + queryParams.Set(sectionKey, string(section)) parsedUrl.RawQuery = queryParams.Encode() // Return the modified URL diff --git a/artifactory/utils/commandsummary/utils_test.go b/artifactory/utils/commandsummary/utils_test.go index 5473df4d0..62dc669ac 100644 --- a/artifactory/utils/commandsummary/utils_test.go +++ b/artifactory/utils/commandsummary/utils_test.go @@ -19,9 +19,9 @@ func TestGenerateArtifactUrl(t *testing.T) { majorVersion int expected string }{ - {"artifactory 7 without project", "", 7, "https://myplatform.com/ui/repos/tree/General/repo/path/file?clearFilter=true&gh_job_id=JFrog+CLI+Core+Tests&gh_section=test-section"}, - {"artifactory 7 with project", "proj", 7, "https://myplatform.com/ui/repos/tree/General/repo/path/file?clearFilter=true&gh_job_id=JFrog+CLI+Core+Tests&gh_section=test-section"}, - {"artifactory 6 without project", "", 6, "https://myplatform.com/artifactory/webapp/?gh_job_id=JFrog+CLI+Core+Tests&gh_section=test-section#/artifacts/browse/tree/General/repo/path/file"}, + {"artifactory 7 without project", "", 7, "https://myplatform.com/ui/repos/tree/General/repo/path/file?clearFilter=true&gh_job_id=JFrog+CLI+Core+Tests&gh_section=test-section&m=3&s=1"}, + {"artifactory 7 with project", "proj", 7, "https://myplatform.com/ui/repos/tree/General/repo/path/file?clearFilter=true&gh_job_id=JFrog+CLI+Core+Tests&gh_section=test-section&m=3&s=1"}, + {"artifactory 6 without project", "", 6, "https://myplatform.com/artifactory/webapp/?gh_job_id=JFrog+CLI+Core+Tests&gh_section=test-section&m=3&s=1#/artifacts/browse/tree/General/repo/path/file"}, } StaticMarkdownConfig.setPlatformUrl(testPlatformUrl) for _, testCase := range cases { @@ -71,7 +71,7 @@ func TestAddGitHubTrackingToUrl(t *testing.T) { "https://example.com/path", buildInfoSection, "workflow123", - "https://example.com/path?gh_job_id=workflow123&gh_section=buildInfo", + "https://example.com/path?gh_job_id=workflow123&gh_section=buildInfo&m=3&s=1", false, }, { @@ -87,7 +87,7 @@ func TestAddGitHubTrackingToUrl(t *testing.T) { "https://example.com/path?existing_param=value", packagesSection, "workflow123", - "https://example.com/path?existing_param=value&gh_job_id=workflow123&gh_section=packages", + "https://example.com/path?existing_param=value&gh_job_id=workflow123&gh_section=packages&m=3&s=1", false, }, { @@ -95,7 +95,15 @@ func TestAddGitHubTrackingToUrl(t *testing.T) { "https://example.com/path", artifactsSection, "workflow with spaces & special?characters", - "https://example.com/path?gh_job_id=workflow+with+spaces+%26+special%3Fcharacters&gh_section=artifacts", + "https://example.com/path?gh_job_id=workflow+with+spaces+%26+special%3Fcharacters&gh_section=artifacts&m=3&s=1", + false, + }, + { + "URL with spaces", + "https://example.com/path?existing_param=value", + packagesSection, + "workflow space", + "https://example.com/path?existing_param=value&gh_job_id=workflow+space&gh_section=packages&m=3&s=1", false, }, } diff --git a/artifactory/utils/container/containermanager.go b/artifactory/utils/container/containermanager.go index 8c16c6659..9867352bc 100644 --- a/artifactory/utils/container/containermanager.go +++ b/artifactory/utils/container/containermanager.go @@ -189,11 +189,7 @@ func (loginCmd *LoginCmd) RunCmd() error { // First we'll try to log in assuming a proxy-less tag (e.g. "registry-address/docker-repo/image:ver"). // If fails, we will try assuming a reverse proxy tag (e.g. "registry-address-docker-repo/image:ver"). -func ContainerManagerLogin(image *Image, config *ContainerManagerLoginConfig, containerManager ContainerManagerType) error { - imageRegistry, err := image.GetRegistry() - if err != nil { - return err - } +func ContainerManagerLogin(imageRegistry string, config *ContainerManagerLoginConfig, containerManager ContainerManagerType) error { username := config.ServerDetails.User password := config.ServerDetails.Password // If access-token exists, perform login with it. @@ -206,7 +202,7 @@ func ContainerManagerLogin(image *Image, config *ContainerManagerLoginConfig, co } // Perform login. cmd := &LoginCmd{DockerRegistry: imageRegistry, Username: username, Password: password, containerManager: containerManager} - err = cmd.RunCmd() + err := cmd.RunCmd() if exitCode := coreutils.GetExitCode(err, 0, 0, false); exitCode == coreutils.ExitCodeNoError { // Login succeeded return nil diff --git a/artifactory/utils/repositoryutils.go b/artifactory/utils/repositoryutils.go index 40e11b30f..1e3aa39a6 100644 --- a/artifactory/utils/repositoryutils.go +++ b/artifactory/utils/repositoryutils.go @@ -1,6 +1,7 @@ package utils import ( + "fmt" "github.com/jfrog/gofrog/datastructures" "github.com/jfrog/jfrog-cli-core/v2/utils/config" "github.com/jfrog/jfrog-cli-core/v2/utils/ioutils" @@ -101,7 +102,7 @@ func SelectRepositoryInteractively(serverDetails *config.ServerDetails, repoFilt return filteredRepos[0], nil } // Prompt the user to select a repository. - return ioutils.AskFromListWithMismatchConfirmation("Please select a repository to login to:", "Repository not found.", ioutils.ConvertToSuggests(filteredRepos)), nil + return ioutils.AskFromListWithMismatchConfirmation(fmt.Sprintf("Please select a %s %s repository to configure:", repoFilterParams.RepoType, repoFilterParams.PackageType), "Repository not found.", ioutils.ConvertToSuggests(filteredRepos)), nil } // GetFilteredRepositoriesWithFilterParams returns the names of local, remote, virtual, and federated repositories filtered by their names and type. diff --git a/artifactory/utils/testdata/command_summaries/extended/build-info-table.md b/artifactory/utils/testdata/command_summaries/extended/build-info-table.md index 5d0bb212c..9dba95552 100644 --- a/artifactory/utils/testdata/command_summaries/extended/build-info-table.md +++ b/artifactory/utils/testdata/command_summaries/extended/build-info-table.md @@ -1,7 +1,7 @@ -| Build Info| Security Violations| Security Issues| -|:---------|:------------|:------------| -| [buildName 123](http://myJFrogPlatform/builds/buildName/123?gh_job_id=JFrog+CLI+Core+Tests&gh_section=buildInfo) | Not scanned| Not scanned| +| Build Info | Security Violations | Security Issues | +| :--------- | :------------ | :------------ | +| [buildName 123](http://myJFrogPlatform/builds/buildName/123?gh_job_id=JFrog+CLI+Core+Tests&gh_section=buildInfo&m=3&s=1) | Not scanned | Not scanned | diff --git a/artifactory/utils/testdata/command_summaries/extended/docker-image-module.md b/artifactory/utils/testdata/command_summaries/extended/docker-image-module.md index 2300c23c0..7e57d9f5e 100644 --- a/artifactory/utils/testdata/command_summaries/extended/docker-image-module.md +++ b/artifactory/utils/testdata/command_summaries/extended/docker-image-module.md @@ -10,4 +10,4 @@ | Artifacts | Security Violations | Security Issues | | :------------ | :--------------------- | :------------------ | -|
📦 docker-local
└── 📁 image2
└── 📁 sha256:552c
└── sha256__aae9

| Not scanned | Not scanned | +|
📦 docker-local
└── 📁 image2
└── 📁 sha256:552c
└── sha256__aae9

| Not scanned | Not scanned | diff --git a/artifactory/utils/testdata/command_summaries/extended/generic-module.md b/artifactory/utils/testdata/command_summaries/extended/generic-module.md index 33e1fe0f1..c8d6e0aff 100644 --- a/artifactory/utils/testdata/command_summaries/extended/generic-module.md +++ b/artifactory/utils/testdata/command_summaries/extended/generic-module.md @@ -11,7 +11,7 @@
📦 generic-local
 └── 📁 path
     └── 📁 to
-        └── artifact2
+        └── artifact2
 
 
diff --git a/artifactory/utils/testdata/command_summaries/extended/maven-module.md b/artifactory/utils/testdata/command_summaries/extended/maven-module.md index c1454b39c..05eb108f6 100644 --- a/artifactory/utils/testdata/command_summaries/extended/maven-module.md +++ b/artifactory/utils/testdata/command_summaries/extended/maven-module.md @@ -11,7 +11,7 @@
📦 libs-release
 └── 📁 path
     └── 📁 to
-        └── artifact1
+        └── artifact1
 
 
diff --git a/artifactory/utils/testdata/command_summaries/extended/maven-nested-module.md b/artifactory/utils/testdata/command_summaries/extended/maven-nested-module.md index 5e6d5b90c..178250a80 100644 --- a/artifactory/utils/testdata/command_summaries/extended/maven-nested-module.md +++ b/artifactory/utils/testdata/command_summaries/extended/maven-nested-module.md @@ -12,7 +12,7 @@ 📦 libs-release └── 📁 path └── 📁 to - └── artifact2 + └── artifact2 @@ -22,7 +22,7 @@ 📦 libs-release └── 📁 path └── 📁 to - └── artifact3 + └── artifact3 diff --git a/artifactory/utils/testdata/command_summaries/extended/multiarch-docker-image.md b/artifactory/utils/testdata/command_summaries/extended/multiarch-docker-image.md index 5dfc53802..f3df8aea3 100644 --- a/artifactory/utils/testdata/command_summaries/extended/multiarch-docker-image.md +++ b/artifactory/utils/testdata/command_summaries/extended/multiarch-docker-image.md @@ -10,4 +10,4 @@ | Artifacts | Security Violations | Security Issues | | :------------ | :--------------------- | :------------------ | -|
linux/amd64/multiarch-image:1 (🐸 View)
📦 docker-local
└── 📁 multiarch-image
├── 📁 sha256:552c
│ └── sha256
└── sha256

| Not scanned | Not scanned | +|
linux/amd64/multiarch-image:1 (🐸 View)
📦 docker-local
└── 📁 multiarch-image
├── 📁 sha256:552c
│ └── sha256
└── sha256

| Not scanned | Not scanned | diff --git a/common/project/projectconfig.go b/common/project/projectconfig.go index 92134ae75..646f014eb 100644 --- a/common/project/projectconfig.go +++ b/common/project/projectconfig.go @@ -26,6 +26,7 @@ const ( type ProjectType int const ( + // When adding new ProjectType here, Must also add it as a string to the ProjectTypes slice Go ProjectType = iota Pip Pipenv @@ -41,6 +42,8 @@ const ( Terraform Cocoapods Swift + Docker + Podman ) type ConfigType string @@ -66,12 +69,24 @@ var ProjectTypes = []string{ "terraform", "cocoapods", "swift", + "docker", + "podman", } func (projectType ProjectType) String() string { return ProjectTypes[projectType] } +// FromString converts a string to its corresponding ProjectType +func FromString(value string) ProjectType { + for i, projectType := range ProjectTypes { + if projectType == value { + return ProjectType(i) + } + } + return -1 +} + type MissingResolverErr struct { message string } diff --git a/common/project/projectconfig_test.go b/common/project/projectconfig_test.go new file mode 100644 index 000000000..9a7edafc5 --- /dev/null +++ b/common/project/projectconfig_test.go @@ -0,0 +1,30 @@ +package project + +import ( + "github.com/stretchr/testify/assert" + "testing" +) + +func TestFromString(t *testing.T) { + // Test valid conversions + testCases := []struct { + input string + expected ProjectType + }{ + {"go", Go}, + {"pip", Pip}, + {"npm", Npm}, + {"pnpm", Pnpm}, + } + + for _, testCase := range testCases { + t.Run(testCase.input, func(t *testing.T) { + result := FromString(testCase.input) + assert.Equal(t, testCase.expected, result) + }) + } + + // Test invalid conversion + result := FromString("InvalidProject") + assert.Equal(t, ProjectType(-1), result) +} diff --git a/go.mod b/go.mod index e36659a68..702fa6e99 100644 --- a/go.mod +++ b/go.mod @@ -12,9 +12,9 @@ require ( github.com/google/uuid v1.6.0 github.com/gookit/color v1.5.4 github.com/jedib0t/go-pretty/v6 v6.6.5 - github.com/jfrog/build-info-go v1.10.7 + github.com/jfrog/build-info-go v1.10.8 github.com/jfrog/gofrog v1.7.6 - github.com/jfrog/jfrog-client-go v1.48.6 + github.com/jfrog/jfrog-client-go v1.49.0 github.com/magiconair/properties v1.8.9 github.com/manifoldco/promptui v0.9.0 github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c @@ -22,10 +22,10 @@ require ( github.com/stretchr/testify v1.10.0 github.com/urfave/cli v1.22.16 github.com/vbauerster/mpb/v8 v8.8.3 - golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f + golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 golang.org/x/mod v0.22.0 golang.org/x/sync v0.10.0 - golang.org/x/term v0.27.0 + golang.org/x/term v0.28.0 golang.org/x/text v0.21.0 gopkg.in/yaml.v3 v3.0.1 ) @@ -88,10 +88,10 @@ require ( github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect go.uber.org/atomic v1.9.0 // indirect go.uber.org/multierr v1.9.0 // indirect - golang.org/x/crypto v0.31.0 // indirect - golang.org/x/net v0.33.0 // indirect - golang.org/x/sys v0.28.0 // indirect - golang.org/x/tools v0.27.0 // indirect + golang.org/x/crypto v0.32.0 // indirect + golang.org/x/net v0.34.0 // indirect + golang.org/x/sys v0.29.0 // indirect + golang.org/x/tools v0.29.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect ) diff --git a/go.sum b/go.sum index c1956415a..ffce8e369 100644 --- a/go.sum +++ b/go.sum @@ -89,12 +89,12 @@ github.com/jedib0t/go-pretty/v6 v6.6.5 h1:9PgMJOVBedpgYLI56jQRJYqngxYAAzfEUua+3N github.com/jedib0t/go-pretty/v6 v6.6.5/go.mod h1:Uq/HrbhuFty5WSVNfjpQQe47x16RwVGXIveNGEyGtHs= github.com/jfrog/archiver/v3 v3.6.1 h1:LOxnkw9pOn45DzCbZNFV6K0+6dCsQ0L8mR3ZcujO5eI= github.com/jfrog/archiver/v3 v3.6.1/go.mod h1:VgR+3WZS4N+i9FaDwLZbq+jeU4B4zctXL+gL4EMzfLw= -github.com/jfrog/build-info-go v1.10.7 h1:10NVHYg0193gJpQft+S4WQfvYMtj5jlwwhJRvkFJtBE= -github.com/jfrog/build-info-go v1.10.7/go.mod h1:JcISnovFXKx3wWf3p1fcMmlPdt6adxScXvoJN4WXqIE= +github.com/jfrog/build-info-go v1.10.8 h1:8D4wtvKzLS1hzfDWtfH4OliZLtLCgL62tXCnGWDXuac= +github.com/jfrog/build-info-go v1.10.8/go.mod h1:JcISnovFXKx3wWf3p1fcMmlPdt6adxScXvoJN4WXqIE= github.com/jfrog/gofrog v1.7.6 h1:QmfAiRzVyaI7JYGsB7cxfAJePAZTzFz0gRWZSE27c6s= github.com/jfrog/gofrog v1.7.6/go.mod h1:ntr1txqNOZtHplmaNd7rS4f8jpA5Apx8em70oYEe7+4= -github.com/jfrog/jfrog-client-go v1.48.6 h1:Pl9foa9XBaPbP3gz4wevDmF/mpfit94IQHKQKnfk3lA= -github.com/jfrog/jfrog-client-go v1.48.6/go.mod h1:2ySOMva54L3EYYIlCBYBTcTgqfrrQ19gtpA/MWfA/ec= +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/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= @@ -222,15 +222,15 @@ go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -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/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= +golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= +golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 h1:yqrTHse8TCMW1M1ZCP+VAR/l0kKxwaAIqN/il7x4voA= +golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU= golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4= golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= -golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= +golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= +golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= @@ -252,17 +252,17 @@ golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= -golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= +golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= -golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= +golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg= +golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -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.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE= +golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= diff --git a/utils/ioutils/questionnaire.go b/utils/ioutils/questionnaire.go index 74459ac43..40ba1b965 100644 --- a/utils/ioutils/questionnaire.go +++ b/utils/ioutils/questionnaire.go @@ -82,7 +82,7 @@ func interruptKeyBind() prompt.Option { interrupt := prompt.KeyBind{ Key: prompt.ControlC, Fn: func(buf *prompt.Buffer) { - panic("Interrupted") + panic("Operation interrupted. Exiting...") }, } return prompt.OptionAddKeyBind(interrupt) @@ -177,7 +177,7 @@ func validateAnswer(answer string, options []prompt.Suggest, allowVars bool) boo // If the provided answer does not appear in list, confirm the choice. func AskFromListWithMismatchConfirmation(promptPrefix, misMatchMsg string, options []prompt.Suggest) string { for { - answer := prompt.Input(promptPrefix+" ", prefixCompleter(options), interruptKeyBind()) + answer := prompt.Input(promptPrefix+" ", prefixCompleter(options), interruptKeyBind(), prompt.OptionShowCompletionAtStart(), prompt.OptionCompletionOnDown()) if answer == "" { log.Output(EmptyValueMsg) } diff --git a/utils/tests/test_cli.go b/utils/tests/test_cli.go index 8c300f45d..8c4a1cc6c 100644 --- a/utils/tests/test_cli.go +++ b/utils/tests/test_cli.go @@ -52,21 +52,37 @@ func (cli *JfrogCli) RunCliCmdWithOutput(t *testing.T, args ...string) string { return RunCmdWithOutput(t, func() error { return cli.Exec(args...) }) } -// Run a command, redirect the stdout and return the output -func RunCmdWithOutput(t *testing.T, executeCmd func() error) string { +func (cli *JfrogCli) RunCliCmdWithOutputs(t *testing.T, args ...string) (string, error) { + return RunCmdWithOutputs(t, func() error { return cli.Exec(args...) }) +} + +func RunCmdWithOutputs(t *testing.T, executeCmd func() error) (output string, err error) { newStdout, stdWriter, cleanUp := redirectOutToPipe(t) defer cleanUp() + errCh := make(chan error, 1) + go func() { - assert.NoError(t, executeCmd()) + errCh <- executeCmd() // Closing the temp stdout in order to be able to read it's content. assert.NoError(t, stdWriter.Close()) }() - content, err := io.ReadAll(newStdout) + content, e := io.ReadAll(newStdout) + assert.NoError(t, e) + output = string(content) + log.Debug(output) + + err = <-errCh + + return +} + +// Run a command, redirect the stdout and return the output +func RunCmdWithOutput(t *testing.T, executeCmd func() error) string { + output, err := RunCmdWithOutputs(t, executeCmd) assert.NoError(t, err) - log.Debug(string(content)) - return string(content) + return output } func redirectOutToPipe(t *testing.T) (*os.File, *os.File, func()) { diff --git a/utils/usage/visibility_system_manager.go b/utils/usage/visibility_system_manager.go index ac030d5b2..6b23c9aa8 100644 --- a/utils/usage/visibility_system_manager.go +++ b/utils/usage/visibility_system_manager.go @@ -39,7 +39,7 @@ func (vsm *VisibilitySystemManager) createMetric(commandName string) services.Vi } func (vsm *VisibilitySystemManager) SendUsage(commandName string) error { - manager, err := utils.CreateJfConnectServiceManager(vsm.serverDetails, 1, 0) + manager, err := utils.CreateJfConnectServiceManager(vsm.serverDetails, 0, 0) if err != nil { return err }