From a2bfc899b76d5b5592857cdd01b4c68e80f5f818 Mon Sep 17 00:00:00 2001 From: ghe Date: Wed, 5 Aug 2020 16:44:54 +0100 Subject: [PATCH] feat: scan gradle projects behind --all-projects --- .gitignore | 1 + .../formatters/format-monitor-response.ts | 4 +- src/cli/commands/monitor/index.ts | 15 ++- src/cli/commands/test/index.ts | 1 + src/lib/detect.ts | 1 + src/lib/find-files.ts | 43 ++++++-- src/lib/plugins/get-deps-from-plugin.ts | 2 + .../cli-monitor.all-projects.spec.ts | 102 ++++++++++++++++++ .../cli-test/cli-test.acceptance.test.ts | 2 +- .../cli-test/cli-test.all-projects.spec.ts | 43 +++----- .../workspaces/gradle-monorepo/build.gradle | 0 .../gradle-monorepo/package-lock.json | 18 ++++ .../workspaces/gradle-monorepo/package.json | 14 +++ .../gradle-monorepo/settings.gradle | 5 + .../workspaces/gradle-monorepo/subproj/.snyk | 9 ++ .../gradle-monorepo/subproj/build.gradle | 51 +++++++++ .../large-mono-repo/gradle-app/build.gradle | 1 + .../gradle-multi-project/build.gradle | 0 .../gradle-multi-project/settings.gradle | 5 + .../gradle-multi-project/subproj/.snyk | 9 ++ .../gradle-multi-project/subproj/build.gradle | 51 +++++++++ test/find-files.test.ts | 10 +- .../gradle-another/build.gradle | 0 .../gradle-another/subproject/build.gradle | 0 .../gradle-multiple/gradle/build.gradle | 0 .../gradle/subproject/build.gradle | 0 26 files changed, 340 insertions(+), 47 deletions(-) create mode 100644 test/acceptance/workspaces/gradle-monorepo/build.gradle create mode 100644 test/acceptance/workspaces/gradle-monorepo/package-lock.json create mode 100644 test/acceptance/workspaces/gradle-monorepo/package.json create mode 100644 test/acceptance/workspaces/gradle-monorepo/settings.gradle create mode 100644 test/acceptance/workspaces/gradle-monorepo/subproj/.snyk create mode 100644 test/acceptance/workspaces/gradle-monorepo/subproj/build.gradle create mode 100644 test/acceptance/workspaces/large-mono-repo/gradle-app/build.gradle create mode 100644 test/acceptance/workspaces/large-mono-repo/gradle-multi-project/build.gradle create mode 100644 test/acceptance/workspaces/large-mono-repo/gradle-multi-project/settings.gradle create mode 100644 test/acceptance/workspaces/large-mono-repo/gradle-multi-project/subproj/.snyk create mode 100644 test/acceptance/workspaces/large-mono-repo/gradle-multi-project/subproj/build.gradle create mode 100644 test/fixtures/find-files/gradle-multiple/gradle-another/build.gradle create mode 100644 test/fixtures/find-files/gradle-multiple/gradle-another/subproject/build.gradle create mode 100644 test/fixtures/find-files/gradle-multiple/gradle/build.gradle create mode 100644 test/fixtures/find-files/gradle-multiple/gradle/subproject/build.gradle diff --git a/.gitignore b/.gitignore index bc9649b29f..e381c7a004 100644 --- a/.gitignore +++ b/.gitignore @@ -23,4 +23,5 @@ report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json # test fixture build artifacts /test/acceptance/workspaces/**/project/ /test/acceptance/workspaces/**/target/ +test/acceptance/workspaces/**/.gradle .vscode diff --git a/src/cli/commands/monitor/formatters/format-monitor-response.ts b/src/cli/commands/monitor/formatters/format-monitor-response.ts index 0fb49ef0e5..525ba2bdab 100644 --- a/src/cli/commands/monitor/formatters/format-monitor-response.ts +++ b/src/cli/commands/monitor/formatters/format-monitor-response.ts @@ -14,7 +14,9 @@ export function formatMonitorOutput( ): string { const manageUrl = buildManageUrl(res.id, res.org); const advertiseGradleSubProjectsCount = - packageManager === 'gradle' && !options['gradle-sub-project']; + packageManager === 'gradle' && + !options['gradle-sub-project'] && + !options.allProjects; const advertiseAllProjectsCount = packageManager !== 'gradle' && !options.allProjects && foundProjectCount; const issues = res.licensesPolicy ? 'issues' : 'vulnerabilities'; diff --git a/src/cli/commands/monitor/index.ts b/src/cli/commands/monitor/index.ts index e1aaf79ecd..feb0fbd2af 100644 --- a/src/cli/commands/monitor/index.ts +++ b/src/cli/commands/monitor/index.ts @@ -213,18 +213,15 @@ async function monitor(...args0: MethodArgs): Promise { analytics.add('packageManager', extractedPackageManager); - let projectName; - + const projectName = getProjectName(projectDeps); if (projectDeps.depGraph) { - debug(`Processing ${projectDeps.depGraph.rootPkg.name}...`); + debug(`Processing ${projectDeps.depGraph.rootPkg?.name}...`); maybePrintDepGraph(options, projectDeps.depGraph); - projectName = projectDeps.depGraph.rootPkg.name; } if (projectDeps.depTree) { debug(`Processing ${projectDeps.depTree.name}...`); maybePrintDepTree(options, projectDeps.depTree); - projectName = projectDeps.depTree.name; } const tFile = projectDeps.targetFile || targetFile; @@ -319,3 +316,11 @@ function validateMonitorPath(path: string, isDocker?: boolean): void { throw new Error('"' + path + '" is not a valid path for "snyk monitor"'); } } + +function getProjectName(projectDeps): string { + return ( + projectDeps.meta?.gradleProjectName || + projectDeps.depGraph?.rootPkg?.name || + projectDeps.depTree?.name + ); +} diff --git a/src/cli/commands/test/index.ts b/src/cli/commands/test/index.ts index 131ef7b116..cb2f239719 100644 --- a/src/cli/commands/test/index.ts +++ b/src/cli/commands/test/index.ts @@ -434,6 +434,7 @@ function displayResult( const advertiseGradleSubProjectsCount = projectType === 'gradle' && !options['gradle-sub-project'] && + !options.allProjects && foundProjectCount; if (advertiseGradleSubProjectsCount) { multiProjAdvice = chalk.bold.white( diff --git a/src/lib/detect.ts b/src/lib/detect.ts index 187d4cc162..65260c0f1a 100644 --- a/src/lib/detect.ts +++ b/src/lib/detect.ts @@ -52,6 +52,7 @@ export const AUTO_DETECTABLE_FILES: string[] = [ 'Pipfile', 'requirements.txt', 'build.sbt', + 'build.gradle', ]; // when file is specified with --file, we look it up here diff --git a/src/lib/find-files.ts b/src/lib/find-files.ts index 8ae5280075..810a00c0a1 100644 --- a/src/lib/find-files.ts +++ b/src/lib/find-files.ts @@ -113,7 +113,7 @@ async function findInDirectory( return Array.prototype.concat.apply([], found); } -function filterForDefaultManifests(files: string[]) { +function filterForDefaultManifests(files: string[]): string[] { // take all the files in the same dir & filter out // based on package Manager if (files.length <= 1) { @@ -129,23 +129,28 @@ function filterForDefaultManifests(files: string[]) { ...pathLib.parse(p), packageManager: detectProjectTypeFromFile(p), })) + .sortBy('dir') .groupBy('dir') .value(); for (const directory of Object.keys(foundFiles)) { const filesInDirectory = foundFiles[directory]; - if (filesInDirectory.length <= 1) { - filteredFiles.push(filesInDirectory[0].path); - continue; - } - const groupedFiles = _(filesInDirectory) .groupBy('packageManager') .value(); for (const packageManager of Object.keys(groupedFiles)) { const filesPerPackageManager = groupedFiles[packageManager]; + if (filesPerPackageManager.length <= 1) { + const shouldSkip = shouldSkipAddingFile( + packageManager, + filesPerPackageManager[0].path, + filteredFiles, + ); + if (shouldSkip) { + continue; + } filteredFiles.push(filesPerPackageManager[0].path); continue; } @@ -154,6 +159,14 @@ function filterForDefaultManifests(files: string[]) { packageManager, ); if (defaultManifestFileName) { + const shouldSkip = shouldSkipAddingFile( + packageManager, + filesPerPackageManager[0].path, + filteredFiles, + ); + if (shouldSkip) { + continue; + } filteredFiles.push(defaultManifestFileName); } } @@ -173,6 +186,24 @@ function detectProjectTypeFromFile(file: string): string | null { } } +function shouldSkipAddingFile( + packageManager: string, + filePath: string, + filteredFiles: string[], +): boolean { + if (['gradle'].includes(packageManager) && filePath) { + const rootGradleFile = filteredFiles + .filter((targetFile) => targetFile.endsWith('build.gradle')) + .filter((targetFile) => { + const parsedPath = pathLib.parse(targetFile); + const relativePath = pathLib.relative(parsedPath.dir, filePath); + return !relativePath.startsWith(`..${pathLib.sep}`); + }); + return !!rootGradleFile.length; + } + return false; +} + function chooseBestManifest( files: Array<{ base: string; path: string }>, projectType: string, diff --git a/src/lib/plugins/get-deps-from-plugin.ts b/src/lib/plugins/get-deps-from-plugin.ts index b32e03c55e..4fd4117e65 100644 --- a/src/lib/plugins/get-deps-from-plugin.ts +++ b/src/lib/plugins/get-deps-from-plugin.ts @@ -55,6 +55,8 @@ export async function getDepsFromPlugin( if (targetFiles.length === 0) { throw NoSupportedManifestsFoundError([root]); } + // enable full sub-project scan for gradle + options.allSubProjects = true; inspectRes = await multiProjectProcessors[scanType].handler( root, options, diff --git a/test/acceptance/cli-monitor/cli-monitor.all-projects.spec.ts b/test/acceptance/cli-monitor/cli-monitor.all-projects.spec.ts index fe6d9f5049..97603ca348 100644 --- a/test/acceptance/cli-monitor/cli-monitor.all-projects.spec.ts +++ b/test/acceptance/cli-monitor/cli-monitor.all-projects.spec.ts @@ -1,6 +1,7 @@ import * as sinon from 'sinon'; import * as _ from '@snyk/lodash'; import * as path from 'path'; +import * as depGraphLib from '@snyk/dep-graph'; interface AcceptanceTests { language: string; @@ -740,5 +741,106 @@ export const AllProjectsTests: AcceptanceTests = { ); }); }, + '`monitor gradle-monorepo with --all-projects`': (params, utils) => async ( + t, + ) => { + utils.chdirWorkspaces(); + const simpleGradleGraph = depGraphLib.createFromJSON({ + schemaVersion: '1.2.0', + pkgManager: { + name: 'gradle', + }, + pkgs: [ + { + id: 'gradle-monorepo@0.0.0', + info: { + name: 'gradle-monorepo', + version: '0.0.0', + }, + }, + ], + graph: { + rootNodeId: 'root-node', + nodes: [ + { + nodeId: 'root-node', + pkgId: 'gradle-monorepo@0.0.0', + deps: [], + }, + ], + }, + }); + const plugin = { + async inspect() { + return { + plugin: { + name: 'bundled:gradle', + runtime: 'unknown', + meta: {}, + }, + scannedProjects: [ + { + meta: { + gradleProjectName: 'root-proj', + versionBuildInfo: { + gradleVersion: '6.5', + }, + }, + depGraph: simpleGradleGraph, + }, + { + meta: { + gradleProjectName: 'root-proj/subproj', + versionBuildInfo: { + gradleVersion: '6.5', + }, + }, + depGraph: simpleGradleGraph, + }, + ], + }; + }, + }; + const spyPlugin = sinon.spy(plugin, 'inspect'); + const loadPlugin = sinon.stub(params.plugins, 'loadPlugin'); + t.teardown(loadPlugin.restore); + loadPlugin.withArgs('gradle').returns(plugin); + loadPlugin.callThrough(); + const result = await params.cli.monitor('gradle-monorepo', { + allProjects: true, + detectionDepth: 3, + d: true, + }); + t.match( + result, + 'gradle/graph/some/project-id', + 'gradle project was monitored', + ); + t.match( + result, + 'npm/graph/some/project-id', + 'gradle project was monitored', + ); + + // Pop one extra call to server and filter out call to `featureFlag` endpoint + const requests = params.server + .popRequests(4) + .filter((req) => req.url.includes('/monitor/')); + t.equal(requests.length, 3, 'correct amount of monitor requests'); + requests.forEach((req) => { + t.match( + req.url, + /\/api\/v1\/monitor\/(npm\/graph|gradle\/graph)/, + 'puts at correct url', + ); + t.notOk(req.body.targetFile, "doesn't send the targetFile"); + t.equal(req.method, 'PUT', 'makes PUT request'); + t.equal( + req.headers['x-snyk-cli-version'], + params.versionNumber, + 'sends version number', + ); + }); + }, }, }; diff --git a/test/acceptance/cli-test/cli-test.acceptance.test.ts b/test/acceptance/cli-test/cli-test.acceptance.test.ts index 48a6e126f9..ee5ef061c1 100644 --- a/test/acceptance/cli-test/cli-test.acceptance.test.ts +++ b/test/acceptance/cli-test/cli-test.acceptance.test.ts @@ -150,7 +150,7 @@ if (!isWindows) { } }); - // @later: try and remove this config stuff + // TODO: try and remove this config stuff // Was copied straight from ../src/cli-server.js after('teardown', async (t) => { t.plan(4); diff --git a/test/acceptance/cli-test/cli-test.all-projects.spec.ts b/test/acceptance/cli-test/cli-test.all-projects.spec.ts index d610fcfd85..6a71a1372f 100644 --- a/test/acceptance/cli-test/cli-test.all-projects.spec.ts +++ b/test/acceptance/cli-test/cli-test.all-projects.spec.ts @@ -547,34 +547,6 @@ export const AllProjectsTests: AcceptanceTests = { ); }, - '`test large-mono-repo with --all-projects and --detection-depth=2`': ( - params, - utils, - ) => async (t) => { - utils.chdirWorkspaces(); - const spyPlugin = sinon.spy(params.plugins, 'loadPlugin'); - t.teardown(spyPlugin.restore); - await params.cli.test('large-mono-repo', { - allProjects: true, - detectionDepth: 2, - }); - t.equals( - spyPlugin.withArgs('rubygems').callCount, - 1, - 'calls rubygems plugin once', - ); - t.equals( - spyPlugin.withArgs('npm').callCount, - 19, - 'calls npm plugin 19 times', - ); - t.equals( - spyPlugin.withArgs('maven').callCount, - 1, - 'calls maven plugin once', - ); - }, - '`test large-mono-repo with --all-projects and --detection-depth=7`': ( params, utils, @@ -596,6 +568,16 @@ export const AllProjectsTests: AcceptanceTests = { 19, 'calls npm plugin 19 times', ); + t.equals( + spyPlugin.withArgs('gradle').callCount, + 2, + 'calls gradle plugin 2 times', + ); + t.equals( + spyPlugin.withArgs('gradle').args[0][1].allSubProjects, + true, + 'calls gradle plugin with allSubProjects property', + ); t.equals( spyPlugin.withArgs('maven').callCount, 6, @@ -742,6 +724,11 @@ export const AllProjectsTests: AcceptanceTests = { 19, 'calls npm plugin 19 times', ); + t.equals( + spyPlugin.withArgs('gradle').callCount, + 2, + 'calls gradle plugin 2 times', + ); t.equals( spyPlugin.withArgs('maven').callCount, 1, diff --git a/test/acceptance/workspaces/gradle-monorepo/build.gradle b/test/acceptance/workspaces/gradle-monorepo/build.gradle new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test/acceptance/workspaces/gradle-monorepo/package-lock.json b/test/acceptance/workspaces/gradle-monorepo/package-lock.json new file mode 100644 index 0000000000..fce7bf7f28 --- /dev/null +++ b/test/acceptance/workspaces/gradle-monorepo/package-lock.json @@ -0,0 +1,18 @@ +{ + "name": "shallow-goof", + "version": "0.0.1", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "node-uuid": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.0.tgz", + "integrity": "sha1-B/myM3Vy/2J1x3Xh1IUT86RdemU=" + }, + "qs": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/qs/-/qs-0.0.6.tgz", + "integrity": "sha1-SBZZt+W/al6omAEN5a7TXrRp4SQ=" + } + } +} diff --git a/test/acceptance/workspaces/gradle-monorepo/package.json b/test/acceptance/workspaces/gradle-monorepo/package.json new file mode 100644 index 0000000000..4b2ecd8d3d --- /dev/null +++ b/test/acceptance/workspaces/gradle-monorepo/package.json @@ -0,0 +1,14 @@ +{ + "name": "shallow-goof", + "version": "0.0.1", + "description": "A vulnerable demo application", + "homepage": "https://snyk.io/", + "repository": { + "type": "git", + "url": "https://github.com/Snyk/shallow-goof" + }, + "dependencies": { + "qs": "0.0.6", + "node-uuid": "1.4.0" + } +} diff --git a/test/acceptance/workspaces/gradle-monorepo/settings.gradle b/test/acceptance/workspaces/gradle-monorepo/settings.gradle new file mode 100644 index 0000000000..90289e8a32 --- /dev/null +++ b/test/acceptance/workspaces/gradle-monorepo/settings.gradle @@ -0,0 +1,5 @@ +rootProject.name = 'root-proj' + +include 'subproj' + + diff --git a/test/acceptance/workspaces/gradle-monorepo/subproj/.snyk b/test/acceptance/workspaces/gradle-monorepo/subproj/.snyk new file mode 100644 index 0000000000..d46ea24efa --- /dev/null +++ b/test/acceptance/workspaces/gradle-monorepo/subproj/.snyk @@ -0,0 +1,9 @@ +# Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities. +version: v1.14.1 +# ignores vulnerabilities until expiry date; change duration by modifying expiry date +ignore: + SNYK-JAVA-ORGBOUNCYCASTLE-32364: + - '*': + reason: None Given + expires: 2020-07-19T11:33:19.028Z +patch: {} diff --git a/test/acceptance/workspaces/gradle-monorepo/subproj/build.gradle b/test/acceptance/workspaces/gradle-monorepo/subproj/build.gradle new file mode 100644 index 0000000000..301e7be03b --- /dev/null +++ b/test/acceptance/workspaces/gradle-monorepo/subproj/build.gradle @@ -0,0 +1,51 @@ +apply plugin: 'java' +apply plugin: 'maven' + +group = 'com.github.jitpack' + +sourceCompatibility = 1.8 // java 8 +targetCompatibility = 1.8 + +repositories { + mavenCentral() +} + +dependencies { + // Gradle 3+ will not pick up "compile" dependencies for "compileOnly" + // Gradle 2 will, so for configuration-matching tests we use "runtime" + runtime 'com.google.guava:guava:18.0' + runtime 'batik:batik-dom:1.6' + runtime 'commons-discovery:commons-discovery:0.2' + compileOnly 'axis:axis:1.3' + runtime 'com.android.tools.build:builder:2.3.0' +} + +task sourcesJar(type: Jar, dependsOn: classes) { + classifier = 'sources' + from sourceSets.main.allSource +} + +task javadocJar(type: Jar, dependsOn: javadoc) { + classifier = 'javadoc' + from javadoc.destinationDir +} + +artifacts { + archives sourcesJar + archives javadocJar +} + +// To specify a license in the pom: +install { + repositories.mavenInstaller { + pom.project { + licenses { + license { + name 'The Apache Software License, Version 2.0' + url 'http://www.apache.org/licenses/LICENSE-2.0.txt' + distribution 'repo' + } + } + } + } +} diff --git a/test/acceptance/workspaces/large-mono-repo/gradle-app/build.gradle b/test/acceptance/workspaces/large-mono-repo/gradle-app/build.gradle new file mode 100644 index 0000000000..53f261f8b7 --- /dev/null +++ b/test/acceptance/workspaces/large-mono-repo/gradle-app/build.gradle @@ -0,0 +1 @@ +// not a real Gradle build file diff --git a/test/acceptance/workspaces/large-mono-repo/gradle-multi-project/build.gradle b/test/acceptance/workspaces/large-mono-repo/gradle-multi-project/build.gradle new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test/acceptance/workspaces/large-mono-repo/gradle-multi-project/settings.gradle b/test/acceptance/workspaces/large-mono-repo/gradle-multi-project/settings.gradle new file mode 100644 index 0000000000..90289e8a32 --- /dev/null +++ b/test/acceptance/workspaces/large-mono-repo/gradle-multi-project/settings.gradle @@ -0,0 +1,5 @@ +rootProject.name = 'root-proj' + +include 'subproj' + + diff --git a/test/acceptance/workspaces/large-mono-repo/gradle-multi-project/subproj/.snyk b/test/acceptance/workspaces/large-mono-repo/gradle-multi-project/subproj/.snyk new file mode 100644 index 0000000000..d46ea24efa --- /dev/null +++ b/test/acceptance/workspaces/large-mono-repo/gradle-multi-project/subproj/.snyk @@ -0,0 +1,9 @@ +# Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities. +version: v1.14.1 +# ignores vulnerabilities until expiry date; change duration by modifying expiry date +ignore: + SNYK-JAVA-ORGBOUNCYCASTLE-32364: + - '*': + reason: None Given + expires: 2020-07-19T11:33:19.028Z +patch: {} diff --git a/test/acceptance/workspaces/large-mono-repo/gradle-multi-project/subproj/build.gradle b/test/acceptance/workspaces/large-mono-repo/gradle-multi-project/subproj/build.gradle new file mode 100644 index 0000000000..301e7be03b --- /dev/null +++ b/test/acceptance/workspaces/large-mono-repo/gradle-multi-project/subproj/build.gradle @@ -0,0 +1,51 @@ +apply plugin: 'java' +apply plugin: 'maven' + +group = 'com.github.jitpack' + +sourceCompatibility = 1.8 // java 8 +targetCompatibility = 1.8 + +repositories { + mavenCentral() +} + +dependencies { + // Gradle 3+ will not pick up "compile" dependencies for "compileOnly" + // Gradle 2 will, so for configuration-matching tests we use "runtime" + runtime 'com.google.guava:guava:18.0' + runtime 'batik:batik-dom:1.6' + runtime 'commons-discovery:commons-discovery:0.2' + compileOnly 'axis:axis:1.3' + runtime 'com.android.tools.build:builder:2.3.0' +} + +task sourcesJar(type: Jar, dependsOn: classes) { + classifier = 'sources' + from sourceSets.main.allSource +} + +task javadocJar(type: Jar, dependsOn: javadoc) { + classifier = 'javadoc' + from javadoc.destinationDir +} + +artifacts { + archives sourcesJar + archives javadocJar +} + +// To specify a license in the pom: +install { + repositories.mavenInstaller { + pom.project { + licenses { + license { + name 'The Apache Software License, Version 2.0' + url 'http://www.apache.org/licenses/LICENSE-2.0.txt' + distribution 'repo' + } + } + } + } +} diff --git a/test/find-files.test.ts b/test/find-files.test.ts index fff035eeb1..fc5c3de70c 100644 --- a/test/find-files.test.ts +++ b/test/find-files.test.ts @@ -20,7 +20,8 @@ test('find all files in test fixture', async (t) => { path.join(testFixture, 'golang', 'golang-app', 'Gopkg.toml'), path.join(testFixture, 'golang', 'golang-gomodules', 'go.mod'), path.join(testFixture, 'gradle', 'build.gradle'), - path.join(testFixture, 'gradle', 'subproject', 'build.gradle'), + path.join(testFixture, 'gradle-multiple', 'gradle/build.gradle'), + path.join(testFixture, 'gradle-multiple', 'gradle-another/build.gradle'), path.join(testFixture, 'maven', 'pom.xml'), path.join(testFixture, 'maven', 'test.txt'), path.join(testFixture, 'npm-with-lockfile', 'package-lock.json'), @@ -51,7 +52,8 @@ test('find all files in test fixture ignoring node_modules', async (t) => { path.join(testFixture, 'golang', 'golang-app', 'Gopkg.toml'), path.join(testFixture, 'golang', 'golang-gomodules', 'go.mod'), path.join(testFixture, 'gradle', 'build.gradle'), - path.join(testFixture, 'gradle', 'subproject', 'build.gradle'), + path.join(testFixture, 'gradle-multiple', 'gradle/build.gradle'), + path.join(testFixture, 'gradle-multiple', 'gradle-another/build.gradle'), path.join(testFixture, 'maven', 'pom.xml'), path.join(testFixture, 'maven', 'test.txt'), path.join(testFixture, 'mvn', 'pom.xml'), @@ -150,10 +152,6 @@ test('find pom.xml files in test fixture', async (t) => { t.same(result.sort(), expected, 'should return expected files'); }); -test('find build.gradle, but stop at first build.gradle found', async (t) => { - t.todo('stop recursion for given file names'); -}); - test('find path that does not exist', async (t) => { try { await find('does-not-exist'); diff --git a/test/fixtures/find-files/gradle-multiple/gradle-another/build.gradle b/test/fixtures/find-files/gradle-multiple/gradle-another/build.gradle new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test/fixtures/find-files/gradle-multiple/gradle-another/subproject/build.gradle b/test/fixtures/find-files/gradle-multiple/gradle-another/subproject/build.gradle new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test/fixtures/find-files/gradle-multiple/gradle/build.gradle b/test/fixtures/find-files/gradle-multiple/gradle/build.gradle new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test/fixtures/find-files/gradle-multiple/gradle/subproject/build.gradle b/test/fixtures/find-files/gradle-multiple/gradle/subproject/build.gradle new file mode 100644 index 0000000000..e69de29bb2