diff --git a/.github/workflows/analysis.yml b/.github/workflows/analysis.yml index 5ca78123e..23a98a6e9 100644 --- a/.github/workflows/analysis.yml +++ b/.github/workflows/analysis.yml @@ -37,6 +37,7 @@ jobs: go-version: 1.20.x - name: Run Gosec Security Scanner - uses: securego/gosec@master + # Temporarily set version 2.18.0 to workaround https://github.com/securego/gosec/issues/1046 + uses: securego/gosec@v2.18.0 with: args: -exclude G204,G301,G302,G304,G306 -tests -exclude-dir \.*test\.* ./... diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4f356231f..a3026f842 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -21,7 +21,7 @@ jobs: - uses: actions/checkout@v4 - name: Setup Python3 - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: "3.x" diff --git a/artifactory/commands/golang/go.go b/artifactory/commands/golang/go.go index 87627aa31..b6950c50a 100644 --- a/artifactory/commands/golang/go.go +++ b/artifactory/commands/golang/go.go @@ -18,6 +18,7 @@ import ( "github.com/jfrog/jfrog-client-go/utils/io/fileutils" "github.com/jfrog/jfrog-client-go/utils/log" "net/http" + "os" "path" "path/filepath" "strings" @@ -328,3 +329,20 @@ func buildPackageVersionRequest(name, branchName string) string { // No version was given to "go get" command, so the latest version should be requested return path.Join(packageVersionRequest, "latest.info") } + +func SetArtifactoryAsResolutionServer(serverDetails *config.ServerDetails, depsRepo string) (err error) { + err = setGoProxy(serverDetails, depsRepo) + if err != nil { + err = fmt.Errorf("failed while setting Artifactory as a dependencies resolution registry: %s", err.Error()) + } + return +} + +func setGoProxy(server *config.ServerDetails, remoteGoRepo string) error { + repoUrl, err := goutils.GetArtifactoryRemoteRepoUrl(server, remoteGoRepo) + if err != nil { + return err + } + repoUrl += "|direct" + return os.Setenv("GOPROXY", repoUrl) +} diff --git a/artifactory/commands/golang/go_test.go b/artifactory/commands/golang/go_test.go index 974808889..ed899aad3 100644 --- a/artifactory/commands/golang/go_test.go +++ b/artifactory/commands/golang/go_test.go @@ -1,11 +1,14 @@ package golang import ( + "fmt" + "github.com/jfrog/jfrog-cli-core/v2/utils/config" goutils "github.com/jfrog/jfrog-cli-core/v2/utils/golang" testsutils "github.com/jfrog/jfrog-client-go/utils/tests" "github.com/stretchr/testify/assert" "os" "path/filepath" + "strings" "testing" ) @@ -42,3 +45,24 @@ func TestGetPackageFilesPath(t *testing.T) { assert.NoError(t, err) assert.Equal(t, expectedPackagePath, actualPackagePath) } + +func TestSetArtifactoryAsResolutionServer(t *testing.T) { + server := &config.ServerDetails{ + Url: "http://localhost:8080/", + ArtifactoryUrl: "http://localhost:8080/artifactory/", + User: "myUser", + Password: "myPassword", + ServerId: "myServer", + } + repo := "myRepo" + + // Setting the GOPROXY value to "" to ensure that the new value set in SetArtifactoryAsResolutionServer is correctly validated. + cleanup := testsutils.SetEnvWithCallbackAndAssert(t, "GOPROXY", "") + defer cleanup() + + assert.NoError(t, SetArtifactoryAsResolutionServer(server, repo)) + + serverUrlWithoutHttp := strings.TrimPrefix(server.ArtifactoryUrl, "http://") + expectedGoProxy := fmt.Sprintf("http://%s:%s@%sapi/go/%s|direct", server.User, server.Password, serverUrlWithoutHttp, repo) + assert.Equal(t, expectedGoProxy, os.Getenv("GOPROXY")) +} diff --git a/artifactory/commands/repository/repository.go b/artifactory/commands/repository/repository.go index d6c77206f..05e407872 100644 --- a/artifactory/commands/repository/repository.go +++ b/artifactory/commands/repository/repository.go @@ -116,7 +116,7 @@ var writersMap = map[string]ioutils.AnswerWriter{ YumRootDepth: ioutils.WriteIntAnswer, DockerApiVersion: ioutils.WriteStringAnswer, EnableFileListsIndexing: ioutils.WriteBoolAnswer, - OptionalIndexCompressionFormats: ioutils.WriteStringAnswer, + OptionalIndexCompressionFormats: ioutils.WriteStringArrayAnswer, Username: ioutils.WriteStringAnswer, Password: ioutils.WriteStringAnswer, Proxy: ioutils.WriteStringAnswer, @@ -239,6 +239,8 @@ var localRepoHandlers = map[string]repoHandler{ Alpine: localAlpineHandler, Generic: localGenericHandler, Swift: localSwiftHandler, + Terraform: localTerraformHandler, + Cargo: localCargoHandler, } func localMavenHandler(servicesManager artifactory.ArtifactoryServicesManager, jsonConfig []byte, isUpdate bool) error { @@ -620,6 +622,36 @@ func localSwiftHandler(servicesManager artifactory.ArtifactoryServicesManager, j return err } +func localTerraformHandler(servicesManager artifactory.ArtifactoryServicesManager, jsonConfig []byte, isUpdate bool) error { + params := services.NewTerraformLocalRepositoryParams() + err := json.Unmarshal(jsonConfig, ¶ms) + if errorutils.CheckError(err) != nil { + return err + } + + if isUpdate { + err = servicesManager.UpdateLocalRepository().Terraform(params) + } else { + err = servicesManager.CreateLocalRepository().Terraform(params) + } + return err +} + +func localCargoHandler(servicesManager artifactory.ArtifactoryServicesManager, jsonConfig []byte, isUpdate bool) error { + params := services.NewCargoLocalRepositoryParams() + err := json.Unmarshal(jsonConfig, ¶ms) + if errorutils.CheckError(err) != nil { + return err + } + + if isUpdate { + err = servicesManager.UpdateLocalRepository().Cargo(params) + } else { + err = servicesManager.CreateLocalRepository().Cargo(params) + } + return err +} + func localGenericHandler(servicesManager artifactory.ArtifactoryServicesManager, jsonConfig []byte, isUpdate bool) error { params := services.NewGenericLocalRepositoryParams() err := json.Unmarshal(jsonConfig, ¶ms) @@ -665,6 +697,8 @@ var remoteRepoHandlers = map[string]repoHandler{ Alpine: remoteAlpineHandler, Generic: remoteGenericHandler, Swift: remoteSwiftHandler, + Terraform: remoteTerraformHandler, + Cargo: remoteCargoHandler, } func remoteMavenHandler(servicesManager artifactory.ArtifactoryServicesManager, jsonConfig []byte, isUpdate bool) error { @@ -1059,6 +1093,34 @@ func remoteSwiftHandler(servicesManager artifactory.ArtifactoryServicesManager, return err } +func remoteCargoHandler(servicesManager artifactory.ArtifactoryServicesManager, jsonConfig []byte, isUpdate bool) error { + params := services.NewCargoRemoteRepositoryParams() + err := json.Unmarshal(jsonConfig, ¶ms) + if errorutils.CheckError(err) != nil { + return err + } + if isUpdate { + err = servicesManager.UpdateRemoteRepository().Cargo(params) + } else { + err = servicesManager.CreateRemoteRepository().Cargo(params) + } + return err +} + +func remoteTerraformHandler(servicesManager artifactory.ArtifactoryServicesManager, jsonConfig []byte, isUpdate bool) error { + params := services.NewTerraformRemoteRepositoryParams() + err := json.Unmarshal(jsonConfig, ¶ms) + if errorutils.CheckError(err) != nil { + return err + } + if isUpdate { + err = servicesManager.UpdateRemoteRepository().Terraform(params) + } else { + err = servicesManager.CreateRemoteRepository().Terraform(params) + } + return err +} + func remoteGenericHandler(servicesManager artifactory.ArtifactoryServicesManager, jsonConfig []byte, isUpdate bool) error { params := services.NewGenericRemoteRepositoryParams() err := json.Unmarshal(jsonConfig, ¶ms) @@ -1102,6 +1164,8 @@ var federatedRepoHandlers = map[string]repoHandler{ Generic: federatedGenericHandler, Yum: federatedYumHandler, Swift: federatedSwiftHandler, + Terraform: federatedTerraformHandler, + Cargo: federatedCargoHandler, } func federatedMavenHandler(servicesManager artifactory.ArtifactoryServicesManager, jsonConfig []byte, isUpdate bool) error { @@ -1430,6 +1494,30 @@ func federatedSwiftHandler(servicesManager artifactory.ArtifactoryServicesManage return servicesManager.CreateFederatedRepository().Swift(params) } +func federatedTerraformHandler(servicesManager artifactory.ArtifactoryServicesManager, jsonConfig []byte, isUpdate bool) error { + params := services.NewTerraformFederatedRepositoryParams() + err := json.Unmarshal(jsonConfig, ¶ms) + if errorutils.CheckError(err) != nil { + return err + } + if isUpdate { + return servicesManager.UpdateFederatedRepository().Terraform(params) + } + return servicesManager.CreateFederatedRepository().Terraform(params) +} + +func federatedCargoHandler(servicesManager artifactory.ArtifactoryServicesManager, jsonConfig []byte, isUpdate bool) error { + params := services.NewCargoFederatedRepositoryParams() + err := json.Unmarshal(jsonConfig, ¶ms) + if errorutils.CheckError(err) != nil { + return err + } + if isUpdate { + return servicesManager.UpdateFederatedRepository().Cargo(params) + } + return servicesManager.CreateFederatedRepository().Cargo(params) +} + func federatedYumHandler(servicesManager artifactory.ArtifactoryServicesManager, jsonConfig []byte, isUpdate bool) error { params := services.NewYumFederatedRepositoryParams() err := json.Unmarshal(jsonConfig, ¶ms) @@ -1443,31 +1531,32 @@ func federatedYumHandler(servicesManager artifactory.ArtifactoryServicesManager, } var virtualRepoHandlers = map[string]repoHandler{ - Maven: virtualMavenHandler, - Gradle: virtualGradleHandler, - Ivy: virtualIvyHandler, - Sbt: virtualSbtHandler, - Helm: virtualHelmHandler, - Rpm: virtualRpmHandler, - Nuget: virtualNugetHandler, - Cran: virtualCranHandler, - Gems: virtualGemsHandler, - Npm: virtualNpmHandler, - Bower: virtualBowerHandler, - Debian: virtualDebianHandler, - Pypi: virtualPypiHandler, - Docker: virtualDockerHandler, - Gitlfs: virtualGitLfsHandler, - Go: virtualGoHandler, - Yum: virtualYumHandler, - Conan: virtualConanHandler, - Chef: virtualChefHandler, - Puppet: virtualPuppetHandler, - Conda: virtualCondaHandler, - P2: virtualP2Handler, - Alpine: virtualAlpineHandler, - Generic: virtualGenericHandler, - Swift: virtualSwiftHandler, + Maven: virtualMavenHandler, + Gradle: virtualGradleHandler, + Ivy: virtualIvyHandler, + Sbt: virtualSbtHandler, + Helm: virtualHelmHandler, + Rpm: virtualRpmHandler, + Nuget: virtualNugetHandler, + Cran: virtualCranHandler, + Gems: virtualGemsHandler, + Npm: virtualNpmHandler, + Bower: virtualBowerHandler, + Debian: virtualDebianHandler, + Pypi: virtualPypiHandler, + Docker: virtualDockerHandler, + Gitlfs: virtualGitLfsHandler, + Go: virtualGoHandler, + Yum: virtualYumHandler, + Conan: virtualConanHandler, + Chef: virtualChefHandler, + Puppet: virtualPuppetHandler, + Conda: virtualCondaHandler, + P2: virtualP2Handler, + Alpine: virtualAlpineHandler, + Generic: virtualGenericHandler, + Swift: virtualSwiftHandler, + Terraform: virtualTerraformHandler, } func virtualMavenHandler(servicesManager artifactory.ArtifactoryServicesManager, jsonConfig []byte, isUpdate bool) error { @@ -1806,6 +1895,20 @@ func virtualSwiftHandler(servicesManager artifactory.ArtifactoryServicesManager, return err } +func virtualTerraformHandler(servicesManager artifactory.ArtifactoryServicesManager, jsonConfig []byte, isUpdate bool) error { + params := services.NewTerraformVirtualRepositoryParams() + err := json.Unmarshal(jsonConfig, ¶ms) + if errorutils.CheckError(err) != nil { + return err + } + if isUpdate { + err = servicesManager.UpdateVirtualRepository().Terraform(params) + } else { + err = servicesManager.CreateVirtualRepository().Terraform(params) + } + return err +} + func virtualGenericHandler(servicesManager artifactory.ArtifactoryServicesManager, jsonConfig []byte, isUpdate bool) error { params := services.NewGenericVirtualRepositoryParams() err := json.Unmarshal(jsonConfig, ¶ms) diff --git a/artifactory/commands/repository/template.go b/artifactory/commands/repository/template.go index b19e59c9d..f046b1041 100644 --- a/artifactory/commands/repository/template.go +++ b/artifactory/commands/repository/template.go @@ -53,23 +53,24 @@ const ( BlockPushingSchema1 = "blockPushingSchema1" // Mutual local and virtual repository configuration JSON keys - DebianTrivialLayout = "debianTrivialLayout" + DebianTrivialLayout = "debianTrivialLayout" + OptionalIndexCompressionFormats = "optionalIndexCompressionFormats" + PrimaryKeyPairRef = "primaryKeyPairRef" // Mutual remote and virtual repository configuration JSON keys ExternalDependenciesEnabled = "externalDependenciesEnabled" ExternalDependenciesPatterns = "externalDependenciesPatterns" // Unique local repository configuration JSON keys - ChecksumPolicyType = "checksumPolicyType" - MaxUniqueTags = "maxUniqueTags" - SnapshotVersionBehavior = "snapshotVersionBehavior" - ArchiveBrowsingEnabled = "archiveBrowsingEnabled" - CalculateYumMetadata = "calculateYumMetadata" - YumRootDepth = "yumRootDepth" - DockerApiVersion = "dockerApiVersion" - EnableFileListsIndexing = "enableFileListsIndexing" - OptionalIndexCompressionFormats = "optionalIndexCompressionFormats" - ForceNugetAuthentication = "forceNugetAuthentication" + ChecksumPolicyType = "checksumPolicyType" + MaxUniqueTags = "maxUniqueTags" + SnapshotVersionBehavior = "snapshotVersionBehavior" + ArchiveBrowsingEnabled = "archiveBrowsingEnabled" + CalculateYumMetadata = "calculateYumMetadata" + YumRootDepth = "yumRootDepth" + DockerApiVersion = "dockerApiVersion" + EnableFileListsIndexing = "enableFileListsIndexing" + ForceNugetAuthentication = "forceNugetAuthentication" // Unique remote repository configuration JSON keys Url = "url" @@ -158,6 +159,8 @@ const ( Conda = "conda" P2 = "p2" Swift = "swift" + Terraform = "terraform" + Cargo = "cargo" // Repo layout Refs BowerDefaultRepoLayout = "bower-default" @@ -251,6 +254,7 @@ var optionalSuggestsMap = map[string]prompt.Suggest{ Username: {Text: Username}, Password: {Text: Password}, Proxy: {Text: Proxy}, + PrimaryKeyPairRef: {Text: PrimaryKeyPairRef}, RemoteRepoChecksumPolicyType: {Text: RemoteRepoChecksumPolicyType}, HardFail: {Text: HardFail}, Offline: {Text: Offline}, @@ -306,7 +310,7 @@ var mavenGradleLocalRepoConfKeys = []string{ } var rpmLocalRepoConfKeys = []string{ - YumRootDepth, CalculateYumMetadata, EnableFileListsIndexing, + YumRootDepth, CalculateYumMetadata, EnableFileListsIndexing, PrimaryKeyPairRef, } var nugetLocalRepoConfKeys = []string{ @@ -314,7 +318,7 @@ var nugetLocalRepoConfKeys = []string{ } var debianLocalRepoConfKeys = []string{ - DebianTrivialLayout, + DebianTrivialLayout, PrimaryKeyPairRef, } var dockerLocalRepoConfKeys = []string{ @@ -388,7 +392,7 @@ var vcsRemoteRepoConfKeys = []string{ var baseVirtualRepoConfKeys = []string{ Repositories, Description, Notes, IncludePatterns, ExcludePatterns, RepoLayoutRef, ProjectKey, Environment, ArtifactoryRequestsCanRetrieveRemoteArtifacts, - DefaultDeploymentRepo, + DefaultDeploymentRepo, OptionalIndexCompressionFormats, PrimaryKeyPairRef, } var mavenGradleVirtualRepoConfKeys = []string{ @@ -851,11 +855,12 @@ var questionMap = map[string]ioutils.QuestionInfo{ Msg: "Enter a comma separated list of values from " + strings.Join([]string{Bz2Compression, LzmaCompression, XzCompression}, ","), Options: nil, AllowVars: true, - Writer: ioutils.WriteStringArrayAnswer, + Writer: ioutils.WriteStringAnswer, }, - Username: ioutils.FreeStringQuestionInfo, - Password: ioutils.FreeStringQuestionInfo, - Proxy: ioutils.FreeStringQuestionInfo, + PrimaryKeyPairRef: ioutils.FreeStringQuestionInfo, + Username: ioutils.FreeStringQuestionInfo, + Password: ioutils.FreeStringQuestionInfo, + Proxy: ioutils.FreeStringQuestionInfo, RemoteRepoChecksumPolicyType: { Options: []prompt.Suggest{ {Text: GenerateIfAbsentPolicy}, diff --git a/artifactory/utils/repositoryutils.go b/artifactory/utils/repositoryutils.go index 20efbdf7d..db8a46e69 100644 --- a/artifactory/utils/repositoryutils.go +++ b/artifactory/utils/repositoryutils.go @@ -65,16 +65,13 @@ func GetRepositories(artDetails *config.ServerDetails, repoTypes ...RepoType) ([ if err != nil { return nil, err } - if len(repoTypes) == 0 { - return GetFilteredRepositoriesByName(sm, nil, nil) - } repos := []string{} for _, repoType := range repoTypes { - repoKey, err := GetFilteredRepositoriesByNameAndType(sm, nil, nil, repoType) + filteredRepos, err := GetFilteredRepositoriesByNameAndType(sm, nil, nil, repoType) if err != nil { return repos, err } - repos = append(repos, repoKey...) + repos = append(repos, filteredRepos...) } return repos, nil diff --git a/common/commands/configfile.go b/common/commands/configfile.go index be265e761..a7e4518ba 100644 --- a/common/commands/configfile.go +++ b/common/commands/configfile.go @@ -152,27 +152,23 @@ func handleInteractiveConfigCreation(configFile *ConfigFile, confType project.Pr } switch confType { case project.Go: - err = configFile.configGo() - case project.Pip: - err = configFile.configPip() - case project.Pipenv: - err = configFile.configPipenv() - case project.Poetry: - err = configFile.configPoetry() + return configFile.setDeployerResolver() + case project.Pip, project.Pipenv, project.Poetry: + return configFile.setResolver(false) case project.Yarn: - err = configFile.configYarn() + return configFile.setResolver(false) case project.Npm: - err = configFile.configNpm() + return configFile.setDeployerResolver() case project.Nuget, project.Dotnet: - err = configFile.configDotnet() + return configFile.configDotnet() case project.Maven: - err = configFile.configMaven() + return configFile.configMaven() case project.Gradle: - err = configFile.configGradle() + return configFile.configGradle() case project.Terraform: - err = configFile.configTerraform() + return configFile.setResolver(false) } - return errorutils.CheckError(err) + return } func writeConfigFile(configFile *ConfigFile, destination string) (err error) { @@ -394,32 +390,8 @@ func (configFile *ConfigFile) VerifyConfigFile(configFilePath string) error { return errorutils.CheckError(os.Remove(configFilePath)) } -func (configFile *ConfigFile) configGo() error { - return configFile.setDeployerResolver() -} - -func (configFile *ConfigFile) configPip() error { - return configFile.setResolver() -} - -func (configFile *ConfigFile) configPipenv() error { - return configFile.setResolver() -} - -func (configFile *ConfigFile) configPoetry() error { - return configFile.setResolver() -} - -func (configFile *ConfigFile) configYarn() error { - return configFile.setResolver() -} - -func (configFile *ConfigFile) configNpm() error { - return configFile.setDeployerResolver() -} - func (configFile *ConfigFile) configDotnet() error { - if err := configFile.setResolver(); err != nil { + if err := configFile.setResolver(false); err != nil { return err } if configFile.Resolver.ServerId != "" { @@ -429,21 +401,10 @@ func (configFile *ConfigFile) configDotnet() error { } func (configFile *ConfigFile) configMaven() error { - // Set resolution repositories - if err := configFile.setResolverId(); err != nil { - return err - } - if configFile.Resolver.ServerId != "" { - configFile.setRepo(&configFile.Resolver.ReleaseRepo, "Set resolution repository for release dependencies", configFile.Resolver.ServerId, utils.Remote) - configFile.setRepo(&configFile.Resolver.SnapshotRepo, "Set resolution repository for snapshot dependencies", configFile.Resolver.ServerId, utils.Remote) - } - // Set deployment repositories - if err := configFile.setDeployerId(); err != nil { + if err := configFile.setDeployerResolverWithSnapshot(); err != nil { return err } if configFile.Deployer.ServerId != "" { - configFile.setRepo(&configFile.Deployer.ReleaseRepo, "Set repository for release artifacts deployment", configFile.Deployer.ServerId, utils.Local) - configFile.setRepo(&configFile.Deployer.SnapshotRepo, "Set repository for snapshot artifacts deployment", configFile.Deployer.ServerId, utils.Local) configFile.setIncludeExcludePatterns() } configFile.UseWrapper = coreutils.AskYesNo("Use Maven wrapper?", true) @@ -494,11 +455,7 @@ func (configFile *ConfigFile) readGradleGlobalConfig() { configFile.UseWrapper = coreutils.AskYesNo("Use Gradle wrapper?", true) } -func (configFile *ConfigFile) configTerraform() error { - return configFile.setDeployer() -} - -func (configFile *ConfigFile) setDeployer() error { +func (configFile *ConfigFile) setDeployer(withSnapshot bool) error { // Set deployer id if err := configFile.setDeployerId(); err != nil { return err @@ -506,28 +463,57 @@ func (configFile *ConfigFile) setDeployer() error { // Set deployment repository if configFile.Deployer.ServerId != "" { - configFile.setRepo(&configFile.Deployer.Repo, "Set repository for artifacts deployment", configFile.Deployer.ServerId, utils.Local) + deployerRepos, err := getRepositories(configFile.Deployer.ServerId, utils.Virtual, utils.Local) + if err != nil { + log.Error("failed getting repositories list: " + err.Error()) + // Continue without auto complete. + deployerRepos = []string{} + } + if withSnapshot { + configFile.setRepo(&configFile.Deployer.SnapshotRepo, "Set repository for release artifacts deployment", deployerRepos) + configFile.setRepo(&configFile.Deployer.SnapshotRepo, "Set repository for snapshot artifacts deployment", deployerRepos) + } else { + configFile.setRepo(&configFile.Deployer.Repo, "Set repository for artifacts deployment", deployerRepos) + } } return nil } -func (configFile *ConfigFile) setResolver() error { +func (configFile *ConfigFile) setResolver(withSnapshot bool) error { // Set resolver id if err := configFile.setResolverId(); err != nil { return err } // Set resolution repository if configFile.Resolver.ServerId != "" { - configFile.setRepo(&configFile.Resolver.Repo, "Set repository for dependencies resolution", configFile.Resolver.ServerId, utils.Remote) + resolverRepos, err := getRepositories(configFile.Resolver.ServerId, utils.Virtual, utils.Remote) + if err != nil { + log.Error("failed getting repositories list: " + err.Error()) + // Continue without auto complete. + resolverRepos = []string{} + } + if withSnapshot { + configFile.setRepo(&configFile.Resolver.SnapshotRepo, "Set resolution repository for release dependencies", resolverRepos) + configFile.setRepo(&configFile.Resolver.SnapshotRepo, "Set resolution repository for snapshot dependencies", resolverRepos) + } else { + configFile.setRepo(&configFile.Resolver.Repo, "Set resolution repository for dependencies resolution", resolverRepos) + } } return nil } func (configFile *ConfigFile) setDeployerResolver() error { - if err := configFile.setResolver(); err != nil { + if err := configFile.setResolver(false); err != nil { return err } - return configFile.setDeployer() + return configFile.setDeployer(false) +} + +func (configFile *ConfigFile) setDeployerResolverWithSnapshot() error { + if err := configFile.setResolver(true); err != nil { + return err + } + return configFile.setDeployer(true) } func (configFile *ConfigFile) setResolverId() error { @@ -544,9 +530,13 @@ func (configFile *ConfigFile) setServerId(serverId *string, useArtifactoryQuesti return err } -func (configFile *ConfigFile) setRepo(repo *string, message string, serverId string, repoType utils.RepoType) { +func (configFile *ConfigFile) setRepo(repo *string, promptPrefix string, availableRepos []string) { if *repo == "" { - *repo = readRepo(message+ioutils.PressTabMsg, serverId, repoType, utils.Virtual) + if len(availableRepos) > 0 { + *repo = ioutils.AskFromListWithMismatchConfirmation(promptPrefix, "Repository not found.", ioutils.ConvertToSuggests(availableRepos)) + } else { + *repo = ioutils.AskString("", promptPrefix, false, false) + } } } @@ -635,19 +625,6 @@ func readArtifactoryServer(useArtifactoryQuestion string) (string, error) { return ioutils.AskFromList("", "Set Artifactory server ID", false, ioutils.ConvertToSuggests(serversIds), defaultServer), nil } -func readRepo(promptPrefix string, serverId string, repoTypes ...utils.RepoType) string { - availableRepos, err := getRepositories(serverId, repoTypes...) - if err != nil { - log.Error("failed getting repositories list: " + err.Error()) - // Continue without auto complete. - availableRepos = []string{} - } - if len(availableRepos) > 0 { - return ioutils.AskFromListWithMismatchConfirmation(promptPrefix, "Repository not found.", ioutils.ConvertToSuggests(availableRepos)) - } - return ioutils.AskString("", promptPrefix, false, false) -} - func getServersIdAndDefault() ([]string, string, error) { allConfigs, err := config.GetAllServersConfigs() if err != nil { diff --git a/common/project/projectconfig.go b/common/project/projectconfig.go index 89bc25309..19422aea6 100644 --- a/common/project/projectconfig.go +++ b/common/project/projectconfig.go @@ -31,6 +31,7 @@ const ( Pipenv Poetry Npm + Pnpm Yarn Nuget Maven @@ -53,6 +54,7 @@ var ProjectTypes = []string{ "pipenv", "poetry", "npm", + "pnpm", "yarn", "nuget", "maven", diff --git a/go.mod b/go.mod index 9977aad09..9110b805e 100644 --- a/go.mod +++ b/go.mod @@ -9,12 +9,12 @@ require ( github.com/chzyer/readline v1.5.1 github.com/forPelevin/gomoji v1.1.8 github.com/gocarina/gocsv v0.0.0-20231116093920-b87c2d0e983a - github.com/google/uuid v1.4.0 + github.com/google/uuid v1.6.0 github.com/gookit/color v1.5.4 - github.com/jedib0t/go-pretty/v6 v6.5.3 - github.com/jfrog/build-info-go v1.9.21 - github.com/jfrog/gofrog v1.5.1 - github.com/jfrog/jfrog-client-go v1.36.0 + github.com/jedib0t/go-pretty/v6 v6.5.4 + github.com/jfrog/build-info-go v1.9.22 + github.com/jfrog/gofrog v1.6.0 + github.com/jfrog/jfrog-client-go v1.37.0 github.com/magiconair/properties v1.8.7 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.8.4 github.com/urfave/cli v1.22.14 github.com/vbauerster/mpb/v7 v7.5.3 - golang.org/x/exp v0.0.0-20240119083558-1b970713d09a - golang.org/x/mod v0.14.0 + golang.org/x/exp v0.0.0-20240213143201-ec583247a57a + golang.org/x/mod v0.15.0 golang.org/x/sync v0.6.0 - golang.org/x/term v0.16.0 + golang.org/x/term v0.17.0 golang.org/x/text v0.14.0 gopkg.in/yaml.v3 v3.0.1 ) @@ -33,13 +33,13 @@ require ( require ( dario.cat/mergo v1.0.0 // indirect github.com/BurntSushi/toml v1.3.2 // indirect - github.com/CycloneDX/cyclonedx-go v0.7.2 // indirect + github.com/CycloneDX/cyclonedx-go v0.8.0 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect github.com/ProtonMail/go-crypto v1.0.0 // indirect github.com/VividCortex/ewma v1.2.0 // indirect github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect github.com/andybalholm/brotli v1.1.0 // indirect - github.com/cloudflare/circl v1.3.3 // indirect + github.com/cloudflare/circl v1.3.7 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect github.com/cyphar/filepath-securejoin v0.2.4 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect @@ -88,15 +88,15 @@ 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.18.0 // indirect - golang.org/x/net v0.20.0 // indirect - golang.org/x/sys v0.16.0 // indirect - golang.org/x/tools v0.17.0 // indirect + golang.org/x/crypto v0.19.0 // indirect + golang.org/x/net v0.21.0 // indirect + golang.org/x/sys v0.17.0 // indirect + golang.org/x/tools v0.18.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect 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.20240122123649-74f725715bbe +// replace github.com/jfrog/jfrog-client-go => github.com/jfrog/jfrog-client-go v1.28.1-0.20240218093454-1c352a93c23d // replace github.com/jfrog/build-info-go => github.com/jfrog/build-info-go v1.8.9-0.20231220102935-c8776c613ad8 diff --git a/go.sum b/go.sum index 185260231..d6555e914 100644 --- a/go.sum +++ b/go.sum @@ -2,8 +2,8 @@ dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= -github.com/CycloneDX/cyclonedx-go v0.7.2 h1:kKQ0t1dPOlugSIYVOMiMtFqeXI2wp/f5DBIdfux8gnQ= -github.com/CycloneDX/cyclonedx-go v0.7.2/go.mod h1:K2bA+324+Og0X84fA8HhN2X066K7Bxz4rpMQ4ZhjtSk= +github.com/CycloneDX/cyclonedx-go v0.8.0 h1:FyWVj6x6hoJrui5uRQdYZcSievw3Z32Z88uYzG/0D6M= +github.com/CycloneDX/cyclonedx-go v0.8.0/go.mod h1:K2bA+324+Og0X84fA8HhN2X066K7Bxz4rpMQ4ZhjtSk= github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= @@ -32,8 +32,9 @@ github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObk github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/chzyer/test v1.0.0 h1:p3BQDXSxOhOG0P9z6/hGnII4LGiEPOYBhs8asl/fC04= github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8= -github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEMs= github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= +github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU= +github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA= github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= @@ -70,24 +71,24 @@ github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4er github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= -github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0= github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= -github.com/jedib0t/go-pretty/v6 v6.5.3 h1:GIXn6Er/anHTkVUoufs7ptEvxdD6KIhR7Axa2wYCPF0= -github.com/jedib0t/go-pretty/v6 v6.5.3/go.mod h1:5LQIxa52oJ/DlDSLv0HEkWOFMDGoWkJb9ss5KqPpJBg= +github.com/jedib0t/go-pretty/v6 v6.5.4 h1:gOGo0613MoqUcf0xCj+h/V3sHDaZasfv152G6/5l91s= +github.com/jedib0t/go-pretty/v6 v6.5.4/go.mod h1:5LQIxa52oJ/DlDSLv0HEkWOFMDGoWkJb9ss5KqPpJBg= github.com/jfrog/archiver/v3 v3.6.0 h1:OVZ50vudkIQmKMgA8mmFF9S0gA47lcag22N13iV3F1w= github.com/jfrog/archiver/v3 v3.6.0/go.mod h1:fCAof46C3rAXgZurS8kNRNdSVMKBbZs+bNNhPYxLldI= -github.com/jfrog/build-info-go v1.9.21 h1:bcD0SEC2lEilhjE+aDB3xlvA8zsr4Kw/bFzvr9Tcj9I= -github.com/jfrog/build-info-go v1.9.21/go.mod h1:Vxv6zmx4e1NWsx40OHaDWCCYDeYAq2yXzpJ4nsDChbE= -github.com/jfrog/gofrog v1.5.1 h1:2AXL8hHu1jJFMIoCqTp2OyRUfEqEp4nC7J8fwn6KtwE= -github.com/jfrog/gofrog v1.5.1/go.mod h1:SZ1EPJUruxrVGndOzHd+LTiwWYKMlHqhKD+eu+v5Hqg= -github.com/jfrog/jfrog-client-go v1.36.0 h1:iODLDjYSlK7rLH8/lEmAFHwYsboeBfaqxXybz6waraE= -github.com/jfrog/jfrog-client-go v1.36.0/go.mod h1:y1WF6eiZ7V2DortiwjpMEicEH6NIJH+hOXI5QI2W3NU= +github.com/jfrog/build-info-go v1.9.22 h1:+RPM8OJvgBvOo+PYPnVYkg+S3hl1JlyCJn/aGxLqRMI= +github.com/jfrog/build-info-go v1.9.22/go.mod h1:QHcKuesY4MrBVBuEwwBz4uIsX6mwYuMEDV09ng4AvAU= +github.com/jfrog/gofrog v1.6.0 h1:jOwb37nHY2PnxePNFJ6e6279Pgkr3di05SbQQw47Mq8= +github.com/jfrog/gofrog v1.6.0/go.mod h1:SZ1EPJUruxrVGndOzHd+LTiwWYKMlHqhKD+eu+v5Hqg= +github.com/jfrog/jfrog-client-go v1.37.0 h1:ULHMGfST0ONH8Ur4dkfMMVsuvxLWBHral7Y1Oi7ub0k= +github.com/jfrog/jfrog-client-go v1.37.0/go.mod h1:kLlNc4BLpYtH2I0hHB5AcIwx6mEw1gQg65amvrY2DHM= 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= @@ -210,14 +211,14 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= -golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= -golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= -golang.org/x/exp v0.0.0-20240119083558-1b970713d09a h1:Q8/wZp0KX97QFTc2ywcOE0YRjZPVIx+MXInMzdvQqcA= -golang.org/x/exp v0.0.0-20240119083558-1b970713d09a/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08= +golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/exp v0.0.0-20240213143201-ec583247a57a h1:HinSgX1tJRX3KsL//Gxynpw5CTOAIPhgL4W8PNiIpVE= +golang.org/x/exp v0.0.0-20240213143201-ec583247a57a/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= -golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8= +golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= @@ -225,8 +226,8 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= -golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= -golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= +golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -259,15 +260,15 @@ golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= -golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= +golang.org/x/sys v0.17.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.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= -golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE= -golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= +golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -281,8 +282,8 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 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.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc= -golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= +golang.org/x/tools v0.18.0 h1:k8NLag8AGHnn+PHbl7g43CtqZAwG60vZkLqgyZgIHgQ= +golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 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= diff --git a/lifecycle/common.go b/lifecycle/common.go index 55e7ef771..e185cb578 100644 --- a/lifecycle/common.go +++ b/lifecycle/common.go @@ -19,7 +19,8 @@ type releaseBundleCmd struct { rbProjectKey string } -func (rbc *releaseBundleCmd) getPrerequisites() (servicesManager *lifecycle.LifecycleServicesManager, rbDetails services.ReleaseBundleDetails, params services.CreateOrPromoteReleaseBundleParams, err error) { +func (rbc *releaseBundleCmd) getPrerequisites() (servicesManager *lifecycle.LifecycleServicesManager, + rbDetails services.ReleaseBundleDetails, queryParams services.CommonOptionalQueryParams, err error) { servicesManager, err = utils.CreateLifecycleServiceManager(rbc.serverDetails, false) if err != nil { return @@ -28,12 +29,9 @@ func (rbc *releaseBundleCmd) getPrerequisites() (servicesManager *lifecycle.Life ReleaseBundleName: rbc.releaseBundleName, ReleaseBundleVersion: rbc.releaseBundleVersion, } - params = services.CreateOrPromoteReleaseBundleParams{ - ReleaseBundleQueryParams: services.ReleaseBundleQueryParams{ - ProjectKey: rbc.rbProjectKey, - Async: !rbc.sync, - }, - SigningKeyName: rbc.signingKeyName, + queryParams = services.CommonOptionalQueryParams{ + ProjectKey: rbc.rbProjectKey, + Async: !rbc.sync, } return } diff --git a/lifecycle/createcommon.go b/lifecycle/createcommon.go index 5149a82a7..4d5fba599 100644 --- a/lifecycle/createcommon.go +++ b/lifecycle/createcommon.go @@ -67,13 +67,13 @@ func (rbc *ReleaseBundleCreateCommand) Run() error { return err } - servicesManager, rbDetails, params, err := rbc.getPrerequisites() + servicesManager, rbDetails, queryParams, err := rbc.getPrerequisites() if err != nil { return err } if rbc.buildsSpecPath != "" { - return rbc.createFromBuilds(servicesManager, rbDetails, params) + return rbc.createFromBuilds(servicesManager, rbDetails, queryParams) } - return rbc.createFromReleaseBundles(servicesManager, rbDetails, params) + return rbc.createFromReleaseBundles(servicesManager, rbDetails, queryParams) } diff --git a/lifecycle/createfrombuilds.go b/lifecycle/createfrombuilds.go index f9a13ed2e..a955b997a 100644 --- a/lifecycle/createfrombuilds.go +++ b/lifecycle/createfrombuilds.go @@ -12,7 +12,7 @@ import ( ) func (rbc *ReleaseBundleCreateCommand) createFromBuilds(servicesManager *lifecycle.LifecycleServicesManager, - rbDetails services.ReleaseBundleDetails, params services.CreateOrPromoteReleaseBundleParams) error { + rbDetails services.ReleaseBundleDetails, queryParams services.CommonOptionalQueryParams) error { builds := CreateFromBuildsSpec{} content, err := fileutils.ReadFile(rbc.buildsSpecPath) @@ -31,13 +31,13 @@ func (rbc *ReleaseBundleCreateCommand) createFromBuilds(servicesManager *lifecyc if err != nil { return err } - return servicesManager.CreateReleaseBundleFromBuilds(rbDetails, params, buildsSource) + return servicesManager.CreateReleaseBundleFromBuilds(rbDetails, queryParams, rbc.signingKeyName, buildsSource) } func (rbc *ReleaseBundleCreateCommand) convertToBuildsSource(builds CreateFromBuildsSpec) (services.CreateFromBuildsSource, error) { buildsSource := services.CreateFromBuildsSource{} for _, build := range builds.Builds { - buildSource := services.BuildSource{BuildName: build.Name} + buildSource := services.BuildSource{BuildName: build.Name, IncludeDependencies: build.IncludeDependencies} buildNumber, err := rbc.getLatestBuildNumberIfEmpty(build.Name, build.Number, build.Project) if err != nil { return services.CreateFromBuildsSource{}, err @@ -82,7 +82,8 @@ type CreateFromBuildsSpec struct { } type SourceBuildSpec struct { - Name string `json:"name,omitempty"` - Number string `json:"number,omitempty"` - Project string `json:"project,omitempty"` + Name string `json:"name,omitempty"` + Number string `json:"number,omitempty"` + Project string `json:"project,omitempty"` + IncludeDependencies bool `json:"includeDependencies,omitempty"` } diff --git a/lifecycle/createfrombundles.go b/lifecycle/createfrombundles.go index 872d76765..1e9253351 100644 --- a/lifecycle/createfrombundles.go +++ b/lifecycle/createfrombundles.go @@ -9,7 +9,7 @@ import ( ) func (rbc *ReleaseBundleCreateCommand) createFromReleaseBundles(servicesManager *lifecycle.LifecycleServicesManager, - rbDetails services.ReleaseBundleDetails, params services.CreateOrPromoteReleaseBundleParams) error { + rbDetails services.ReleaseBundleDetails, queryParams services.CommonOptionalQueryParams) error { bundles := CreateFromReleaseBundlesSpec{} content, err := fileutils.ReadFile(rbc.releaseBundlesSpecPath) @@ -25,7 +25,7 @@ func (rbc *ReleaseBundleCreateCommand) createFromReleaseBundles(servicesManager } releaseBundlesSource := rbc.convertToReleaseBundlesSource(bundles) - return servicesManager.CreateReleaseBundleFromBundles(rbDetails, params, releaseBundlesSource) + return servicesManager.CreateReleaseBundleFromBundles(rbDetails, queryParams, rbc.signingKeyName, releaseBundlesSource) } func (rbc *ReleaseBundleCreateCommand) convertToReleaseBundlesSource(bundles CreateFromReleaseBundlesSpec) services.CreateFromReleaseBundlesSource { diff --git a/lifecycle/promote.go b/lifecycle/promote.go index 6958bae5a..0cf8b64d0 100644 --- a/lifecycle/promote.go +++ b/lifecycle/promote.go @@ -3,14 +3,16 @@ package lifecycle import ( "encoding/json" "github.com/jfrog/jfrog-cli-core/v2/utils/config" + "github.com/jfrog/jfrog-client-go/lifecycle/services" "github.com/jfrog/jfrog-client-go/utils" "github.com/jfrog/jfrog-client-go/utils/log" ) type ReleaseBundlePromoteCommand struct { releaseBundleCmd - environment string - overwrite bool + environment string + includeReposPatterns []string + excludeReposPatterns []string } func NewReleaseBundlePromoteCommand() *ReleaseBundlePromoteCommand { @@ -52,8 +54,13 @@ func (rbp *ReleaseBundlePromoteCommand) SetEnvironment(environment string) *Rele return rbp } -func (rbp *ReleaseBundlePromoteCommand) SetOverwrite(overwrite bool) *ReleaseBundlePromoteCommand { - rbp.overwrite = overwrite +func (rbp *ReleaseBundlePromoteCommand) SetIncludeReposPatterns(includeReposPatterns []string) *ReleaseBundlePromoteCommand { + rbp.includeReposPatterns = includeReposPatterns + return rbp +} + +func (rbp *ReleaseBundlePromoteCommand) SetExcludeReposPatterns(excludeReposPatterns []string) *ReleaseBundlePromoteCommand { + rbp.excludeReposPatterns = excludeReposPatterns return rbp } @@ -70,12 +77,18 @@ func (rbp *ReleaseBundlePromoteCommand) Run() error { return err } - servicesManager, rbDetails, params, err := rbp.getPrerequisites() + servicesManager, rbDetails, queryParams, err := rbp.getPrerequisites() if err != nil { return err } - promotionResp, err := servicesManager.PromoteReleaseBundle(rbDetails, params, rbp.environment, rbp.overwrite) + promotionParams := services.RbPromotionParams{ + Environment: rbp.environment, + IncludedRepositoryKeys: rbp.includeReposPatterns, + ExcludedRepositoryKeys: rbp.excludeReposPatterns, + } + + promotionResp, err := servicesManager.PromoteReleaseBundle(rbDetails, queryParams, rbp.signingKeyName, promotionParams) if err != nil { return err } diff --git a/plugins/common/config.go b/plugins/common/config.go index 8e22a2f3e..a906321ab 100644 --- a/plugins/common/config.go +++ b/plugins/common/config.go @@ -14,10 +14,9 @@ import ( // Any empty configuration could be later overridden by environment variables if set. func CreateBuildConfiguration(c *components.Context) *build.BuildConfiguration { buildConfiguration := new(build.BuildConfiguration) - buildNameArg, buildNumberArg := c.Arguments[0], c.Arguments[1] - if buildNameArg == "" || buildNumberArg == "" { - buildNameArg = "" - buildNumberArg = "" + var buildNameArg, buildNumberArg string + if len(c.Arguments) > 1 && c.Arguments[0] != "" && c.Arguments[1] != "" { + buildNameArg, buildNumberArg = c.Arguments[0], c.Arguments[1] } buildConfiguration.SetBuildName(buildNameArg).SetBuildNumber(buildNumberArg).SetProject(c.GetStringFlagValue("project")).SetModule(c.GetStringFlagValue("module")) return buildConfiguration diff --git a/tests/testdata/maven-example-with-many-types/pom.xml b/tests/testdata/maven-example-with-many-types/pom.xml new file mode 100644 index 000000000..6d2096c96 --- /dev/null +++ b/tests/testdata/maven-example-with-many-types/pom.xml @@ -0,0 +1,53 @@ + + 4.0.0 + + org.jfrog + cli-test + 1.0 + jar + + cli-test + http://maven.apache.org + + + UTF-8 + 1.8 + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.1 + + ${java.version} + ${java.version} + + + + + + + + junit + junit + 4.11 + test + + + commons-io + commons-io + 1.2 + test + + + org.webjars + lodash + 4.17.21 + pom + + + \ No newline at end of file diff --git a/utils/coreutils/techutils.go b/utils/coreutils/techutils.go index 78e2321e6..ca63dc653 100644 --- a/utils/coreutils/techutils.go +++ b/utils/coreutils/techutils.go @@ -24,6 +24,7 @@ const ( Maven Technology = "maven" Gradle Technology = "gradle" Npm Technology = "npm" + Pnpm Technology = "pnpm" Yarn Technology = "yarn" Go Technology = "go" Pip Technology = "pip" @@ -77,7 +78,7 @@ var technologiesData = map[Technology]TechData{ }, Npm: { indicators: []string{"package.json", "package-lock.json", "npm-shrinkwrap.json"}, - exclude: []string{".yarnrc.yml", "yarn.lock", ".yarn"}, + exclude: []string{"pnpm-lock.yaml", ".yarnrc.yml", "yarn.lock", ".yarn"}, ciSetupSupport: true, packageDescriptors: []string{"package.json"}, formal: string(Npm), @@ -85,8 +86,16 @@ var technologiesData = map[Technology]TechData{ packageInstallationCommand: "install", applicabilityScannable: true, }, + Pnpm: { + indicators: []string{"pnpm-lock.yaml"}, + exclude: []string{".yarnrc.yml", "yarn.lock", ".yarn"}, + packageDescriptors: []string{"package.json"}, + packageVersionOperator: "@", + applicabilityScannable: true, + }, Yarn: { indicators: []string{".yarnrc.yml", "yarn.lock", ".yarn", ".yarnrc"}, + exclude: []string{"pnpm-lock.yaml"}, packageDescriptors: []string{"package.json"}, packageVersionOperator: "@", applicabilityScannable: true, diff --git a/utils/coreutils/techutils_test.go b/utils/coreutils/techutils_test.go index 7d170b9da..c69d58ebe 100644 --- a/utils/coreutils/techutils_test.go +++ b/utils/coreutils/techutils_test.go @@ -17,6 +17,7 @@ func TestDetectTechnologiesByFilePaths(t *testing.T) { }{ {"simpleMavenTest", []string{"pom.xml"}, map[Technology]bool{Maven: true}}, {"npmTest", []string{"../package.json"}, map[Technology]bool{Npm: true}}, + {"pnpmTest", []string{"../package.json", "pnpm-lock.yaml"}, map[Technology]bool{Pnpm: true}}, {"yarnTest", []string{"./package.json", "./.yarn"}, map[Technology]bool{Yarn: true}}, {"windowsGradleTest", []string{"c:\\users\\test\\package\\build.gradle"}, map[Technology]bool{Gradle: true}}, {"windowsPipTest", []string{"c:\\users\\test\\package\\setup.py"}, map[Technology]bool{Pip: true}}, @@ -73,12 +74,19 @@ func TestMapFilesToRelevantWorkingDirectories(t *testing.T) { }, expectedExcluded: noExclude, }, + { + name: "pnpmTest", + paths: []string{filepath.Join("dir", "package.json"), filepath.Join("dir", "pnpm-lock.yaml")}, + requestedDescriptors: noRequest, + expectedWorkingDir: map[string][]string{"dir": {filepath.Join("dir", "package.json"), filepath.Join("dir", "pnpm-lock.yaml")}}, + expectedExcluded: map[string][]Technology{"dir": {Npm, Yarn}}, + }, { name: "yarnTest", paths: []string{filepath.Join("dir", "package.json"), filepath.Join("dir", ".yarn")}, requestedDescriptors: noRequest, expectedWorkingDir: map[string][]string{"dir": {filepath.Join("dir", "package.json"), filepath.Join("dir", ".yarn")}}, - expectedExcluded: map[string][]Technology{"dir": {Npm}}, + expectedExcluded: map[string][]Technology{"dir": {Npm, Pnpm}}, }, { name: "golangTest", @@ -138,13 +146,20 @@ func TestMapFilesToRelevantWorkingDirectories(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { detectedWd, detectedExcluded := mapFilesToRelevantWorkingDirectories(test.paths, test.requestedDescriptors) + // Assert working directories expectedKeys := maps.Keys(test.expectedWorkingDir) actualKeys := maps.Keys(detectedWd) assert.ElementsMatch(t, expectedKeys, actualKeys, "expected: %s, actual: %s", expectedKeys, actualKeys) for key, value := range test.expectedWorkingDir { assert.ElementsMatch(t, value, detectedWd[key], "expected: %s, actual: %s", value, detectedWd[key]) } - assert.True(t, reflect.DeepEqual(test.expectedExcluded, detectedExcluded), "expected: %s, actual: %s", test.expectedExcluded, detectedExcluded) + // Assert excluded + expectedKeys = maps.Keys(test.expectedExcluded) + actualKeys = maps.Keys(detectedExcluded) + assert.ElementsMatch(t, expectedKeys, actualKeys, "expected: %s, actual: %s", expectedKeys, actualKeys) + for key, value := range test.expectedExcluded { + assert.ElementsMatch(t, value, detectedExcluded[key], "expected: %s, actual: %s", value, detectedExcluded[key]) + } }) } } @@ -178,6 +193,7 @@ func TestMapWorkingDirectoriesToTechnologies(t *testing.T) { "dir": {filepath.Join("dir", "package.json"), filepath.Join("dir", "package-lock.json"), filepath.Join("dir", "build.gradle.kts"), filepath.Join("dir", "project.sln")}, "directory": {filepath.Join("directory", "npm-shrinkwrap.json")}, "dir3": {filepath.Join("dir3", "package.json"), filepath.Join("dir3", ".yarn")}, + filepath.Join("dir3", "dir"): {filepath.Join("dir3", "dir", "package.json"), filepath.Join("dir3", "dir", "pnpm-lock.yaml")}, filepath.Join("dir", "dir2"): {filepath.Join("dir", "dir2", "go.mod")}, filepath.Join("users_dir", "test", "package"): {filepath.Join("users_dir", "test", "package", "setup.py")}, filepath.Join("users_dir", "test", "package2"): {filepath.Join("users_dir", "test", "package2", "requirements.txt")}, @@ -186,7 +202,8 @@ func TestMapWorkingDirectoriesToTechnologies(t *testing.T) { }, excludedTechAtWorkingDir: map[string][]Technology{ filepath.Join("users", "test", "package"): {Pip}, - "dir3": {Npm}, + "dir3": {Npm}, + filepath.Join("dir3", "dir"): {Npm, Yarn}, }, requestedTechs: noRequestTech, requestedDescriptors: noRequestSpecialDescriptors, @@ -196,6 +213,7 @@ func TestMapWorkingDirectoriesToTechnologies(t *testing.T) { "dir": {filepath.Join("dir", "package.json")}, "directory": {}, }, + Pnpm: {filepath.Join("dir3", "dir"): {filepath.Join("dir3", "dir", "package.json")}}, Yarn: {"dir3": {filepath.Join("dir3", "package.json")}}, Go: {filepath.Join("dir", "dir2"): {filepath.Join("dir", "dir2", "go.mod")}}, Pip: { @@ -354,6 +372,7 @@ func TestGetTechInformationFromWorkingDir(t *testing.T) { "dir": {filepath.Join("dir", "package.json"), filepath.Join("dir", "package-lock.json"), filepath.Join("dir", "build.gradle.kts"), filepath.Join("dir", "project.sln"), filepath.Join("dir", "blabla.txt")}, "directory": {filepath.Join("directory", "npm-shrinkwrap.json")}, "dir3": {filepath.Join("dir3", "package.json"), filepath.Join("dir3", ".yarn")}, + filepath.Join("dir3", "dir"): {filepath.Join("dir3", "dir", "package.json"), filepath.Join("dir3", "dir", "pnpm-lock.yaml")}, filepath.Join("dir", "dir2"): {filepath.Join("dir", "dir2", "go.mod")}, filepath.Join("users_dir", "test", "package"): {filepath.Join("users_dir", "test", "package", "setup.py")}, filepath.Join("users_dir", "test", "package2"): {filepath.Join("users_dir", "test", "package2", "requirements.txt")}, @@ -362,7 +381,8 @@ func TestGetTechInformationFromWorkingDir(t *testing.T) { } excludedTechAtWorkingDir := map[string][]Technology{ filepath.Join("users", "test", "package"): {Pip}, - "dir3": {Npm}, + "dir3": {Npm, Pnpm}, + filepath.Join("dir3", "dir"): {Npm, Yarn}, } tests := []struct { @@ -392,6 +412,12 @@ func TestGetTechInformationFromWorkingDir(t *testing.T) { "directory": {}, }, }, + { + name: "pnpmTest", + tech: Pnpm, + requestedDescriptors: map[Technology][]string{}, + expected: map[string][]string{filepath.Join("dir3", "dir"): {filepath.Join("dir3", "dir", "package.json")}}, + }, { name: "yarnTest", tech: Yarn, diff --git a/utils/java/deptreemanager.go b/utils/java/deptreemanager.go index 071e2192d..e323fad1e 100644 --- a/utils/java/deptreemanager.go +++ b/utils/java/deptreemanager.go @@ -5,7 +5,6 @@ import ( "os" "strings" - "github.com/jfrog/gofrog/datastructures" "github.com/jfrog/jfrog-cli-core/v2/utils/config" "github.com/jfrog/jfrog-cli-core/v2/utils/coreutils" "github.com/jfrog/jfrog-cli-core/v2/utils/xray" @@ -17,22 +16,20 @@ const ( GavPackageTypeIdentifier = "gav://" ) -func BuildDependencyTree(serverDetails *config.ServerDetails, depsRepo string, useWrapper, isMavenDepTreeInstalled bool, tech coreutils.Technology) ([]*xrayUtils.GraphNode, []string, error) { - depTreeParams := &DepTreeParams{ - UseWrapper: useWrapper, - Server: serverDetails, - DepsRepo: depsRepo, - } +func BuildDependencyTree(depTreeParams DepTreeParams, tech coreutils.Technology) ([]*xrayUtils.GraphNode, map[string][]string, error) { if tech == coreutils.Maven { - return buildMavenDependencyTree(depTreeParams, isMavenDepTreeInstalled) + return buildMavenDependencyTree(&depTreeParams) } - return buildGradleDependencyTree(depTreeParams) + return buildGradleDependencyTree(&depTreeParams) } type DepTreeParams struct { - UseWrapper bool - Server *config.ServerDetails - DepsRepo string + UseWrapper bool + Server *config.ServerDetails + DepsRepo string + IsMavenDepTreeInstalled bool + IsCurationCmd bool + CurationCacheFolder string } type DepTreeManager struct { @@ -47,36 +44,31 @@ func NewDepTreeManager(params *DepTreeParams) DepTreeManager { // The structure of a dependency tree of a module in a Gradle/Maven project, as created by the gradle-dep-tree and maven-dep-tree plugins. type moduleDepTree struct { - Root string `json:"root"` - Nodes map[string]depTreeNode `json:"nodes"` -} - -type depTreeNode struct { - Children []string `json:"children"` + Root string `json:"root"` + Nodes map[string]xray.DepTreeNode `json:"nodes"` } // Reads the output files of the gradle-dep-tree and maven-dep-tree plugins and returns them as a slice of GraphNodes. // It takes the output of the plugin's run (which is a byte representation of a list of paths of the output files, separated by newlines) as input. -func getGraphFromDepTree(outputFilePaths string) (depsGraph []*xrayUtils.GraphNode, uniqueDeps []string, err error) { +func getGraphFromDepTree(outputFilePaths string) (depsGraph []*xrayUtils.GraphNode, uniqueDepsMap map[string][]string, err error) { modules, err := parseDepTreeFiles(outputFilePaths) if err != nil { return } - allModulesUniqueDeps := datastructures.MakeSet[string]() + uniqueDepsMap = map[string][]string{} for _, module := range modules { moduleTree, moduleUniqueDeps := GetModuleTreeAndDependencies(module) depsGraph = append(depsGraph, moduleTree) - for _, depToAdd := range moduleUniqueDeps { - allModulesUniqueDeps.Add(depToAdd) + for depToAdd, depTypes := range moduleUniqueDeps { + uniqueDepsMap[depToAdd] = depTypes } } - uniqueDeps = allModulesUniqueDeps.ToSlice() return } // Returns a dependency tree and a flat list of the module's dependencies for the given module -func GetModuleTreeAndDependencies(module *moduleDepTree) (*xrayUtils.GraphNode, []string) { - moduleTreeMap := make(map[string][]string) +func GetModuleTreeAndDependencies(module *moduleDepTree) (*xrayUtils.GraphNode, map[string][]string) { + moduleTreeMap := make(map[string]xray.DepTreeNode) moduleDeps := module.Nodes for depName, dependency := range moduleDeps { dependencyId := GavPackageTypeIdentifier + depName @@ -85,8 +77,9 @@ func GetModuleTreeAndDependencies(module *moduleDepTree) (*xrayUtils.GraphNode, childId := GavPackageTypeIdentifier + childName childrenList = append(childrenList, childId) } - if len(childrenList) > 0 { - moduleTreeMap[dependencyId] = childrenList + moduleTreeMap[dependencyId] = xray.DepTreeNode{ + Types: dependency.Types, + Children: childrenList, } } return xray.BuildXrayDependencyTree(moduleTreeMap, GavPackageTypeIdentifier+module.Root) diff --git a/utils/java/gradle.go b/utils/java/gradle.go index 013a2f3a6..57ceff8bd 100644 --- a/utils/java/gradle.go +++ b/utils/java/gradle.go @@ -56,7 +56,7 @@ type gradleDepTreeManager struct { DepTreeManager } -func buildGradleDependencyTree(params *DepTreeParams) (dependencyTree []*xrayUtils.GraphNode, uniqueDeps []string, err error) { +func buildGradleDependencyTree(params *DepTreeParams) (dependencyTree []*xrayUtils.GraphNode, uniqueDeps map[string][]string, err error) { manager := &gradleDepTreeManager{DepTreeManager: NewDepTreeManager(params)} outputFileContent, err := manager.runGradleDepTree() if err != nil { diff --git a/utils/java/mvn.go b/utils/java/mvn.go index 56deee17c..5b38db6c0 100644 --- a/utils/java/mvn.go +++ b/utils/java/mvn.go @@ -7,6 +7,7 @@ import ( "net/url" "os" "os/exec" + "path" "path/filepath" "strings" @@ -21,7 +22,7 @@ const ( mavenDepTreeJarFile = "maven-dep-tree.jar" mavenDepTreeOutputFile = "mavendeptree.out" // Changing this version also requires a change in MAVEN_DEP_TREE_VERSION within buildscripts/download_jars.sh - mavenDepTreeVersion = "1.0.2" + mavenDepTreeVersion = "1.1.0" settingsXmlFile = "settings.xml" ) @@ -42,25 +43,31 @@ var mavenDepTreeJar []byte type MavenDepTreeManager struct { DepTreeManager - isInstalled bool - cmdName MavenDepTreeCmd - settingsXmlPath string + isInstalled bool + // this flag its curation command, it will set dedicated cache and download url. + isCurationCmd bool + // path to the curation dedicated cache + curationCacheFolder string + cmdName MavenDepTreeCmd + settingsXmlPath string } -func NewMavenDepTreeManager(params *DepTreeParams, cmdName MavenDepTreeCmd, isDepTreeInstalled bool) *MavenDepTreeManager { +func NewMavenDepTreeManager(params *DepTreeParams, cmdName MavenDepTreeCmd) *MavenDepTreeManager { depTreeManager := NewDepTreeManager(&DepTreeParams{ Server: params.Server, DepsRepo: params.DepsRepo, }) return &MavenDepTreeManager{ - DepTreeManager: depTreeManager, - isInstalled: isDepTreeInstalled, - cmdName: cmdName, + DepTreeManager: depTreeManager, + isInstalled: params.IsMavenDepTreeInstalled, + cmdName: cmdName, + isCurationCmd: params.IsCurationCmd, + curationCacheFolder: params.CurationCacheFolder, } } -func buildMavenDependencyTree(params *DepTreeParams, isDepTreeInstalled bool) (dependencyTree []*xrayUtils.GraphNode, uniqueDeps []string, err error) { - manager := NewMavenDepTreeManager(params, Tree, isDepTreeInstalled) +func buildMavenDependencyTree(params *DepTreeParams) (dependencyTree []*xrayUtils.GraphNode, uniqueDeps map[string][]string, err error) { + manager := NewMavenDepTreeManager(params, Tree) outputFilePaths, clearMavenDepTreeRun, err := manager.RunMavenDepTree() if err != nil { if clearMavenDepTreeRun != nil { @@ -68,11 +75,9 @@ func buildMavenDependencyTree(params *DepTreeParams, isDepTreeInstalled bool) (d } return } - defer func() { err = errors.Join(err, clearMavenDepTreeRun()) }() - dependencyTree, uniqueDeps, err = getGraphFromDepTree(outputFilePaths) return } @@ -123,6 +128,9 @@ func (mdt *MavenDepTreeManager) execMavenDepTree(depTreeExecDir string) (string, func (mdt *MavenDepTreeManager) runTreeCmd(depTreeExecDir string) (string, error) { mavenDepTreePath := filepath.Join(depTreeExecDir, mavenDepTreeOutputFile) goals := []string{"com.jfrog:maven-dep-tree:" + mavenDepTreeVersion + ":" + string(Tree), "-DdepsTreeOutputFile=" + mavenDepTreePath, "-B"} + if mdt.isCurationCmd { + goals = append(goals, "-Dmaven.repo.local="+mdt.curationCacheFolder) + } if _, err := mdt.RunMvnCmd(goals); err != nil { return "", err } @@ -199,16 +207,20 @@ func removeMavenConfig() (func() error, error) { // Creates a new settings.xml file configured with the provided server and repository from the current MavenDepTreeManager instance. // The settings.xml will be written to the given path. -func (mdt *MavenDepTreeManager) createSettingsXmlWithConfiguredArtifactory(path string) error { +func (mdt *MavenDepTreeManager) createSettingsXmlWithConfiguredArtifactory(settingsXmlPath string) error { username, password, err := getArtifactoryAuthFromServer(mdt.server) if err != nil { return err } - remoteRepositoryFullPath, err := url.JoinPath(mdt.server.ArtifactoryUrl, mdt.depsRepo) + endPoint := mdt.depsRepo + if mdt.isCurationCmd { + endPoint = path.Join("api/curation/audit", endPoint) + } + remoteRepositoryFullPath, err := url.JoinPath(mdt.server.ArtifactoryUrl, endPoint) if err != nil { return err } - mdt.settingsXmlPath = filepath.Join(path, settingsXmlFile) + mdt.settingsXmlPath = filepath.Join(settingsXmlPath, settingsXmlFile) settingsXmlContent := fmt.Sprintf(settingsXmlTemplate, username, password, remoteRepositoryFullPath) return errorutils.CheckError(os.WriteFile(mdt.settingsXmlPath, []byte(settingsXmlContent), 0600)) diff --git a/utils/java/mvn_test.go b/utils/java/mvn_test.go index fb141a3d9..b588806c4 100644 --- a/utils/java/mvn_test.go +++ b/utils/java/mvn_test.go @@ -1,11 +1,14 @@ package java import ( + "github.com/jfrog/build-info-go/utils" "github.com/jfrog/jfrog-cli-core/v2/utils/config" coreTests "github.com/jfrog/jfrog-cli-core/v2/utils/tests" "github.com/jfrog/jfrog-client-go/utils/io/fileutils" "github.com/jfrog/jfrog-client-go/utils/tests" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "golang.org/x/exp/maps" "os" "path/filepath" "strings" @@ -32,6 +35,26 @@ const ( * +` + //#nosec G101 - dummy token for testing + settingsXmlWithUsernameAndPasswordAndCurationDedicatedAPi = ` + + + + artifactory + testUser + testPass + + + + + artifactory + https://myartifactory.com/artifactory/api/curation/audit/testRepo + * + + ` //#nosec G101 - dummy token for testing settingsXmlWithUsernameAndToken = ` @@ -95,9 +118,9 @@ func TestMavenTreesMultiModule(t *testing.T) { GavPackageTypeIdentifier + "hsqldb:hsqldb:1.8.0.10", } // Run getModulesDependencyTrees - modulesDependencyTrees, uniqueDeps, err := buildMavenDependencyTree(&DepTreeParams{}, false) + modulesDependencyTrees, uniqueDeps, err := buildMavenDependencyTree(&DepTreeParams{}) if assert.NoError(t, err) && assert.NotEmpty(t, modulesDependencyTrees) { - assert.ElementsMatch(t, uniqueDeps, expectedUniqueDeps, "First is actual, Second is Expected") + assert.ElementsMatch(t, maps.Keys(uniqueDeps), expectedUniqueDeps, "First is actual, Second is Expected") // Check root module multi := coreTests.GetAndAssertNode(t, modulesDependencyTrees, "org.jfrog.test:multi:3.7-SNAPSHOT") if assert.NotNil(t, multi) { @@ -145,9 +168,9 @@ func TestMavenWrapperTrees(t *testing.T) { GavPackageTypeIdentifier + "javax.servlet:servlet-api:2.5", } - modulesDependencyTrees, uniqueDeps, err := buildMavenDependencyTree(&DepTreeParams{}, false) + modulesDependencyTrees, uniqueDeps, err := buildMavenDependencyTree(&DepTreeParams{}) if assert.NoError(t, err) && assert.NotEmpty(t, modulesDependencyTrees) { - assert.ElementsMatch(t, uniqueDeps, expectedUniqueDeps, "First is actual, Second is Expected") + assert.ElementsMatch(t, maps.Keys(uniqueDeps), expectedUniqueDeps, "First is actual, Second is Expected") // Check root module multi := coreTests.GetAndAssertNode(t, modulesDependencyTrees, "org.jfrog.test:multi:3.7-SNAPSHOT") if assert.NotNil(t, multi) { @@ -166,6 +189,58 @@ func TestMavenWrapperTrees(t *testing.T) { } } +func TestMavenWrapperTreesTypes(t *testing.T) { + // Create and change directory to test workspace + _, cleanUp := coreTests.CreateTestWorkspace(t, filepath.Join("..", "..", "tests", "testdata", "maven-example-with-many-types")) + defer cleanUp() + tree, uniqueDeps, err := buildMavenDependencyTree(&DepTreeParams{}) + require.NoError(t, err) + // dependency of pom type + depWithPomType := uniqueDeps["gav://org.webjars:lodash:4.17.21"] + assert.NotEmpty(t, depWithPomType) + assert.Equal(t, depWithPomType[0], "pom") + existInTreePom := false + for _, node := range tree[0].Nodes { + if node.Id == "gav://org.webjars:lodash:4.17.21" { + nodeTypes := *node.Types + assert.Equal(t, nodeTypes[0], "pom") + existInTreePom = true + } + } + assert.True(t, existInTreePom) + + // dependency of jar type + depWithJarType := uniqueDeps["gav://junit:junit:4.11"] + assert.NotEmpty(t, depWithJarType) + assert.Equal(t, depWithJarType[0], "jar") + existInTreeJar := false + for _, node := range tree[0].Nodes { + if node.Id == "gav://junit:junit:4.11" { + nodeTypes := *node.Types + assert.Equal(t, nodeTypes[0], "jar") + existInTreeJar = true + } + } + assert.True(t, existInTreeJar) +} + +func TestDepTreeWithDedicatedCache(t *testing.T) { + // Create and change directory to test workspace + _, cleanUp := coreTests.CreateTestWorkspace(t, filepath.Join("..", "..", "tests", "testdata", "maven-example-with-wrapper")) + err := os.Chmod("mvnw", 0700) + defer cleanUp() + assert.NoError(t, err) + tempDir := t.TempDir() + defer assert.NoError(t, utils.RemoveTempDir(tempDir)) + manager := NewMavenDepTreeManager(&DepTreeParams{IsCurationCmd: true, CurationCacheFolder: tempDir}, Tree) + _, err = manager.runTreeCmd(tempDir) + require.NoError(t, err) + // validate one of the jars exist in the dedicated cache for curation + fileExist, err := utils.IsFileExists(filepath.Join(tempDir, "org/slf4j/slf4j-api/1.7.36/slf4j-api-1.7.36.jar"), false) + require.NoError(t, err) + assert.True(t, fileExist) +} + func TestGetMavenPluginInstallationArgs(t *testing.T) { args := GetMavenPluginInstallationGoals("testPlugin") assert.Equal(t, "org.apache.maven.plugins:maven-install-plugin:3.1.1:install-file", args[0]) @@ -196,6 +271,16 @@ func TestCreateSettingsXmlWithConfiguredArtifactory(t *testing.T) { assert.NoError(t, err) assert.Equal(t, settingsXmlWithUsernameAndPassword, string(actualContent)) + // check curation command write a dedicated api for curation. + mdt.isCurationCmd = true + err = mdt.createSettingsXmlWithConfiguredArtifactory(tempDir) + require.NoError(t, err) + actualContent, err = os.ReadFile(settingsXmlPath) + actualContent = []byte(strings.ReplaceAll(string(actualContent), "\r\n", "\n")) + assert.NoError(t, err) + assert.Equal(t, settingsXmlWithUsernameAndPasswordAndCurationDedicatedAPi, string(actualContent)) + mdt.isCurationCmd = false + mdt.server.Password = "" // jfrog-ignore mdt.server.AccessToken = dummyToken @@ -223,7 +308,7 @@ func TestRunProjectsCmd(t *testing.T) { // Create and change directory to test workspace _, cleanUp := coreTests.CreateTestWorkspace(t, filepath.Join("..", "..", "tests", "testdata", "maven-example")) defer cleanUp() - mvnDepTreeManager := NewMavenDepTreeManager(&DepTreeParams{}, Projects, false) + mvnDepTreeManager := NewMavenDepTreeManager(&DepTreeParams{}, Projects) output, clearMavenDepTreeRun, err := mvnDepTreeManager.RunMavenDepTree() assert.NoError(t, err) assert.NotNil(t, clearMavenDepTreeRun) diff --git a/utils/lock/lock.go b/utils/lock/lock.go index 0e8358809..57c5281af 100644 --- a/utils/lock/lock.go +++ b/utils/lock/lock.go @@ -10,6 +10,7 @@ import ( "strings" "time" + "github.com/jfrog/jfrog-cli-core/v2/utils/osutils" "github.com/jfrog/jfrog-client-go/utils/errorutils" "github.com/jfrog/jfrog-client-go/utils/io/fileutils" "github.com/jfrog/jfrog-client-go/utils/log" @@ -129,9 +130,9 @@ func (lock *Lock) removeOtherLockOrWait(otherLock Lock, filesList *[]string) err log.Debug("Lock hasn't been acquired.") // Check if the process is running. - // There are two implementation of the 'isProcessRunning'. + // There are two implementation of the 'IsProcessRunning'. // One for Windows and one for Unix based systems. - running, err := isProcessRunning(otherLock.pid) + running, err := osutils.IsProcessRunning(otherLock.pid) if err != nil { return err } @@ -241,7 +242,7 @@ func GetLastLockTimestamp(lockDirPath string) (int64, error) { lastLock := locks[len(locks)-1] // If the lock isn't acquired by a running process, an unexpected error was occurred. - running, err := isProcessRunning(lastLock.pid) + running, err := osutils.IsProcessRunning(lastLock.pid) if err != nil { return 0, err } diff --git a/utils/lock/utils_unix.go b/utils/osutils/utils_unix.go similarity index 93% rename from utils/lock/utils_unix.go rename to utils/osutils/utils_unix.go index 476ac23ce..c70c907e5 100644 --- a/utils/lock/utils_unix.go +++ b/utils/osutils/utils_unix.go @@ -1,19 +1,20 @@ //go:build linux || darwin || freebsd // +build linux darwin freebsd -package lock +package osutils import ( - "github.com/jfrog/jfrog-client-go/utils/log" "os" "syscall" + + "github.com/jfrog/jfrog-client-go/utils/log" ) // This file will be compiled only on unix systems. // Checks if the process is running. // If error occurs, check if the error is part of the OS permission errors. This means the process is running. // Else means the process is not running. -func isProcessRunning(pid int) (bool, error) { +func IsProcessRunning(pid int) (bool, error) { process, err := os.FindProcess(pid) if err != nil { return false, err diff --git a/utils/lock/utils_windows.go b/utils/osutils/utils_windows.go similarity index 93% rename from utils/lock/utils_windows.go rename to utils/osutils/utils_windows.go index 504d1c97f..2711fcd1d 100644 --- a/utils/lock/utils_windows.go +++ b/utils/osutils/utils_windows.go @@ -1,15 +1,16 @@ -package lock +package osutils import ( - "github.com/jfrog/jfrog-client-go/utils/errorutils" "syscall" + + "github.com/jfrog/jfrog-client-go/utils/errorutils" ) // This file will be compiled on Windows. // Checks if the process can be reached. // If an error occurs, check if the error is part of the invalid parameter. This means the process is not running. // Else find the exit code. If the exit code 259 means the process is running. -func isProcessRunning(pid int) (bool, error) { +func IsProcessRunning(pid int) (bool, error) { process, err := syscall.OpenProcess(syscall.PROCESS_QUERY_INFORMATION, true, uint32(pid)) if err != nil { // Check if err is of type of syscall.Errno, which is a Windows error number. diff --git a/utils/xray/xrayutils.go b/utils/xray/xrayutils.go index 7f34d82a6..fc2f0f928 100644 --- a/utils/xray/xrayutils.go +++ b/utils/xray/xrayutils.go @@ -5,7 +5,6 @@ import ( clientconfig "github.com/jfrog/jfrog-client-go/config" "github.com/jfrog/jfrog-client-go/xray" xrayUtils "github.com/jfrog/jfrog-client-go/xray/services/utils" - "golang.org/x/exp/maps" ) func CreateXrayServiceManager(serviceDetails *config.ServerDetails) (*xray.XrayServicesManager, error) { @@ -36,26 +35,46 @@ func CreateXrayServiceManagerAndGetVersion(serviceDetails *config.ServerDetails) const maxUniqueAppearances = 10 -func BuildXrayDependencyTree(treeHelper map[string][]string, nodeId string) (*xrayUtils.GraphNode, []string) { +type DepTreeNode struct { + Types *[]string `json:"types"` + Children []string `json:"children"` +} + +func toNodeTypesMap(depMap map[string]DepTreeNode) map[string][]string { + mapOfTypes := map[string][]string{} + for nodId, value := range depMap { + mapOfTypes[nodId] = nil + if value.Types != nil { + mapOfTypes[nodId] = *value.Types + } + } + return mapOfTypes +} + +func BuildXrayDependencyTree(treeHelper map[string]DepTreeNode, nodeId string) (*xrayUtils.GraphNode, map[string][]string) { rootNode := &xrayUtils.GraphNode{ Id: nodeId, Nodes: []*xrayUtils.GraphNode{}, } dependencyAppearances := map[string]int8{} - populateXrayDependencyTree(rootNode, treeHelper, &dependencyAppearances) - return rootNode, maps.Keys(dependencyAppearances) + populateXrayDependencyTree(rootNode, treeHelper, dependencyAppearances) + return rootNode, toNodeTypesMap(treeHelper) } -func populateXrayDependencyTree(currNode *xrayUtils.GraphNode, treeHelper map[string][]string, dependencyAppearances *map[string]int8) { - (*dependencyAppearances)[currNode.Id]++ +func populateXrayDependencyTree(currNode *xrayUtils.GraphNode, treeHelper map[string]DepTreeNode, dependencyAppearances map[string]int8) { + dependencyAppearances[currNode.Id]++ + if _, ok := treeHelper[currNode.Id]; !ok { + treeHelper[currNode.Id] = DepTreeNode{} + } // Recursively create & append all node's dependencies. - for _, childDepId := range treeHelper[currNode.Id] { + for _, childDepId := range treeHelper[currNode.Id].Children { childNode := &xrayUtils.GraphNode{ Id: childDepId, Nodes: []*xrayUtils.GraphNode{}, Parent: currNode, + Types: treeHelper[childDepId].Types, } - if (*dependencyAppearances)[childDepId] >= maxUniqueAppearances || childNode.NodeHasLoop() { + if dependencyAppearances[childDepId] >= maxUniqueAppearances || childNode.NodeHasLoop() { continue } currNode.Nodes = append(currNode.Nodes, childNode)