UNPKG

snyk-sbt-plugin

Version:
231 lines 9.72 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.buildArgs = exports.inspect = void 0; const path = require("path"); const fs = require("fs"); const semver = require("semver"); const debugModule = require("debug"); // To enable debugging output, run the CLI as `DEBUG=snyk-sbt-plugin snyk ...` const debug = debugModule('snyk-sbt-plugin'); const constants_1 = require("./constants"); const subProcess = require("./sub-process"); const parser = require("./parse-sbt"); const plugin_search_1 = require("./plugin-search"); const version_1 = require("./version"); const tmp = require("tmp"); tmp.setGracefulCleanup(); const packageFormatVersion = 'mvn:0.0.1'; async function inspect(root, targetFile, options) { if (!options) { options = { dev: false }; } const isCoursierPresent = await (0, plugin_search_1.isPluginInstalled)(root, targetFile, constants_1.sbtCoursierPluginName); const isSbtDependencyGraphPresent = await (0, plugin_search_1.isPluginInstalled)(root, targetFile, constants_1.sbtDependencyGraphPluginName); const isNewSbtDependencyGraphPresent = await (0, plugin_search_1.isPluginInstalled)(root, targetFile, constants_1.sbtDependencyGraphPluginNameNew); debug(`isCoursierPresent: ${isCoursierPresent}, isSbtDependencyGraphPresent: ${isSbtDependencyGraphPresent}, isNewSbtDependencyGraphPresent: ${isNewSbtDependencyGraphPresent}`); // If coursier is present, use it because it gives different results to sbt dependencyTree let result; if (isCoursierPresent) { try { options.useCoursier = true; result = await legacyInspect(root, targetFile, options); return result; } catch (err) { debug('Coursier failed with error: ', err); } } // TODO:Legacy path creates a large output that can cause RangeError in bigger projects // We would prefer to use plugin inspect by default but currently it requires sbt-dep-graph plugin if (isSbtDependencyGraphPresent) { try { debug('Applying plugin inspect'); result = await pluginInspect(root, targetFile, options); } catch (err) { debug('pluginInspect failed with error: ', err); } if (result) { result.package.packageFormatVersion = packageFormatVersion; return result; } } options.useCoursier = false; try { result = await legacyInspect(root, targetFile, options); return result; } catch (err) { const hintMsg = buildHintMessage(options); err.message = err.message + '\n' + hintMsg; throw new Error(err); } } exports.inspect = inspect; async function legacyInspect(root, targetFile, options) { const targetFilePath = path.dirname(path.resolve(root, targetFile)); if (!fs.existsSync(targetFilePath)) { debug(`build.sbt not found at location: ${targetFilePath}. This may result in no dependencies`); } const useCoursier = options.useCoursier; const sbtArgs = buildArgs(options.args, useCoursier); debug(`running command: sbt ${sbtArgs.join(' ')}`); const result = { sbtOutput: await subProcess.execute('sbt', sbtArgs, { cwd: targetFilePath, }), coursier: useCoursier, }; const packageName = path.basename(root); const packageVersion = '0.0.0'; const depTree = parser.parse(result.sbtOutput, packageName, packageVersion, result.coursier); depTree.packageFormatVersion = packageFormatVersion; return { plugin: { name: 'bundled:sbt', runtime: 'unknown', }, package: depTree, }; } async function injectSbtScript(sbtPluginPath, targetFolderPath) { try { // We could be running from a bundled CLI generated by `pkg`. // The Node filesystem in that case is not real: https://github.com/zeit/pkg#snapshot-filesystem // Copying the injectable script into a temp file. let projectFolderPath = path.resolve(targetFolderPath, 'project/'); debug(`injectSbtScript: injecting snyk sbt plugin "${sbtPluginPath}" in "${projectFolderPath}"`); if (!fs.existsSync(projectFolderPath)) { debug(`injectSbtScript: "${projectFolderPath}" does not exist`); projectFolderPath = path.resolve(targetFolderPath, '..', 'project/'); debug(`injectSbtScript: will try "${projectFolderPath}"`); } const tmpSbtPlugin = tmp.fileSync({ postfix: '-SnykSbtPlugin.scala', dir: projectFolderPath, }); fs.createReadStream(sbtPluginPath).pipe(fs.createWriteStream(tmpSbtPlugin.name)); debug(`successfully injected plugin at "${tmpSbtPlugin.name}"`); return { path: tmpSbtPlugin.name, remove: tmpSbtPlugin.removeCallback }; } catch (error) { error.message = error.message + '\n\n' + 'Failed to create a temporary file to host Snyk script for SBT build analysis.'; throw error; } } function generateSbtPluginPath(sbtVersion) { let pluginName = 'SnykSbtPlugin-1.2x.scala'; if (semver.lt(sbtVersion, '0.1.0')) { throw new Error('Snyk does not support sbt with version less than 0.1.0'); } if (semver.gte(sbtVersion, '0.1.0') && semver.lt(sbtVersion, '1.1.0')) { pluginName = 'SnykSbtPlugin-0.1x.scala'; } if (/index.[tj]s$/.test(__filename)) { debug('Applying ', pluginName); return path.join(__dirname, `../scala/${pluginName}`); } else { throw new Error(`Cannot locate ${pluginName} script`); } } async function pluginInspect(root, targetFile, options) { let injectedScript; try { const targetFolderPath = path.dirname(path.resolve(root, targetFile)); const sbtArgs = buildArgs(options.args, false, true); const sbtVersion = await (0, version_1.getSbtVersion)(root, targetFile); const sbtPluginPath = generateSbtPluginPath(sbtVersion); const packageName = path.basename(root); const packageVersion = '1.0.0'; injectedScript = await injectSbtScript(sbtPluginPath, targetFolderPath); debug('injectedScript.path: ' + injectedScript.path); debug('args passed to plugin inspect: ', sbtArgs.join(' ')); const stdout = await subProcess.execute('sbt', sbtArgs, { cwd: targetFolderPath, }); return { plugin: { name: 'snyk:sbt', runtime: 'unknown', meta: { versionBuildInfo: { metaBuildVersion: { sbtVersion, }, }, }, }, package: parser.parseSbtPluginResults(stdout, packageName, packageVersion), }; } catch (error) { debug('Failed to produce dependency tree with custom snyk plugin due to error: ' + error.message); return null; } finally { // in case of subProcess.execute failing, perform cleanup here, as putting it after `getInjectScriptPath` might // not be executed because of `sbt` failing if (injectedScript && injectedScript.remove) { try { injectedScript.remove(); debug(`Removed the snyk sbt plugin at '${injectedScript.path}'`); } catch (error) { // NOTE(alexmu): we don't want to kill the whole run because we can still fall back to the legacy // method, but at least tell the user to clean up after the CLI :(. // tslint:disable-next-line:no-console console.warn(`Failed to remove the snyk sbt plugin file at '${injectedScript.path}'`); } } } } function buildHintMessage(options) { const dgArgs = '`sbt ' + buildArgs(options.args, false).join(' ') + '`'; const csArgs = '`sbt ' + buildArgs(options.args, true).join(' ') + '`'; return ('\n\n' + 'Please make sure that the `sbt-dependency-graph` plugin ' + '(https://github.com/jrudolph/sbt-dependency-graph) is installed ' + 'globally or on the current project, and that ' + dgArgs + ' executes successfully on this project.\n\n' + 'Alternatively you can use `sbt-coursier` for dependency resolution ' + '(https://get-coursier.io/docs/sbt-coursier), in which case ensure ' + 'that the plugin is installed on the current project and that ' + csArgs + ' executes successfully on this project.\n\n' + 'For this project we guessed that you are using ' + (options.useCoursier ? 'sbt-coursier' : 'sbt-dependency-graph') + '.\n\n' + 'If the problem persists, collect the output of ' + dgArgs + ' or ' + csArgs + ' and contact support@snyk.io\n'); } function buildArgs(sbtArgs, isCoursierProject, isOutputGraph) { // force plain output so we don't have to parse colour codes let args = ['-Dsbt.log.noformat=true']; if (sbtArgs) { args = args.concat(sbtArgs); } if (isOutputGraph) { args.push('snykRenderTree'); // sbt-dependency-graph } else if (isCoursierProject) { args.push('coursierDependencyTree'); // coursier } else { // enhance sbt default output width from 40 chars to the max args.push('set asciiGraphWidth := 999999999'); args.push('dependencyTree'); // sbt native } return args; } exports.buildArgs = buildArgs; //# sourceMappingURL=index.js.map