diff --git a/lib/cli.js b/lib/cli.js index 1143583..1617ac5 100644 --- a/lib/cli.js +++ b/lib/cli.js @@ -19,7 +19,7 @@ async function main(cli, { policyDetails, setupProjectFn }) { cli.showHelp(1); } else { const currentDate = processDate(cli.flags.currentDate) || undefined; - + const ignoredDependencies = []; let policies = { primary: DEFAULT_PRIMARY_POLICY, }; @@ -28,6 +28,9 @@ async function main(cli, { policyDetails, setupProjectFn }) { let configuredPolicies = JSON.parse(configFile); if (configuredPolicies.primary) { policies.primary = configuredPolicies.primary; + if (configuredPolicies.primary.ignoredDependencies) { + ignoredDependencies.push(...configuredPolicies.primary.ignoredDependencies); + } } if (configuredPolicies.custom) { policies.custom = {}; @@ -38,6 +41,11 @@ async function main(cli, { policyDetails, setupProjectFn }) { `The dependency ${dep} was found multiple times in the config file. Please refer Rules section in configuration.md`, ); } + if (ignoredDependencies.includes(dep)) { + throw new Error( + `The dependency ${dep} was found in ignoredDependencies and custom configuration. Please refer Rules section in configuration.md`, + ); + } policies.custom[dep] = { upgradeBudget: policy.upgradeBudget, effectiveReleaseDate: @@ -52,7 +60,14 @@ async function main(cli, { policyDetails, setupProjectFn }) { let result; let processed = false; try { - result = await processPolicies(projectPaths, setupProjectFn, spinner, currentDate, policies); + result = await processPolicies( + projectPaths, + setupProjectFn, + spinner, + currentDate, + policies, + ignoredDependencies, + ); if (result.isInSupportWindow === false) { process.exitCode = 1; } diff --git a/lib/project/multiple-projects.js b/lib/project/multiple-projects.js index 6dce394..bf20c4f 100644 --- a/lib/project/multiple-projects.js +++ b/lib/project/multiple-projects.js @@ -10,7 +10,14 @@ const { ProgressLogger } = require('../util'); const DEFAULT_SETUP_FILE = './setup-project'; module.exports.processPolicies = processPolicies; -async function processPolicies(projectPaths, setupProjectFn, spinner, today, policies) { +async function processPolicies( + projectPaths, + setupProjectFn, + spinner, + today, + policies, + ignoredDependencies, +) { const setupProject = setupProjectFn ? setupProjectFn : require(DEFAULT_SETUP_FILE); let result = { isInSupportWindow: true, @@ -30,9 +37,15 @@ async function processPolicies(projectPaths, setupProjectFn, spinner, today, pol work.push( queue.add(async () => { let { dependenciesToCheck, pkg } = await setupProject(projectPath); + let dependenciesToCheckAfterIgnore = dependenciesToCheck.filter( + dep => !ignoredDependencies.includes(dep.name), + ); progressLogger.updateTotalDepCount(dependenciesToCheck.length); + progressLogger.updateIgnoredDepCount( + dependenciesToCheck.length - dependenciesToCheckAfterIgnore.length, + ); let auditResult = await isInSupportWindow( - dependenciesToCheck, + dependenciesToCheckAfterIgnore, pkg.name, { progressLogger, diff --git a/lib/util.js b/lib/util.js index 4f83164..0dc658c 100644 --- a/lib/util.js +++ b/lib/util.js @@ -91,12 +91,13 @@ function sortLibraries(a, b) { } module.exports.ProgressLogger = class ProgressLogger { - constructor(_spinner = { prefixText: '', text: '' }, _isMultipleProduct) { + constructor(_spinner = { prefixText: '', text: '' }, _isMultipleProduct = false) { this.spinner = _spinner; this.totalPackages = 0; // start from `1` as we have node policy tested all the time this.processedCount = 0; this.semVerLogged = false; this.isMultipleProduct = _isMultipleProduct; + this.ignoredPackages = 0; } getLoggerPrefixText(name, isSupported, isExpiringSoon) { @@ -123,6 +124,10 @@ module.exports.ProgressLogger = class ProgressLogger { this.totalPackages += count; } + updateIgnoredDepCount(count) { + this.ignoredPackages = count; + } + updateSpinner(name, isSupported, isExpiringSoon) { if (name && !this.isMultipleProduct) { if (!this.spinner.prefixText) { @@ -141,7 +146,9 @@ module.exports.ProgressLogger = class ProgressLogger { } this.spinner.text = `Total Dependencies: ${this.totalPackages}, Verified: ${ this.processedCount - }, Remaining: ${this.totalPackages - this.processedCount}`; + }, Remaining: ${this.totalPackages - this.processedCount - this.ignoredPackages}, Ignored: ${ + this.ignoredPackages + }`; if (this.totalPackages > this.processedCount) { this.processedCount++; } diff --git a/tests/cli-test.js b/tests/cli-test.js index b3407ce..51ac2c1 100644 --- a/tests/cli-test.js +++ b/tests/cli-test.js @@ -180,6 +180,44 @@ describe('CLI', function () { }); }); + describe(`ignore-dependencies`, function () { + it('check console log', async function () { + const child = await runSupportedCmd([ + `${__dirname}/fixtures/supported-project`, + `-f ${__dirname}/fixtures/supported-project/config.json`, + ]); + + expect(child).to.exitGracefully(); + expect(child.stderr).to.includes(`Ignored: 2`); + expect(child.stderr).to.includes('✓ SemVer Policy'); + expect(child.stdout).to.includes('Congrats!'); + }); + + it('check if verbose do not incude the entry', async function () { + const child = await runSupportedCmd([ + `${__dirname}/fixtures/supported-project`, + `-f ${__dirname}/fixtures/supported-project/config.json`, + '--verbose', + ]); + + expect(child).to.exitGracefully(); + expect(child.stderr).to.includes(`Ignored: 2`); + expect(child.stderr).to.includes('✓ SemVer Policy'); + expect(child.stdout).to.includes('Congrats!'); + expect(child.stdout).not.include('@stefanpenner/a'); + }); + + it('make unsupported to supported project using ignoreDependency config', async function () { + const child = await runSupportedCmd([ + `${__dirname}/fixtures/unsupported-project`, + `-f ${__dirname}/fixtures/unsupported-project/config-ignore-dep.json`, + ]); + + expect(child).to.exitGracefully(); + expect(child.stderr).to.includes('✓ SemVer Policy'); + }); + }); + describe('Filter options like --unsupported/expiring/supported', function () { it('works against a unsupported project with --unsupported option', async function () { const child = await runSupportedCmd([ @@ -364,6 +402,7 @@ describe('CLI', function () { }); }); }); + describe('--config-file', function () { it('make unsupported to supported project using effectiveReleaseDate', async function () { const child = await runSupportedCmd([ @@ -374,10 +413,11 @@ describe('CLI', function () { expect(child).to.exitGracefully(); expect(child.stderr).to.includes('✓ SemVer Policy'); }); + it('make unsupported to supported project using upgradeBudget', async function () { const child = await runSupportedCmd([ `${__dirname}/fixtures/unsupported-project`, - `--config-file ${__dirname}/fixtures/unsupported-project/config_2.json`, + `--config-file ${__dirname}/fixtures/unsupported-project/config-custom-budget.json`, ]); expect(child).to.exitGracefully(); @@ -385,16 +425,25 @@ describe('CLI', function () { }); it('alert user when there is conflicting custom config', async function () { - try { - await runSupportedCmd([ - `${__dirname}/fixtures/unsupported-project`, - `-f ${__dirname}/fixtures/unsupported-project/config-conflict.json`, - ]); - } catch (e) { - expect(e).includes( - `The dependency es6-promise was found multiple times in the config file. Please refer Rules section in configuration.md`, - ); - } + const child = await runSupportedCmd([ + `${__dirname}/fixtures/unsupported-project`, + `-f ${__dirname}/fixtures/unsupported-project/config-conflict.json`, + ]); + expect(child).not.to.exitGracefully(); + expect(child.stderr).includes( + `The dependency es6-promise was found multiple times in the config file. Please refer Rules section in configuration.md`, + ); + }); + + it('alert user when there is conflict in custom config and ignoredDependency', async function () { + const child = await runSupportedCmd([ + `${__dirname}/fixtures/unsupported-project`, + `-f ${__dirname}/fixtures/unsupported-project/config-ignore-dep-conflict.json`, + ]); + expect(child).not.to.exitGracefully(); + expect(child.stderr).includes( + `The dependency es6-promise was found in ignoredDependencies and custom configuration. Please refer Rules section in configuration.md`, + ); }); }); }); diff --git a/tests/fixtures/supported-project/config.json b/tests/fixtures/supported-project/config.json new file mode 100644 index 0000000..d2b8546 --- /dev/null +++ b/tests/fixtures/supported-project/config.json @@ -0,0 +1,5 @@ +{ + "primary": { + "ignoredDependencies": ["@stefanpenner/a", "rsvp"] + } +} \ No newline at end of file diff --git a/tests/fixtures/unsupported-project/config_2.json b/tests/fixtures/unsupported-project/config-custom-budget.json similarity index 100% rename from tests/fixtures/unsupported-project/config_2.json rename to tests/fixtures/unsupported-project/config-custom-budget.json diff --git a/tests/fixtures/unsupported-project/config-ignore-dep-conflict.json b/tests/fixtures/unsupported-project/config-ignore-dep-conflict.json new file mode 100644 index 0000000..dcc6aa9 --- /dev/null +++ b/tests/fixtures/unsupported-project/config-ignore-dep-conflict.json @@ -0,0 +1,11 @@ +{ + "primary": { + "ignoredDependencies": ["es6-promise", "@stefanpenner/a", "rsvp"] + }, + "custom": [ + { + "dependencies": ["es6-promise"], + "effectiveReleaseDate": "Dec 10 2022" + } + ] +} \ No newline at end of file diff --git a/tests/fixtures/unsupported-project/config-ignore-dep.json b/tests/fixtures/unsupported-project/config-ignore-dep.json new file mode 100644 index 0000000..d9fd92d --- /dev/null +++ b/tests/fixtures/unsupported-project/config-ignore-dep.json @@ -0,0 +1,5 @@ +{ + "primary": { + "ignoredDependencies": ["es6-promise", "@stefanpenner/a", "rsvp"] + } +} \ No newline at end of file