Skip to content

Commit

Permalink
fix: reinstate --all-sub-project support for Gradle
Browse files Browse the repository at this point in the history
  • Loading branch information
Konstantin Yegupov committed Aug 13, 2019
1 parent a59786d commit 047998e
Show file tree
Hide file tree
Showing 9 changed files with 72 additions and 126 deletions.
39 changes: 18 additions & 21 deletions src/cli/commands/monitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,6 @@ import * as detect from '../../lib/detect';
import * as plugins from '../../lib/plugins';
import {ModuleInfo} from '../../lib/module-info'; // TODO(kyegupov): fix import
import {
SingleDepRootResult,
MultiDepRootsResult,
isMultiResult,
MonitorOptions,
MonitorMeta,
MonitorResult,
Expand All @@ -30,6 +27,7 @@ import {
MonitorError,
UnsupportedFeatureFlagError,
} from '../../lib/errors';
import { legacyPlugin as pluginApi } from '@snyk/cli-interface';

const SEPARATOR = '\n-------------------------------------------------------\n';

Expand Down Expand Up @@ -78,7 +76,7 @@ async function monitor(...args0: MethodArgs): Promise<any> {
snyk.id = options.id;
}

if (options['all-sub-projects'] && options['project-name']) {
if (options.allSubProjects && options['project-name']) {
throw new Error('`--all-sub-projects` is currently not compatible with `--project-name`');
}

Expand Down Expand Up @@ -126,13 +124,12 @@ async function monitor(...args0: MethodArgs): Promise<any> {

// Scan the project dependencies via a plugin

const pluginOptions = plugins.getPluginOptions(packageManager, options);
analytics.add('packageManager', packageManager);
analytics.add('pluginOptions', pluginOptions);
analytics.add('pluginOptions', options);

// TODO: the type should depend on multiDepRoots flag
const inspectResult: SingleDepRootResult|MultiDepRootsResult = await promiseOrCleanup(
moduleInfo.inspect(path, targetFile, { ...options, ...pluginOptions }),
// TODO: the type should depend on allSubProjects flag
const inspectResult: pluginApi.InspectResult = await promiseOrCleanup(
moduleInfo.inspect(path, targetFile, { ...options }),
spinner.clear(analyzingDepsSpinnerLabel));

analytics.add('pluginName', inspectResult.plugin.name);
Expand All @@ -155,30 +152,30 @@ async function monitor(...args0: MethodArgs): Promise<any> {

// We send results from "all-sub-projects" scanning as different Monitor objects

// SingleDepRootResult is a legacy format understood by Registry, so we have to convert
// a MultiDepRootsResult to an array of these.
// SinglePackageResult is a legacy format understood by Registry, so we have to convert
// a MultiProjectResult to an array of these.

let perDepRootResults: SingleDepRootResult[] = [];
let perSubProjectResults: pluginApi.SinglePackageResult[] = [];
let advertiseSubprojectsCount: number | null = null;
if (isMultiResult(inspectResult)) {
perDepRootResults = inspectResult.depRoots.map(
(depRoot) => ({plugin: inspectResult.plugin, package: depRoot.depTree}));
if (pluginApi.isMultiResult(inspectResult)) {
perSubProjectResults = inspectResult.scannedProjects.map(
(scannedProject) => ({plugin: inspectResult.plugin, package: scannedProject.depTree}));
} else {
if (!options['gradle-sub-project']
&& inspectResult.plugin.meta
&& inspectResult.plugin.meta.allSubProjectNames
&& inspectResult.plugin.meta.allSubProjectNames.length > 1) {
advertiseSubprojectsCount = inspectResult.plugin.meta.allSubProjectNames.length;
}
perDepRootResults = [inspectResult];
perSubProjectResults = [inspectResult];
}

// Post the project dependencies to the Registry
for (const depRootDeps of perDepRootResults) {
maybePrintDeps(options, depRootDeps.package);
for (const subProjDeps of perSubProjectResults) {
maybePrintDeps(options, subProjDeps.package);

const res = await promiseOrCleanup(
snykMonitor(path, meta, depRootDeps, targetFile),
snykMonitor(path, meta, subProjDeps, targetFile),
spinner.clear(postingMonitorSpinnerLabel));

await spinner.clear(postingMonitorSpinnerLabel)(res);
Expand All @@ -193,8 +190,8 @@ async function monitor(...args0: MethodArgs): Promise<any> {
const manageUrl = url.format(endpoint);

endpoint.pathname = leader + '/monitor/' + res.id;
const subProjectName = isMultiResult(inspectResult)
? depRootDeps.package.name
const subProjectName = pluginApi.isMultiResult(inspectResult)
? subProjDeps.package.name
: undefined;
const monOutput = formatMonitorOutput(
packageManager,
Expand Down
4 changes: 2 additions & 2 deletions src/lib/module-info/index.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import * as _ from 'lodash';
import * as Debug from 'debug';
import { SingleDepRootResult } from '../types';
import { legacyPlugin as pluginApi } from '@snyk/cli-interface';

const debug = Debug('snyk-module-info');

export function ModuleInfo(plugin, policy) {
return {
async inspect(root, targetFile, options): Promise<SingleDepRootResult> {
async inspect(root, targetFile, options): Promise<pluginApi.SinglePackageResult> {
const pluginOptions = _.merge({
args: options._doubleDashArgs,
}, options);
Expand Down
9 changes: 5 additions & 4 deletions src/lib/monitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@ import * as os from 'os';
import * as _ from 'lodash';
import {isCI} from './is-ci';
import * as analytics from './analytics';
import { SingleDepRootResult, DepTree, MonitorMeta, MonitorResult } from './types';
import { DepTree, MonitorMeta, MonitorResult } from './types';
import * as projectMetadata from './project-metadata';
import * as path from 'path';
import {MonitorError, ConnectionTimeoutError} from './errors';
import { countPathsToGraphRoot, pruneGraph } from './prune';
import { GRAPH_SUPPORTED_PACKAGE_MANAGERS } from './package-managers';
import { legacyPlugin as pluginApi } from '@snyk/cli-interface';

const debug = Debug('snyk');

Expand Down Expand Up @@ -104,7 +105,7 @@ function filterOutMissingDeps(depTree: DepTree): FilteredDepTree {

for (const depKey of Object.keys(depTree.dependencies)) {
const dep = depTree.dependencies[depKey];
if (dep.missingLockFileEntry) {
if ((dep as any).missingLockFileEntry) { // TODO(kyegupov): add field to the type
missingDeps.push(`${dep.name}@${dep.version}`);
} else {
filteredDeps[depKey] = dep;
Expand All @@ -124,7 +125,7 @@ function filterOutMissingDeps(depTree: DepTree): FilteredDepTree {
export async function monitor(
root: string,
meta: MonitorMeta,
info: SingleDepRootResult,
info: pluginApi.SinglePackageResult,
targetFile?: string,
): Promise<MonitorResult> {
apiTokenExists();
Expand Down Expand Up @@ -231,7 +232,7 @@ export async function monitor(
export async function monitorGraph(
root: string,
meta: MonitorMeta,
info: SingleDepRootResult,
info: pluginApi.SinglePackageResult,
targetFile?: string,
): Promise<MonitorResult> {
const packageManager = meta.packageManager;
Expand Down
15 changes: 0 additions & 15 deletions src/lib/plugins/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,18 +59,3 @@ export function loadPlugin(packageManager: SupportedPackageManagers,
}
}
}

export function getPluginOptions(packageManager: string, options: types.Options): types.Options {
const pluginOptions: types.Options = {};
switch (packageManager) {
case 'gradle': {
if (options['all-sub-projects']) {
pluginOptions.multiDepRoots = true;
}
return pluginOptions;
}
default: {
return pluginOptions;
}
}
}
4 changes: 2 additions & 2 deletions src/lib/plugins/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ export interface InspectResult {
runtime?: string;
};
package?: any;
depRoots?: any;
scannedProjects?: any;
}

export interface Options {
Expand All @@ -13,7 +13,7 @@ export interface Options {
traverseNodeModules?: boolean;
dev?: boolean;
strictOutOfSync?: boolean | 'true' | 'false';
multiDepRoots?: boolean;
allSubProjects?: boolean;
debug?: boolean;
packageManager?: string;
composerIsFine?: boolean;
Expand Down
5 changes: 3 additions & 2 deletions src/lib/print-deps.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { DepDict, Options, MonitorOptions, DepTree } from './types';
import { DepDict, Options, MonitorOptions } from './types';
import { legacyCommon as legacyApi } from '@snyk/cli-interface';

// This option is still experimental and might be deprecated.
// It might be a better idea to convert it to a command (i.e. do not perform test/monitor).
export function maybePrintDeps(options: Options | MonitorOptions, rootPackage: DepTree) {
export function maybePrintDeps(options: Options | MonitorOptions, rootPackage: legacyApi.DepTree) {
if (options['print-deps']) {
if (options.json) {
// Will produce 2 JSON outputs, one for the deps, one for the vuln scan.
Expand Down
31 changes: 17 additions & 14 deletions src/lib/snyk-test/run-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import common = require('./common');
import {DepTree, TestOptions} from '../types';
import gemfileLockToDependencies = require('../../lib/plugins/rubygems/gemfile-lock-to-dependencies');
import {convertTestDepGraphResultToLegacy, AnnotatedIssue, LegacyVulnApiResult, TestDepGraphResponse} from './legacy';
import {SingleDepRootResult, MultiDepRootsResult, isMultiResult, Options} from '../types';
import {Options} from '../types';
import {
NoSupportedManifestsFoundError,
InternalServerError,
Expand All @@ -26,6 +26,7 @@ import {
import { maybePrintDeps } from '../print-deps';
import { SupportedPackageManagers } from '../package-managers';
import { countPathsToGraphRoot, pruneGraph } from '../prune';
import { legacyPlugin as pluginApi } from '@snyk/cli-interface';

// tslint:disable-next-line:no-var-requires
const debug = require('debug')('snyk');
Expand Down Expand Up @@ -207,23 +208,22 @@ function assemblePayloads(root: string, options: Options & TestOptions): Promise
return assembleRemotePayloads(root, options);
}

// Force getDepsFromPlugin to return depRoots for processing in assembleLocalPayload
async function getDepsFromPlugin(root, options: Options): Promise<MultiDepRootsResult> {
// Force getDepsFromPlugin to return scannedProjects for processing in assembleLocalPayload
async function getDepsFromPlugin(root, options: Options): Promise<pluginApi.MultiProjectResult> {
options.file = options.file || detect.detectPackageFile(root);
if (!options.docker && !(options.file || options.packageManager)) {
throw NoSupportedManifestsFoundError([...root]);
}
const plugin = plugins.loadPlugin(options.packageManager, options);
const moduleInfo = ModuleInfo(plugin, options.policy);
const pluginOptions = plugins.getPluginOptions(options.packageManager, options);
const inspectRes: SingleDepRootResult | MultiDepRootsResult =
await moduleInfo.inspect(root, options.file, { ...options, ...pluginOptions });
const inspectRes: pluginApi.InspectResult =
await moduleInfo.inspect(root, options.file, { ...options });

if (!isMultiResult(inspectRes)) {
if (!pluginApi.isMultiResult(inspectRes)) {
if (!inspectRes.package) {
// something went wrong if both are not present...
throw Error(`error getting dependencies from ${options.packageManager} ` +
'plugin: neither \'package\' nor \'depRoots\' were found');
'plugin: neither \'package\' nor \'scannedProjects\' were found');
}
if (!inspectRes.package.targetFile && inspectRes.plugin) {
inspectRes.package.targetFile = inspectRes.plugin.targetFile;
Expand All @@ -238,13 +238,13 @@ async function getDepsFromPlugin(root, options: Options): Promise<MultiDepRootsR
}
return {
plugin: inspectRes.plugin,
depRoots: [{depTree: inspectRes.package}],
scannedProjects: [{depTree: inspectRes.package}],
};
} else {
// We are using "options" to store some information returned from plugin that we need to use later,
// but don't want to send to Registry in the Payload.
// TODO(kyegupov): decouple inspect and payload so that we don't need this hack
options.subProjectNames = inspectRes.depRoots.map((depRoot) => depRoot.depTree.name);
(options as any).subProjectNames = inspectRes.scannedProjects.map((scannedProject) => scannedProject.depTree.name);
return inspectRes;
}
}
Expand All @@ -263,14 +263,14 @@ async function assembleLocalPayloads(root, options: Options & TestOptions): Prom
const deps = await getDepsFromPlugin(root, options);
analytics.add('pluginName', deps.plugin.name);

for (const depRoot of deps.depRoots) {
const pkg = depRoot.depTree;
for (const scannedProject of deps.scannedProjects) {
const pkg = scannedProject.depTree;
if (options['print-deps']) {
await spinner.clear<void>(spinnerLbl)();
maybePrintDeps(options, pkg);
}
if (deps.plugin && deps.plugin.packageManager) {
options.packageManager = deps.plugin.packageManager;
(options as any).packageManager = deps.plugin.packageManager;
}

if (pkg.docker) {
Expand Down Expand Up @@ -332,7 +332,10 @@ async function assembleLocalPayloads(root, options: Options & TestOptions): Prom
} else {
// Graphs are more compact and robust representations.
// Legacy parts of the code are still using trees, but will eventually be fully migrated.
debug('converting dep-tree to dep-graph', {name: pkg.name, targetFile: depRoot.targetFile || options.file});
debug('converting dep-tree to dep-graph', {
name: pkg.name,
targetFile: scannedProject.targetFile || options.file,
});
let depGraph = await depGraphLib.legacy.depTreeToGraph(
pkg, options.packageManager);

Expand Down
47 changes: 4 additions & 43 deletions src/lib/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { SupportedPackageManagers } from './package-managers';

// TODO(kyegupov): use a shared repository snyk-cli-interface
import { legacyCommon as legacyApi } from '@snyk/cli-interface';

export interface PluginMetadata {
name: string;
Expand All @@ -19,45 +18,7 @@ export interface DepDict {
[name: string]: DepTree;
}

export interface DepTree {
name: string;
version: string;
dependencies?: DepDict;
packageFormatVersion?: string;
docker?: any;
files?: any;
targetFile?: string;
missingLockFileEntry?: boolean;

labels?: {
[key: string]: string;

// Known keys:
// pruned: identical subtree already presents in the parent node.
// See --prune-repeated-subdependencies flag.
};
}

export interface DepRoot {
depTree: DepTree; // to be soon replaced with depGraph
targetFile?: string;
}

// Legacy result type. Will be deprecated soon.
export interface SingleDepRootResult {
plugin: PluginMetadata;
package: DepTree;
}

export interface MultiDepRootsResult {
plugin: PluginMetadata;
depRoots: DepRoot[];
}

// https://www.typescriptlang.org/docs/handbook/advanced-types.html#user-defined-type-guards
export function isMultiResult(pet: SingleDepRootResult | MultiDepRootsResult): pet is MultiDepRootsResult {
return !!(pet as MultiDepRootsResult).depRoots;
}
export type DepTree = legacyApi.DepTree;

export interface TestOptions {
traverseNodeModules: boolean;
Expand All @@ -81,7 +42,7 @@ export interface Options {
'ignore-policy'?: boolean;
'trust-policies'?: boolean; // used in snyk/policy lib
'policy-path'?: boolean;
'all-sub-projects'?: boolean; // Corresponds to multiDepRoot in plugins
allSubProjects?: boolean;
'project-name'?: string;
'show-vulnerable-paths'?: string;
showVulnPaths?: boolean;
Expand All @@ -100,7 +61,7 @@ export interface MonitorOptions {
file?: string;
policy?: string;
json?: boolean;
'all-sub-projects'?: boolean; // Corresponds to multiDepRoot in plugins
allSubProjects?: boolean;
'project-name'?: string;
'print-deps'?: boolean;
'experimental-dep-graph'?: boolean;
Expand Down

0 comments on commit 047998e

Please sign in to comment.