Skip to content

Commit

Permalink
feat: experimental command line flag to print the dependency tree
Browse files Browse the repository at this point in the history
  • Loading branch information
Konstantin Yegupov committed Jun 13, 2019
1 parent 4ff99b4 commit c431bc4
Show file tree
Hide file tree
Showing 6 changed files with 57 additions and 14 deletions.
1 change: 1 addition & 0 deletions help/help.txt
Expand Up @@ -45,6 +45,7 @@ Options:
--dry-run .......... Don't apply updates or patches during protect.
--severity-threshold=<low|medium|high>
Only report vulnerabilities of provided level or higher.
--print-deps ....... (experimental) Print the dependency tree.
-q, --quiet ........ Silence all output.
-h, --help ......... This help information.
-v, --version ...... The CLI version.
Expand Down
16 changes: 4 additions & 12 deletions src/cli/commands/monitor.ts
Expand Up @@ -14,22 +14,12 @@ import * as spinner from '../../lib/spinner';
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, MonitorError } from '../../lib/types';
import { SingleDepRootResult, MultiDepRootsResult, isMultiResult, MonitorError, MonitorOptions } from '../../lib/types';
import { MethodArgs, ArgsOptions } from '../args';
import { maybePrintDeps } from '../../lib/print-deps';

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

// TODO(kyegupov): catch accessing ['undefined-properties'] via noImplicitAny
interface MonitorOptions {
id?: string;
docker?: boolean;
file?: string;
policy?: string;
json?: boolean;
'all-sub-projects'?: boolean; // Corresponds to multiDepRoot in plugins
'project-name'?: string;
}

interface GoodResult {
ok: true;
data: string;
Expand Down Expand Up @@ -153,6 +143,8 @@ async function monitor(...args0: MethodArgs): Promise<any> {

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

const res = await promiseOrCleanup(
snykMonitor(path, meta, depRootDeps, targetFile),
spinner.clear(postingMonitorSpinnerLabel));
Expand Down
2 changes: 1 addition & 1 deletion src/lib/plugins/nodejs-plugin/npm-modules-parser.ts
Expand Up @@ -38,6 +38,6 @@ export async function parse(root: string, targetFile: string, options: Options):
return snyk.modules(
root, Object.assign({}, options, {noFromArrays: true}));
} finally {
spinner.clear(resolveModuleSpinnerLabel)();
await spinner.clear(resolveModuleSpinnerLabel)();
}
}
32 changes: 32 additions & 0 deletions src/lib/print-deps.ts
@@ -0,0 +1,32 @@
import { DepDict, TestOptions, MonitorOptions, DepTree } from './types';

// 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: TestOptions | MonitorOptions, rootPackage: DepTree) {
if (options['print-deps']) {
if (options.json) {
// Will produce 2 JSON outputs, one for the deps, one for the vuln scan.
console.log(JSON.stringify(rootPackage, null, 2));
} else {
printDeps({[rootPackage.name!]: rootPackage});
}
}
}

function printDeps(depDict: DepDict, prefix: string = '') {
let counter = 0;
const keys = Object.keys(depDict);
for (const name of keys) {
const dep = depDict[name];
let branch = '├─ ';
const last = (counter === keys.length - 1);
if (last) {
branch = '└─ ';
}
console.log(prefix + (prefix ? branch : '') + dep.name + ' @ ' + dep.version);
if (dep.dependencies) {
printDeps(dep.dependencies, prefix + (last ? ' ' : '│ '));
}
counter++;
}
}
7 changes: 6 additions & 1 deletion src/lib/snyk-test/run-test.ts
Expand Up @@ -19,6 +19,7 @@ import gemfileLockToDependencies = require('../../lib/plugins/rubygems/gemfile-l
import {convertTestDepGraphResultToLegacy, AnnotatedIssue, LegacyVulnApiResult, TestDepGraphResponse} from './legacy';
import {SingleDepRootResult, MultiDepRootsResult, isMultiResult, TestOptions} from '../types';
import { NoSupportedManifestsFoundError } from '../errors';
import { maybePrintDeps } from '../print-deps';

// tslint:disable-next-line:no-var-requires
const debug = require('debug')('snyk');
Expand Down Expand Up @@ -255,6 +256,10 @@ async function assembleLocalPayloads(root, options): Promise<Payload[]> {

for (const depRoot of deps.depRoots) {
const pkg = depRoot.depTree;
if (options['print-deps']) {
await spinner.clear(spinnerLbl)();
maybePrintDeps(options, pkg);
}
if (deps.plugin && deps.plugin.packageManager) {
options.packageManager = deps.plugin.packageManager;
}
Expand Down Expand Up @@ -349,7 +354,7 @@ async function assembleLocalPayloads(root, options): Promise<Payload[]> {
}
return payloads;
} finally {
spinner.clear(spinnerLbl)();
await spinner.clear(spinnerLbl)();
}
}

Expand Down
13 changes: 13 additions & 0 deletions src/lib/types.ts
Expand Up @@ -71,4 +71,17 @@ export interface TestOptions {
advertiseSubprojectsCount?: number;
subProjectNames?: string[];
severityThreshold?: string;
'print-deps'?: boolean;
}

// TODO(kyegupov): catch accessing ['undefined-properties'] via noImplicitAny
export interface MonitorOptions {
id?: string;
docker?: boolean;
file?: string;
policy?: string;
json?: boolean;
'all-sub-projects'?: boolean; // Corresponds to multiDepRoot in plugins
'project-name'?: string;
'print-deps'?: boolean;
}

0 comments on commit c431bc4

Please sign in to comment.