-
Notifications
You must be signed in to change notification settings - Fork 61
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Package manager login command - Docker, Podman #1304
Changes from all commits
bb36015
6583684
fb20e20
9a20c6c
ed7b39d
633d8a0
74c9a47
e0e5024
f51ae6d
7233563
f8e793a
c53b4c2
c3e0486
e8cc9f8
95ba696
3dff2bb
0f0856e
f280aa5
ecb74bb
cd0fda3
547fa02
265fea7
e8f884c
2ee5950
aec982a
57df296
e8d5842
f84e6f8
8c2b1c6
e6afac1
f6eed54
be5cdcb
1087833
cd0ce91
31b0627
89cdb97
02252d2
76fd3b4
77e8d71
f28f014
0e959a3
01e0f45
95bf72e
c90cbb3
5f37066
1ecb8ed
6e3f8b9
07af542
746d67d
5f8e0aa
43d1a80
3f8ab57
b73d2eb
0d5e16b
bc7f2d4
6e5def8
a89e2f5
e8566f1
3a1bcf0
52894c8
3a0f99b
70015a8
dc79716
4355159
4908ff5
84f28a3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 | ||
} | ||
Comment on lines
+72
to
+80
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This code is unused in the jfrog-cli-core. Is this code intended to be used in other libraries? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. jfrog-cli @yahavi |
||
|
||
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,27 +139,25 @@ 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) | ||
} | ||
if err != nil { | ||
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 <password> | docker login <artifactory-url-without-scheme> -u <username> --password-stdin | ||
// | ||
// For Podman: | ||
// | ||
// echo <password> | podman login <artifactory-url-without-scheme> -u <username> --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, | ||
) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Where is this function used? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. jem @yahavi |
||
for i, projectType := range ProjectTypes { | ||
if projectType == value { | ||
return ProjectType(i) | ||
} | ||
} | ||
return -1 | ||
} | ||
|
||
type MissingResolverErr struct { | ||
message string | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also - what is the reason for the sorting?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
to show the interactive list according to the order I prefer, npm near yarn, docker near podman and etc
@yahavi