Skip to content

Commit 0e85e18

Browse files
committed
Merge remote-tracking branch 'upstream/dev'
2 parents a6d8289 + 506d2fe commit 0e85e18

File tree

20 files changed

+266
-84
lines changed

20 files changed

+266
-84
lines changed

artifactory/commands/container/containermanagercommand.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,11 @@ func (cm *ContainerCommand) PerformLogin(serverDetails *config.ServerDetails, co
4141
}
4242
}
4343
loginConfig := &container.ContainerManagerLoginConfig{ServerDetails: serverDetails}
44-
return container.ContainerManagerLogin(cm.image, loginConfig, containerManagerType)
44+
imageRegistry, err := cm.image.GetRegistry()
45+
if err != nil {
46+
return err
47+
}
48+
return container.ContainerManagerLogin(imageRegistry, loginConfig, containerManagerType)
4549
}
4650
return nil
4751
}

artifactory/commands/packagemanagerlogin/packagemanagerlogin.go

Lines changed: 97 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,50 @@ import (
1010
"github.com/jfrog/jfrog-cli-core/v2/artifactory/commands/repository"
1111
commandsutils "github.com/jfrog/jfrog-cli-core/v2/artifactory/commands/utils"
1212
"github.com/jfrog/jfrog-cli-core/v2/artifactory/utils"
13+
"github.com/jfrog/jfrog-cli-core/v2/artifactory/utils/container"
1314
"github.com/jfrog/jfrog-cli-core/v2/artifactory/utils/npm"
1415
"github.com/jfrog/jfrog-cli-core/v2/artifactory/utils/yarn"
1516
"github.com/jfrog/jfrog-cli-core/v2/common/project"
1617
"github.com/jfrog/jfrog-cli-core/v2/utils/config"
18+
"github.com/jfrog/jfrog-cli-core/v2/utils/coreutils"
1719
"github.com/jfrog/jfrog-client-go/artifactory/services"
1820
"github.com/jfrog/jfrog-client-go/utils/errorutils"
1921
"github.com/jfrog/jfrog-client-go/utils/log"
22+
"golang.org/x/exp/maps"
23+
"net/url"
24+
"slices"
2025
)
2126

27+
// packageManagerToRepositoryPackageType maps project types to corresponding Artifactory repository package types.
28+
var packageManagerToRepositoryPackageType = map[project.ProjectType]string{
29+
// Npm package managers
30+
project.Npm: repository.Npm,
31+
project.Yarn: repository.Npm,
32+
33+
// Python (pypi) package managers
34+
project.Pip: repository.Pypi,
35+
project.Pipenv: repository.Pypi,
36+
project.Poetry: repository.Pypi,
37+
38+
// Nuget package managers
39+
project.Nuget: repository.Nuget,
40+
project.Dotnet: repository.Nuget,
41+
42+
// Docker package managers
43+
project.Docker: repository.Docker,
44+
project.Podman: repository.Docker,
45+
46+
project.Go: repository.Go,
47+
}
48+
2249
// PackageManagerLoginCommand configures registries and authentication for various package manager (npm, Yarn, Pip, Pipenv, Poetry, Go)
2350
type PackageManagerLoginCommand struct {
2451
// packageManager represents the type of package manager (e.g., NPM, Yarn).
2552
packageManager project.ProjectType
2653
// repoName is the name of the repository used for configuration.
2754
repoName string
55+
// projectKey is the JFrog Project key in JFrog Platform.
56+
projectKey string
2857
// serverDetails contains Artifactory server configuration.
2958
serverDetails *config.ServerDetails
3059
// commandName specifies the command for this instance.
@@ -40,20 +69,19 @@ func NewPackageManagerLoginCommand(packageManager project.ProjectType) *PackageM
4069
}
4170
}
4271

43-
// packageManagerToPackageType maps project types to corresponding Artifactory package types (e.g., npm, pypi).
44-
func packageManagerToPackageType(packageManager project.ProjectType) (string, error) {
45-
switch packageManager {
46-
case project.Npm, project.Yarn:
47-
return repository.Npm, nil
48-
case project.Pip, project.Pipenv, project.Poetry:
49-
return repository.Pypi, nil
50-
case project.Go:
51-
return repository.Go, nil
52-
case project.Nuget, project.Dotnet:
53-
return repository.Nuget, nil
54-
default:
55-
return "", errorutils.CheckErrorf("unsupported package manager: %s", packageManager)
56-
}
72+
// GetSupportedPackageManagersList returns a sorted list of supported package managers.
73+
func GetSupportedPackageManagersList() []project.ProjectType {
74+
allSupportedPackageManagers := maps.Keys(packageManagerToRepositoryPackageType)
75+
// Sort keys based on their natural enum order
76+
slices.SortFunc(allSupportedPackageManagers, func(a, b project.ProjectType) int {
77+
return int(a) - int(b)
78+
})
79+
return allSupportedPackageManagers
80+
}
81+
82+
func IsSupportedPackageManager(packageManager project.ProjectType) bool {
83+
_, exists := packageManagerToRepositoryPackageType[packageManager]
84+
return exists
5785
}
5886

5987
// CommandName returns the name of the login command.
@@ -72,8 +100,24 @@ func (pmlc *PackageManagerLoginCommand) ServerDetails() (*config.ServerDetails,
72100
return pmlc.serverDetails, nil
73101
}
74102

103+
// SetRepoName assigns the repository name to the command.
104+
func (pmlc *PackageManagerLoginCommand) SetRepoName(repoName string) *PackageManagerLoginCommand {
105+
pmlc.repoName = repoName
106+
return pmlc
107+
}
108+
109+
// SetProjectKey assigns the project key to the command.
110+
func (pmlc *PackageManagerLoginCommand) SetProjectKey(projectKey string) *PackageManagerLoginCommand {
111+
pmlc.projectKey = projectKey
112+
return pmlc
113+
}
114+
75115
// Run executes the configuration method corresponding to the package manager specified for the command.
76116
func (pmlc *PackageManagerLoginCommand) Run() (err error) {
117+
if !IsSupportedPackageManager(pmlc.packageManager) {
118+
return errorutils.CheckErrorf("unsupported package manager: %s", pmlc.packageManager)
119+
}
120+
77121
if pmlc.repoName == "" {
78122
// Prompt the user to select a virtual repository that matches the package manager.
79123
if err = pmlc.promptUserToSelectRepository(); err != nil {
@@ -95,27 +139,25 @@ func (pmlc *PackageManagerLoginCommand) Run() (err error) {
95139
err = pmlc.configureGo()
96140
case project.Nuget, project.Dotnet:
97141
err = pmlc.configureDotnetNuget()
142+
case project.Docker, project.Podman:
143+
err = pmlc.configureContainer()
98144
default:
99145
err = errorutils.CheckErrorf("unsupported package manager: %s", pmlc.packageManager)
100146
}
101147
if err != nil {
102148
return fmt.Errorf("failed to configure %s: %w", pmlc.packageManager.String(), err)
103149
}
104150

105-
log.Info(fmt.Sprintf("Successfully configured %s to use JFrog Artifactory repository '%s'.", pmlc.packageManager.String(), pmlc.repoName))
151+
log.Output(fmt.Sprintf("Successfully configured %s to use JFrog Artifactory repository '%s'.", coreutils.PrintBoldTitle(pmlc.packageManager.String()), coreutils.PrintBoldTitle(pmlc.repoName)))
106152
return nil
107153
}
108154

109155
// promptUserToSelectRepository prompts the user to select a compatible virtual repository.
110-
func (pmlc *PackageManagerLoginCommand) promptUserToSelectRepository() error {
111-
// Map the package manager to its corresponding package type.
112-
packageType, err := packageManagerToPackageType(pmlc.packageManager)
113-
if err != nil {
114-
return err
115-
}
156+
func (pmlc *PackageManagerLoginCommand) promptUserToSelectRepository() (err error) {
116157
repoFilterParams := services.RepositoriesFilterParams{
117158
RepoType: utils.Virtual.String(),
118-
PackageType: packageType,
159+
PackageType: packageManagerToRepositoryPackageType[pmlc.packageManager],
160+
ProjectKey: pmlc.projectKey,
119161
}
120162

121163
// Prompt for repository selection based on filter parameters.
@@ -239,3 +281,36 @@ func (pmlc *PackageManagerLoginCommand) configureDotnetNuget() error {
239281
// Add the repository as a source in the NuGet configuration with credentials for authentication.
240282
return dotnet.AddSourceToNugetConfig(toolchainType, sourceUrl, user, password)
241283
}
284+
285+
// configureContainer configures container managers like Docker or Podman to authenticate with JFrog Artifactory.
286+
// It performs a login using the container manager's CLI command.
287+
//
288+
// For Docker:
289+
//
290+
// echo <password> | docker login <artifactory-url-without-scheme> -u <username> --password-stdin
291+
//
292+
// For Podman:
293+
//
294+
// echo <password> | podman login <artifactory-url-without-scheme> -u <username> --password-stdin
295+
func (pmlc *PackageManagerLoginCommand) configureContainer() error {
296+
var containerManagerType container.ContainerManagerType
297+
switch pmlc.packageManager {
298+
case project.Docker:
299+
containerManagerType = container.DockerClient
300+
case project.Podman:
301+
containerManagerType = container.Podman
302+
default:
303+
return errorutils.CheckErrorf("unsupported container manager: %s", pmlc.packageManager)
304+
}
305+
// Parse the URL to remove the scheme (https:// or http://)
306+
parsedPlatformURL, err := url.Parse(pmlc.serverDetails.GetUrl())
307+
if err != nil {
308+
return err
309+
}
310+
urlWithoutScheme := parsedPlatformURL.Host + parsedPlatformURL.Path
311+
return container.ContainerManagerLogin(
312+
urlWithoutScheme,
313+
&container.ContainerManagerLoginConfig{ServerDetails: pmlc.serverDetails},
314+
containerManagerType,
315+
)
316+
}

artifactory/commands/packagemanagerlogin/packagemanagerlogin_test.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
clientTestUtils "github.com/jfrog/jfrog-client-go/utils/tests"
1414
"github.com/stretchr/testify/assert"
1515
"github.com/stretchr/testify/require"
16+
"golang.org/x/exp/slices"
1617
"os"
1718
"os/exec"
1819
"path/filepath"
@@ -52,6 +53,13 @@ func createTestPackageManagerLoginCommand(packageManager project.ProjectType) *P
5253
return cmd
5354
}
5455

56+
func TestPackageManagerLoginCommand_NotSupported(t *testing.T) {
57+
notSupportedLoginCmd := createTestPackageManagerLoginCommand(project.Cocoapods)
58+
err := notSupportedLoginCmd.Run()
59+
assert.Error(t, err)
60+
assert.ErrorContains(t, err, "unsupported package manager")
61+
}
62+
5563
func TestPackageManagerLoginCommand_Npm(t *testing.T) {
5664
// Create a temporary directory to act as the environment's npmrc file location.
5765
tempDir := t.TempDir()
@@ -381,3 +389,20 @@ func testBuildToolLoginCommandConfigureDotnetNuget(t *testing.T, packageManager
381389
})
382390
}
383391
}
392+
393+
func TestGetSupportedPackageManagersList(t *testing.T) {
394+
result := GetSupportedPackageManagersList()
395+
// Check that Go is before Pip, and Pip is before Npm using GreaterOrEqual
396+
assert.GreaterOrEqual(t, slices.Index(result, project.Pip), slices.Index(result, project.Go), "Go should come before Pip")
397+
assert.GreaterOrEqual(t, slices.Index(result, project.Npm), slices.Index(result, project.Pip), "Pip should come before Npm")
398+
}
399+
400+
func TestIsSupportedPackageManager(t *testing.T) {
401+
// Test valid package managers
402+
for pm := range packageManagerToRepositoryPackageType {
403+
assert.True(t, IsSupportedPackageManager(pm), "Package manager %s should be supported", pm)
404+
}
405+
406+
// Test unsupported package manager
407+
assert.False(t, IsSupportedPackageManager(project.Cocoapods), "Package manager Cocoapods should not be supported")
408+
}

artifactory/utils/commandsummary/utils.go

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,26 +49,38 @@ const (
4949
buildInfoSection summarySection = "buildInfo"
5050
)
5151

52-
// addGitHubTrackingToUrl adds GitHub-related query parameters to a given URL if the GITHUB_WORKFLOW environment variable is set.
52+
const (
53+
// The source of the request
54+
sourceParamKey = "s"
55+
githubSourceValue = "1"
56+
// The metric to track
57+
metricParamKey = "m"
58+
githubMetricValue = "3"
59+
60+
jobIDKey = "gh_job_id"
61+
sectionKey = "gh_section"
62+
workflowEnvKey = "GITHUB_WORKFLOW"
63+
)
64+
5365
func addGitHubTrackingToUrl(urlStr string, section summarySection) (string, error) {
5466
// Check if GITHUB_WORKFLOW environment variable is set
55-
githubWorkflow := os.Getenv("GITHUB_WORKFLOW")
67+
githubWorkflow := os.Getenv(workflowEnvKey)
5668
if githubWorkflow == "" {
57-
// Return the original URL if the variable is not set
5869
return urlStr, nil
5970
}
6071

6172
// Parse the input URL
6273
parsedUrl, err := url.Parse(urlStr)
6374
if errorutils.CheckError(err) != nil {
64-
// Return an error if the URL is invalid
6575
return "", err
6676
}
6777

6878
// Get the query parameters and add the GitHub tracking parameters
6979
queryParams := parsedUrl.Query()
70-
queryParams.Set("gh_job_id", githubWorkflow)
71-
queryParams.Set("gh_section", string(section))
80+
queryParams.Set(sourceParamKey, githubSourceValue)
81+
queryParams.Set(metricParamKey, githubMetricValue)
82+
queryParams.Set(jobIDKey, githubWorkflow)
83+
queryParams.Set(sectionKey, string(section))
7284
parsedUrl.RawQuery = queryParams.Encode()
7385

7486
// Return the modified URL

artifactory/utils/commandsummary/utils_test.go

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@ func TestGenerateArtifactUrl(t *testing.T) {
1919
majorVersion int
2020
expected string
2121
}{
22-
{"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"},
23-
{"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"},
24-
{"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"},
22+
{"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"},
23+
{"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"},
24+
{"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"},
2525
}
2626
StaticMarkdownConfig.setPlatformUrl(testPlatformUrl)
2727
for _, testCase := range cases {
@@ -71,7 +71,7 @@ func TestAddGitHubTrackingToUrl(t *testing.T) {
7171
"https://example.com/path",
7272
buildInfoSection,
7373
"workflow123",
74-
"https://example.com/path?gh_job_id=workflow123&gh_section=buildInfo",
74+
"https://example.com/path?gh_job_id=workflow123&gh_section=buildInfo&m=3&s=1",
7575
false,
7676
},
7777
{
@@ -87,15 +87,23 @@ func TestAddGitHubTrackingToUrl(t *testing.T) {
8787
"https://example.com/path?existing_param=value",
8888
packagesSection,
8989
"workflow123",
90-
"https://example.com/path?existing_param=value&gh_job_id=workflow123&gh_section=packages",
90+
"https://example.com/path?existing_param=value&gh_job_id=workflow123&gh_section=packages&m=3&s=1",
9191
false,
9292
},
9393
{
9494
"GITHUB_WORKFLOW with special characters",
9595
"https://example.com/path",
9696
artifactsSection,
9797
"workflow with spaces & special?characters",
98-
"https://example.com/path?gh_job_id=workflow+with+spaces+%26+special%3Fcharacters&gh_section=artifacts",
98+
"https://example.com/path?gh_job_id=workflow+with+spaces+%26+special%3Fcharacters&gh_section=artifacts&m=3&s=1",
99+
false,
100+
},
101+
{
102+
"URL with spaces",
103+
"https://example.com/path?existing_param=value",
104+
packagesSection,
105+
"workflow space",
106+
"https://example.com/path?existing_param=value&gh_job_id=workflow+space&gh_section=packages&m=3&s=1",
99107
false,
100108
},
101109
}

artifactory/utils/container/containermanager.go

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -189,11 +189,7 @@ func (loginCmd *LoginCmd) RunCmd() error {
189189

190190
// First we'll try to log in assuming a proxy-less tag (e.g. "registry-address/docker-repo/image:ver").
191191
// If fails, we will try assuming a reverse proxy tag (e.g. "registry-address-docker-repo/image:ver").
192-
func ContainerManagerLogin(image *Image, config *ContainerManagerLoginConfig, containerManager ContainerManagerType) error {
193-
imageRegistry, err := image.GetRegistry()
194-
if err != nil {
195-
return err
196-
}
192+
func ContainerManagerLogin(imageRegistry string, config *ContainerManagerLoginConfig, containerManager ContainerManagerType) error {
197193
username := config.ServerDetails.User
198194
password := config.ServerDetails.Password
199195
// If access-token exists, perform login with it.
@@ -206,7 +202,7 @@ func ContainerManagerLogin(image *Image, config *ContainerManagerLoginConfig, co
206202
}
207203
// Perform login.
208204
cmd := &LoginCmd{DockerRegistry: imageRegistry, Username: username, Password: password, containerManager: containerManager}
209-
err = cmd.RunCmd()
205+
err := cmd.RunCmd()
210206
if exitCode := coreutils.GetExitCode(err, 0, 0, false); exitCode == coreutils.ExitCodeNoError {
211207
// Login succeeded
212208
return nil

artifactory/utils/repositoryutils.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package utils
22

33
import (
4+
"fmt"
45
"github.com/jfrog/gofrog/datastructures"
56
"github.com/jfrog/jfrog-cli-core/v2/utils/config"
67
"github.com/jfrog/jfrog-cli-core/v2/utils/ioutils"
@@ -101,7 +102,7 @@ func SelectRepositoryInteractively(serverDetails *config.ServerDetails, repoFilt
101102
return filteredRepos[0], nil
102103
}
103104
// Prompt the user to select a repository.
104-
return ioutils.AskFromListWithMismatchConfirmation("Please select a repository to login to:", "Repository not found.", ioutils.ConvertToSuggests(filteredRepos)), nil
105+
return ioutils.AskFromListWithMismatchConfirmation(fmt.Sprintf("Please select a %s %s repository to configure:", repoFilterParams.RepoType, repoFilterParams.PackageType), "Repository not found.", ioutils.ConvertToSuggests(filteredRepos)), nil
105106
}
106107

107108
// GetFilteredRepositoriesWithFilterParams returns the names of local, remote, virtual, and federated repositories filtered by their names and type.
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11

22

3-
| Build Info| Security Violations| Security Issues|
4-
|:---------|:------------|:------------|
5-
| [buildName 123](http://myJFrogPlatform/builds/buildName/123?gh_job_id=JFrog+CLI+Core+Tests&gh_section=buildInfo) | Not scanned| Not scanned|
3+
| Build Info | Security Violations | Security Issues |
4+
| :--------- | :------------ | :------------ |
5+
| [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 |
66

77

artifactory/utils/testdata/command_summaries/extended/docker-image-module.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,4 @@
1010

1111
| Artifacts | Security Violations | Security Issues |
1212
| :------------ | :--------------------- | :------------------ |
13-
| <pre>📦 docker-local<br>└── 📁 image2<br> └── 📁 sha256:552c<br> └── <a href='https://myplatform.com/ui/repos/tree/General/docker-local/image2/sha256:552c/sha256__aae9?clearFilter=true&gh_job_id=JFrog+CLI+Core+Tests&gh_section=packages' target="_blank">sha256__aae9</a><br><br></pre> | Not scanned | Not scanned |
13+
| <pre>📦 docker-local<br>└── 📁 image2<br> └── 📁 sha256:552c<br> └── <a href='https://myplatform.com/ui/repos/tree/General/docker-local/image2/sha256:552c/sha256__aae9?clearFilter=true&gh_job_id=JFrog+CLI+Core+Tests&gh_section=packages&m=3&s=1' target="_blank">sha256__aae9</a><br><br></pre> | Not scanned | Not scanned |

artifactory/utils/testdata/command_summaries/extended/generic-module.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
<pre>📦 generic-local
1212
└── 📁 path
1313
└── 📁 to
14-
└── <a href='https://myplatform.com/ui/repos/tree/General/generic-local/path/to/artifact2?clearFilter=true&gh_job_id=JFrog+CLI+Core+Tests&gh_section=artifacts' target="_blank">artifact2</a>
14+
└── <a href='https://myplatform.com/ui/repos/tree/General/generic-local/path/to/artifact2?clearFilter=true&gh_job_id=JFrog+CLI+Core+Tests&gh_section=artifacts&m=3&s=1' target="_blank">artifact2</a>
1515

1616
</pre>
1717

0 commit comments

Comments
 (0)