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 e8ba2cf7f..05e407872 100644 --- a/artifactory/commands/repository/repository.go +++ b/artifactory/commands/repository/repository.go @@ -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 4f4a6f59f..f046b1041 100644 --- a/artifactory/commands/repository/template.go +++ b/artifactory/commands/repository/template.go @@ -159,6 +159,8 @@ const ( Conda = "conda" P2 = "p2" Swift = "swift" + Terraform = "terraform" + Cargo = "cargo" // Repo layout Refs BowerDefaultRepoLayout = "bower-default" @@ -856,9 +858,9 @@ var questionMap = map[string]ioutils.QuestionInfo{ Writer: ioutils.WriteStringAnswer, }, PrimaryKeyPairRef: ioutils.FreeStringQuestionInfo, - Username: ioutils.FreeStringQuestionInfo, - Password: ioutils.FreeStringQuestionInfo, - Proxy: 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 ea4a0a04d..0721ad476 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/jedib0t/go-pretty/v6 v6.5.4 github.com/jfrog/build-info-go v1.9.23 github.com/jfrog/gofrog v1.6.0 - github.com/jfrog/jfrog-client-go v1.36.0 + github.com/jfrog/jfrog-client-go v1.37.1 github.com/magiconair/properties v1.8.7 github.com/manifoldco/promptui v0.9.0 github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c diff --git a/go.sum b/go.sum index 30a9cb90f..246a69e77 100644 --- a/go.sum +++ b/go.sum @@ -71,16 +71,16 @@ 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.8.9-0.20240222124058-bd9687a8666e h1:NzB2yvEojIhP5KIX9SeCqSljZmoiE98hBzXYvvi52D0= 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/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/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.