Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
feat: log detected orphaned gradle files + test
If a gralde manifest was detected early on but no dependencies were returned at all for it
warn the user to look further as this file may not be included in a root level gradle file.

Thus it could be orhpaned. It could have also failed in which case an error will pop up.
  • Loading branch information
lili2311 committed Aug 28, 2020
1 parent 77d11ba commit d323180
Show file tree
Hide file tree
Showing 6 changed files with 202 additions and 26 deletions.
52 changes: 50 additions & 2 deletions src/lib/plugins/get-deps-from-plugin.ts
@@ -1,4 +1,6 @@
import * as debugModule from 'debug';
import * as pathLib from 'path';
import chalk from 'chalk';
import { legacyPlugin as pluginApi } from '@snyk/cli-interface';
import { find } from '../find-files';
import { Options, TestOptions, MonitorOptions } from '../types';
Expand All @@ -17,6 +19,7 @@ import analytics = require('../analytics');
import { convertSingleResultToMultiCustom } from './convert-single-splugin-res-to-multi-custom';
import { convertMultiResultToMultiCustom } from './convert-multi-plugin-res-to-multi-custom';
import { processYarnWorkspaces } from './nodejs-plugin/yarn-workspaces-parser';
import { ScannedProject } from '@snyk/cli-interface/legacy/common';

const debug = debugModule('snyk-test');

Expand All @@ -42,7 +45,7 @@ export async function getDepsFromPlugin(
const scanType = options.yarnWorkspaces ? 'yarnWorkspaces' : 'allProjects';
const levelsDeep = options.detectionDepth;
const ignore = options.exclude ? options.exclude.split(',') : [];
const { files: targetFiles } = await find(
const { files: targetFiles, allFilesFound } = await find(
root,
ignore,
multiProjectProcessors[scanType].files,
Expand All @@ -62,8 +65,9 @@ export async function getDepsFromPlugin(
options,
targetFiles,
);
const scannedProjects = inspectRes.scannedProjects;
const analyticData = {
scannedProjects: inspectRes.scannedProjects.length,
scannedProjects: scannedProjects.length,
targetFiles,
packageManagers: targetFiles.map((file) =>
detectPackageManagerFromFile(file),
Expand All @@ -72,6 +76,18 @@ export async function getDepsFromPlugin(
ignore,
};
analytics.add(scanType, analyticData);
debug(
`Found ${scannedProjects.length} projects from ${allFilesFound.length} detected manifests`,
);
const userWarningMessage = warnSomeGradleManifestsNotScanned(
scannedProjects,
allFilesFound,
root,
);

if (!options.json && userWarningMessage) {
console.warn(chalk.bold.red(userWarningMessage));
}
return inspectRes;
}

Expand Down Expand Up @@ -105,3 +121,35 @@ export async function getDepsFromPlugin(
);
return convertMultiResultToMultiCustom(inspectRes, options.packageManager);
}

export function warnSomeGradleManifestsNotScanned(
scannedProjects: ScannedProject[],
allFilesFound: string[],
root: string,
): string | null {
const gradleTargetFilesFilter = (targetFile) =>
targetFile &&
(targetFile.endsWith('build.gradle') ||
targetFile.endsWith('build.gradle.kts'));
const scannedGradleFiles = scannedProjects
.map((p) => {
const targetFile = p.meta?.targetFile || p.targetFile;
return targetFile ? pathLib.resolve(root, targetFile) : null;
})
.filter(gradleTargetFilesFilter);
const detectedGradleFiles = allFilesFound.filter(gradleTargetFilesFilter);
const diff = detectedGradleFiles.filter(
(file) => !scannedGradleFiles.includes(file),
);

debug(
`These Gradle manifests did not return any dependency results:\n${diff.join(
',\n',
)}`,
);

if (diff.length > 0) {
return `✗ ${diff.length}/${detectedGradleFiles.length} detected Gradle manifests did not return dependencies.\nThey may have errored or were not included as part of a multi-project build. You may need to scan them individually with --file=path/to/file. Run with \`-d\` for more info.`;
}
return null;
}
120 changes: 96 additions & 24 deletions test/acceptance/cli-test/cli-test.all-projects.spec.ts
Expand Up @@ -4,40 +4,112 @@ import * as depGraphLib from '@snyk/dep-graph';
import { CommandResult } from '../../../src/cli/commands/types';
import { AcceptanceTests } from './cli-test.acceptance.test';
import { getWorkspaceJSON } from '../workspace-helper';
import * as getDepsFromPlugin from '../../../src/lib/plugins/get-deps-from-plugin';

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: [],
},
],
},
});

export const AllProjectsTests: AcceptanceTests = {
language: 'Mixed',
tests: {
'`test kotlin-monorepo --all-projects` scans kotlin files': (
'`test gradle-with-orphaned-build-file --all-projects` warns user': (
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',
},
targetFile: 'build.gradle',
},
depGraph: simpleGradleGraph,
},
{
meta: {
gradleProjectName: 'root-proj/subproj',
versionBuildInfo: {
gradleVersion: '6.5',
},
targetFile: 'subproj/build.gradle',
},
depGraph: simpleGradleGraph,
},
],
};
},
});
};
const loadPlugin = sinon.stub(params.plugins, 'loadPlugin');
t.teardown(loadPlugin.restore);
loadPlugin.withArgs('gradle').returns(plugin);
loadPlugin.callThrough();
// read data from console.log
let stdoutMessages = '';
const stubConsoleLog = (msg: string) => (stdoutMessages += msg);
const stubbedConsole = sinon
.stub(console, 'warn')
.callsFake(stubConsoleLog);
const result: CommandResult = await params.cli.test(
'gradle-with-orphaned-build-file',
{
allProjects: true,
detectionDepth: 3,
},
);
t.same(
stdoutMessages,
'✗ 1/3 detected Gradle manifests did not return dependencies.\n' +
'They may have errored or were not included as part of a multi-project build. You may need to scan them individually with --file=path/to/file. Run with `-d` for more info.',
);
stubbedConsole.restore();
t.ok(stubbedConsole.calledOnce);
t.ok(loadPlugin.withArgs('gradle').calledOnce, 'calls gradle plugin');

t.match(
result.getDisplayResults(),
'Tested 2 projects',
'Detected 2 projects',
);
},
'`test kotlin-monorepo --all-projects` scans kotlin files': (
params,
utils,
) => async (t) => {
utils.chdirWorkspaces();
const plugin = {
async inspect() {
return {
Expand Down
Empty file.
Empty file.
@@ -0,0 +1,5 @@
rootProject.name = 'root-proj'

include 'subproj'


@@ -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'
}
}
}
}
}

0 comments on commit d323180

Please sign in to comment.