diff --git a/.github/workflows/xrayTests.yml b/.github/workflows/xrayTests.yml index afb659a97..fdc8a691b 100644 --- a/.github/workflows/xrayTests.yml +++ b/.github/workflows/xrayTests.yml @@ -63,9 +63,9 @@ jobs: ref: ${{ github.event.pull_request.head.sha }} - name: Run Xray tests (without Docker Scan) - run: go test -v github.com/jfrog/jfrog-cli --timeout 0 --test.xray --jfrog.url=${{ secrets.PLATFORM_URL }} --jfrog.adminToken=${{ secrets.PLATFORM_ADMIN_TOKEN }} --jfrog.user=${{ secrets.PLATFORM_USER }} --test.containerRegistry=${{ secrets.CONTAINER_REGISTRY }} + run: go test -v github.com/jfrog/jfrog-cli --timeout 0 --test.xray --jfrog.url=${{ secrets.PLATFORM_URL }} --jfrog.adminToken=${{ secrets.PLATFORM_ADMIN_TOKEN }} --jfrog.user=${{ secrets.PLATFORM_USER }} --test.containerRegistry=${{ secrets.CONTAINER_REGISTRY }} --ci.runId=${{ runner.os }}-xray if: ${{ matrix.os != 'ubuntu' }} - name: Run Xray tests (with Docker Scan, only on Ubuntu) - run: go test -v github.com/jfrog/jfrog-cli --timeout 0 --test.xray --test.dockerScan --jfrog.url=${{ secrets.PLATFORM_URL }} --jfrog.adminToken=${{ secrets.PLATFORM_ADMIN_TOKEN }} --test.containerRegistry=${{ secrets.CONTAINER_REGISTRY }} + run: go test -v github.com/jfrog/jfrog-cli --timeout 0 --test.xray --test.dockerScan --jfrog.url=${{ secrets.PLATFORM_URL }} --jfrog.adminToken=${{ secrets.PLATFORM_ADMIN_TOKEN }} --test.containerRegistry=${{ secrets.CONTAINER_REGISTRY }} --ci.runId=${{ runner.os }}-xray if: ${{ matrix.os == 'ubuntu' }} diff --git a/testdata/go/simple-project/go.mod b/testdata/go/simple-project/go.mod new file mode 100644 index 000000000..309e9d797 --- /dev/null +++ b/testdata/go/simple-project/go.mod @@ -0,0 +1,9 @@ +module github.com/you/hello + +go 1.20 + +require rsc.io/quote v1.5.2 + +require ( + rsc.io/sampler v1.3.0 // indirect +) diff --git a/testdata/go/simple-project/go.sum b/testdata/go/simple-project/go.sum new file mode 100644 index 000000000..4a8bcd704 --- /dev/null +++ b/testdata/go/simple-project/go.sum @@ -0,0 +1,6 @@ +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c h1:qgOY6WgZOaTkIIMiVjBQcw93ERBE4m30iBm00nkL0i8= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +rsc.io/quote v1.5.2 h1:w5fcysjrx7yqtD/aO+QwRjYZOKnaM9Uh2b40tElTs3Y= +rsc.io/quote v1.5.2/go.mod h1:LzX7hefJvL54yjefDEDHNONDjII0t9xZLPXsUe+TKr0= +rsc.io/sampler v1.3.0 h1:7uVkIFmeBqHfdjD+gZwtXXI+RODJ2Wc4O7MPEh/QiW4= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/testdata/go/simple-project/hello.go b/testdata/go/simple-project/hello.go new file mode 100644 index 000000000..0a29866c4 --- /dev/null +++ b/testdata/go/simple-project/hello.go @@ -0,0 +1,11 @@ +package main + +import ( + "fmt" + + "rsc.io/quote" +) + +func main() { + fmt.Println(quote.Hello()) +} diff --git a/testdata/gradle/gradleproject/build.gradle b/testdata/gradle/gradleproject/build.gradle index e1b9e8fd3..eeac79246 100644 --- a/testdata/gradle/gradleproject/build.gradle +++ b/testdata/gradle/gradleproject/build.gradle @@ -1,6 +1,10 @@ apply plugin: 'groovy' apply plugin: 'idea' +dependencies { + implementation "junit:junit:4.7" +} + version = 1.0 task initProject(description: 'Initialize project directory structure.') { doLast { diff --git a/testdata/maven/mavenproject/pom.xml b/testdata/maven/mavenproject/pom.xml index 17859af3a..502b78fba 100644 --- a/testdata/maven/mavenproject/pom.xml +++ b/testdata/maven/mavenproject/pom.xml @@ -37,5 +37,11 @@ 4.11 test + + commons-io + commons-io + 1.2 + test + \ No newline at end of file diff --git a/testdata/poetry/projecttomlproject/hello.py b/testdata/poetry/projecttomlproject/hello.py new file mode 100644 index 000000000..e59e04407 --- /dev/null +++ b/testdata/poetry/projecttomlproject/hello.py @@ -0,0 +1,4 @@ +import numpy as np + +arr = np.array([1, 2, 3, 4, 5]) +print(arr) diff --git a/testdata/poetry/projecttomlproject/pyproject.toml b/testdata/poetry/projecttomlproject/pyproject.toml new file mode 100644 index 000000000..00c4eb5ae --- /dev/null +++ b/testdata/poetry/projecttomlproject/pyproject.toml @@ -0,0 +1,13 @@ +[tool.poetry] +name = "my-poetry-project" +version = "1.1.0" +description = "" +authors = ["Severus Snape "] + +[tool.poetry.dependencies] +python = "^3.10" +numpy = "^1.23.0" + +[build-system] +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" diff --git a/utils/tests/utils.go b/utils/tests/utils.go index 00d738513..bf276700c 100644 --- a/utils/tests/utils.go +++ b/utils/tests/utils.go @@ -398,7 +398,7 @@ func GetNonVirtualRepositories() map[*string]string { TestPip: {&PypiRemoteRepo}, TestPipenv: {&PipenvRemoteRepo}, TestPlugins: {&RtRepo1}, - TestXray: {&NpmRemoteRepo, &NugetRemoteRepo, &YarnRemoteRepo}, + TestXray: {&NpmRemoteRepo, &NugetRemoteRepo, &YarnRemoteRepo, &GradleRemoteRepo, &MvnRemoteRepo, &GoRepo, &GoRemoteRepo, &PypiRemoteRepo}, TestAccess: {&RtRepo1}, TestTransfer: {&RtRepo1, &RtRepo2, &MvnRepo1, &MvnRemoteRepo, &DockerRemoteRepo}, TestLifecycle: {&RtDevRepo, &RtProdRepo}, @@ -422,7 +422,7 @@ func GetVirtualRepositories() map[*string]string { TestPip: {&PypiVirtualRepo}, TestPipenv: {&PipenvVirtualRepo}, TestPlugins: {}, - TestXray: {}, + TestXray: {&GoVirtualRepo}, TestAccess: {}, } return getNeededRepositories(virtualReposMap) diff --git a/xray_test.go b/xray_test.go index 05182a09a..dc3935780 100644 --- a/xray_test.go +++ b/xray_test.go @@ -47,6 +47,13 @@ import ( "github.com/urfave/cli" ) +const ( + jvmLaunchEnvVar = "MAVEN_OPTS" + mavenCacheRedirectionVal = "-Dmaven.repo.local=" + goCacheEnvVar = "GOMODCACHE" + pipCacheEnvVar = "PIP_CACHE_DIR" +) + var ( xrayDetails *config.ServerDetails xrayAuth auth.ServiceDetails @@ -988,7 +995,45 @@ func TestDependencyResolutionFromArtifactory(t *testing.T) { cacheRepoName: tests.YarnRemoteRepo, projectType: artUtils.Yarn, }, + { + testProjectPath: []string{"gradle", "gradleproject"}, + resolveRepoName: tests.GradleRemoteRepo, + cacheRepoName: tests.GradleRemoteRepo, + projectType: artUtils.Gradle, + }, + { + testProjectPath: []string{"maven", "mavenproject"}, + resolveRepoName: tests.MvnRemoteRepo, + cacheRepoName: tests.MvnRemoteRepo, + projectType: artUtils.Maven, + }, + { + testProjectPath: []string{"go", "simple-project"}, + resolveRepoName: tests.GoVirtualRepo, + cacheRepoName: tests.GoRemoteRepo, + projectType: artUtils.Go, + }, + { + testProjectPath: []string{"pipenv", "pipenvproject"}, + resolveRepoName: tests.PypiRemoteRepo, + cacheRepoName: tests.PypiRemoteRepo, + projectType: artUtils.Pipenv, + }, + { + testProjectPath: []string{"pip", "setuppyproject"}, + resolveRepoName: tests.PypiRemoteRepo, + cacheRepoName: tests.PypiRemoteRepo, + projectType: artUtils.Pip, + }, + { + testProjectPath: []string{"poetry", "projecttomlproject"}, + resolveRepoName: tests.PypiRemoteRepo, + cacheRepoName: tests.PypiRemoteRepo, + projectType: artUtils.Poetry, + }, } + createJfrogHomeConfig(t, true) + defer cleanTestsHomeEnv() for _, testCase := range testCases { t.Run(testCase.projectType.String(), func(t *testing.T) { @@ -1008,27 +1053,71 @@ func testSingleTechDependencyResolution(t *testing.T, testProjectPartialPath []s defer func() { assert.NoError(t, os.Chdir(rootDir)) }() - createJfrogHomeConfig(t, true) - context := createContext(t, "repo-resolve="+resolveRepoName) + + server := &config.ServerDetails{ + Url: *tests.JfrogUrl, + ArtifactoryUrl: *tests.JfrogUrl + tests.ArtifactoryEndpoint, + XrayUrl: *tests.JfrogUrl + tests.XrayEndpoint, + AccessToken: *tests.JfrogAccessToken, + ServerId: tests.ServerId, + } + configCmd := coreCmd.NewConfigCommand(coreCmd.AddOrEdit, tests.ServerId).SetDetails(server).SetUseBasicAuthOnly(true).SetInteractive(false) + assert.NoError(t, configCmd.Run()) + + context := createContext(t, "repo-resolve="+resolveRepoName, "server-id-resolve="+server.ServerId) err = artCmdUtils.CreateBuildConfig(context, projectType) assert.NoError(t, err) artifactoryPathToSearch := cacheRepoName + "-cache/*" - output := artifactoryCli.RunCliCmdWithOutput(t, "s", artifactoryPathToSearch) - // Before the resolution from Artifactory, we verify whether the repository's cache is empty. - assert.Equal(t, "[]\n", output) - - if projectType == artUtils.Dotnet { - // In Nuget/Dotnet projects we need to clear local caches so we will resolve dependencies from Artifactory - _, err = exec.Command("dotnet", "nuget", "locals", "all", "--clear").CombinedOutput() - assert.NoError(t, err) + // To ensure a clean state between test cases, we need to verify that the cache remains clear for remote directories shared across multiple test cases. + assert.NoError(t, artifactoryCli.Exec("del", artifactoryPathToSearch)) + + callbackFunc := clearOrRedirectLocalCacheIfNeeded(t, projectType) + if callbackFunc != nil { + defer func() { + callbackFunc() + }() } - // We execute 'audit' command on a project that hasn't been installed. With the Artifactory server and repository configuration, our expectation is that dependencies will be resolved from there - assert.NoError(t, xrayCli.Exec("audit")) + // Executing the 'audit' command on an uninstalled project, we anticipate the resolution of dependencies from the configured Artifactory server and repository. + assert.NoError(t, xrayCli.WithoutCredentials().Exec("audit")) // Following resolution from Artifactory, we anticipate the repository's cache to contain data. - output = artifactoryCli.RunCliCmdWithOutput(t, "s", artifactoryPathToSearch, "--fail-no-op") + output := artifactoryCli.RunCliCmdWithOutput(t, "s", artifactoryPathToSearch, "--fail-no-op") // After the resolution from Artifactory, we verify whether the repository's cache is filled with artifacts. assert.NotEqual(t, "[]\n", output) } + +// To guarantee that dependencies are resolved from Artifactory, certain package managers may need their local cache to be cleared. +func clearOrRedirectLocalCacheIfNeeded(t *testing.T, projectType artUtils.ProjectType) (callbackFunc func()) { + switch projectType { + case artUtils.Dotnet: + _, err := exec.Command("dotnet", "nuget", "locals", "all", "--clear").CombinedOutput() + assert.NoError(t, err) + case artUtils.Maven: + mavenCacheTempPath, createTempDirCallback := coretests.CreateTempDirWithCallbackAndAssert(t) + envVarCallbackFunc := clientTestUtils.SetEnvWithCallbackAndAssert(t, jvmLaunchEnvVar, mavenCacheRedirectionVal+mavenCacheTempPath) + callbackFunc = func() { + envVarCallbackFunc() + createTempDirCallback() + } + case artUtils.Go: + goTempCachePath, createTempDirCallback := coretests.CreateTempDirWithCallbackAndAssert(t) + envVarCallbackFunc := clientTestUtils.SetEnvWithCallbackAndAssert(t, goCacheEnvVar, goTempCachePath) + + callbackFunc = func() { + envVarCallbackFunc() + // To remove the temporary cache in Go and all its contents, appropriate deletion permissions are required. + assert.NoError(t, coreutils.SetPermissionsRecursively(goTempCachePath, 0755)) + createTempDirCallback() + } + case artUtils.Pip: + pipTempCachePath, createTempDirCallback := coretests.CreateTempDirWithCallbackAndAssert(t) + envVarCallbackFunc := clientTestUtils.SetEnvWithCallbackAndAssert(t, pipCacheEnvVar, pipTempCachePath) + callbackFunc = func() { + envVarCallbackFunc() + createTempDirCallback() + } + } + return +}