|
| 1 | +export = fix; |
| 2 | + |
| 3 | +import * as Debug from 'debug'; |
| 4 | +import * as snykFix from '@snyk/fix'; |
| 5 | +import * as ora from 'ora'; |
| 6 | + |
| 7 | +import { MethodArgs } from '../../args'; |
| 8 | +import * as snyk from '../../../lib'; |
| 9 | +import { TestResult } from '../../../lib/snyk-test/legacy'; |
| 10 | + |
| 11 | +import { convertLegacyTestResultToFixEntities } from './convert-legacy-tests-results-to-fix-entities'; |
| 12 | +import { formatTestError } from '../test/format-test-error'; |
| 13 | +import { processCommandArgs } from '../process-command-args'; |
| 14 | +import { validateCredentials } from '../test/validate-credentials'; |
| 15 | +import { validateTestOptions } from '../test/validate-test-options'; |
| 16 | +import { setDefaultTestOptions } from '../test/set-default-test-options'; |
| 17 | +import { validateFixCommandIsSupported } from './validate-fix-command-is-supported'; |
| 18 | +import { Options, TestOptions } from '../../../lib/types'; |
| 19 | +import { getDisplayPath } from './get-display-path'; |
| 20 | + |
| 21 | +const debug = Debug('snyk-fix'); |
| 22 | +const snykFixFeatureFlag = 'cliSnykFix'; |
| 23 | + |
| 24 | +interface FixOptions { |
| 25 | + dryRun?: boolean; |
| 26 | + quiet?: boolean; |
| 27 | +} |
| 28 | +async function fix(...args: MethodArgs): Promise<string> { |
| 29 | + const { options: rawOptions, paths } = await processCommandArgs<FixOptions>( |
| 30 | + ...args, |
| 31 | + ); |
| 32 | + const options = setDefaultTestOptions<FixOptions>(rawOptions); |
| 33 | + debug(options); |
| 34 | + await validateFixCommandIsSupported(options); |
| 35 | + validateTestOptions(options); |
| 36 | + validateCredentials(options); |
| 37 | + const results: snykFix.EntityToFix[] = []; |
| 38 | + results.push(...(await runSnykTestLegacy(options, paths))); |
| 39 | + |
| 40 | + // fix |
| 41 | + debug( |
| 42 | + `Organization has ${snykFixFeatureFlag} feature flag enabled for experimental Snyk fix functionality`, |
| 43 | + ); |
| 44 | + const { dryRun, quiet } = options; |
| 45 | + const { fixSummary, meta } = await snykFix.fix(results, { dryRun, quiet }); |
| 46 | + if (meta.fixed === 0) { |
| 47 | + throw new Error(fixSummary); |
| 48 | + } |
| 49 | + return fixSummary; |
| 50 | +} |
| 51 | + |
| 52 | +/* @deprecated |
| 53 | + * TODO: once project envelope is default all code below will be deleted |
| 54 | + * we should be calling test via new Ecosystems instead |
| 55 | + */ |
| 56 | +async function runSnykTestLegacy( |
| 57 | + options: Options & TestOptions & FixOptions, |
| 58 | + paths: string[], |
| 59 | +): Promise<snykFix.EntityToFix[]> { |
| 60 | + const results: snykFix.EntityToFix[] = []; |
| 61 | + const stdOutSpinner = ora({ |
| 62 | + isSilent: options.quiet, |
| 63 | + stream: process.stdout, |
| 64 | + }); |
| 65 | + const stdErrSpinner = ora({ |
| 66 | + isSilent: options.quiet, |
| 67 | + stream: process.stdout, |
| 68 | + }); |
| 69 | + stdErrSpinner.start(); |
| 70 | + stdOutSpinner.start(); |
| 71 | + |
| 72 | + for (const path of paths) { |
| 73 | + let displayPath = path; |
| 74 | + try { |
| 75 | + displayPath = getDisplayPath(path); |
| 76 | + stdOutSpinner.info(`Running \`snyk test\` for ${displayPath}`); |
| 77 | + // Create a copy of the options so a specific test can |
| 78 | + // modify them i.e. add `options.file` etc. We'll need |
| 79 | + // these options later. |
| 80 | + const snykTestOptions = { |
| 81 | + ...options, |
| 82 | + path, |
| 83 | + projectName: options['project-name'], |
| 84 | + }; |
| 85 | + |
| 86 | + const testResults: TestResult[] = []; |
| 87 | + |
| 88 | + const testResultForPath: TestResult | TestResult[] = await snyk.test( |
| 89 | + path, |
| 90 | + { ...snykTestOptions, quiet: true }, |
| 91 | + ); |
| 92 | + testResults.push( |
| 93 | + ...(Array.isArray(testResultForPath) |
| 94 | + ? testResultForPath |
| 95 | + : [testResultForPath]), |
| 96 | + ); |
| 97 | + const newRes = convertLegacyTestResultToFixEntities(testResults, path); |
| 98 | + results.push(...newRes); |
| 99 | + } catch (error) { |
| 100 | + const testError = formatTestError(error); |
| 101 | + const userMessage = `Test for ${displayPath} failed with error: ${testError.message}.\nRun \`snyk test ${displayPath} -d\` for more information.`; |
| 102 | + stdErrSpinner.fail(userMessage); |
| 103 | + debug(userMessage); |
| 104 | + } |
| 105 | + } |
| 106 | + stdOutSpinner.stop(); |
| 107 | + stdErrSpinner.stop(); |
| 108 | + return results; |
| 109 | +} |
0 commit comments