Skip to content

Commit

Permalink
feat!: add check command instead of using it as an option (#96)
Browse files Browse the repository at this point in the history
  • Loading branch information
inigomarquinez authored Jul 19, 2023
1 parent b92b15a commit cd53a8d
Show file tree
Hide file tree
Showing 9 changed files with 233 additions and 207 deletions.
64 changes: 40 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,13 @@ This package allows you to do a quick audit on your NPM dependencies by adding i

You can optionally add options to exclude generating the report or avoid generating the error report in case a forbidden license is found (see more details [here](#options)).

The package provides two commands:

| Command | Description |
|---|----|
| scan | (default command) scan licenses of a project looking for forbidden licenses |
| check | check if a license is SPDX compliant |

## 🔎 How to use it in your project

- Install the package
Expand All @@ -28,54 +35,63 @@ You can optionally add options to exclude generating the report or avoid generat
npm install @onebeyond/license-checker
```

### `check` command

Just run the check command with the license expression you want to check against SPDX:

```sh
npx @onebeyond/license-checker check <license>
```

The process will fail if _license_ is not SPDX compliant.

### `scan` command

- Add a script to run the package

```sh
npx @onebeyond/license-checker --failOn license
npx @onebeyond/license-checker scan --failOn <license>
```

- If you are using **yarn** you may want to run it from the node modules instead of using npx

```sh
node_modules/.bin/license-checker --failOn license
node_modules/.bin/license-checker scan --failOn <license>
```

- Use the script wherever you want (husky hook, in your CI/CD pipeline, ...)

## 🚩 <a name="options"></a>Options

| Option | Description | Type | Default |
|-----------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------|----------|---------------------------------|
| --start | Path of the initial json to look for | string | `process.cwd()` |
| --version | Shows the version of the package | string | |
| --failOn | Fail (exit with code 1) if any package license does not satisfies any license in the provided list | string[] | |
| --outputFileName | Name of the output file generated | string | `license-report-<timestamp>.md` |
| --errorReportFileName | Name of the file generated when a license in the failOn option is found | string | `license-error-<timestamp>.md` |
| --disableErrorReport | Flag to disable the error report file generation | boolean | `false` |
| --disableReport | Flag to disable the report file generation, whether there is an error or not | boolean | `false` |
| --customHeader | Name of a text file containing the custom header to add at the start of the generated report | string | |
| --checkLicense | License to be check against SPDX. It is intended to be used as a standalone option, i.e. if it is present, it will not trigger the package licenses scanning | string | |
| -h, --help | Shows help | boolean | |
#### 🚩 <a name="options"></a>Options

| Option | Description | Requiered | Type | Default |
|---|---|---|---|---|
| --start | Path of the initial json to look for | false | string | `process.cwd()` |
| --failOn | Fail (exit with code 1) if any package license does not satisfies any license in the provided list | true | string[] | |
| --outputFileName | Name of the report file generated | false | string | `license-report-<timestamp>.md` |
| --errorReportFileName | Name of the error report file generated when a license in the `failOn` option is found | false | string | `license-error-<timestamp>.md` |
| --disableErrorReport | Flag to disable the error report file generation | false | boolean | `false` |
| --disableReport | Flag to disable the report file generation, whether there is an error or not | false | boolean | `false` |
| --customHeader | Name of a text file containing the custom header to add at the start of the generated report | false | string | This application makes use of the following open source packages: |

## 🧑‍💻 <a name="examples"></a>Examples

### checkLicense
### check command

This option is intended to be used as a standalone functionality to check whether the value supplied is in compliance with SDPX,
meaning that if present, the scanning process will not be triggered. It is useful for checking the value before using it on the `failOn` option:
This command is intended to be used as a standalone functionality to check whether the value supplied is in compliance with SDPX. It is useful for checking the value before using it with the `scan` command:

```sh
npx @onebeyond/license-checker --checkLicense "(MIT OR GPL-1.0+) AND 0BSD"
npx @onebeyond/license-checker check "(MIT OR GPL-1.0+) AND 0BSD"
```

If the value provided is not SPDX compliant, the process fails (exit error 1).

### failOn
### scan command

All the values provided in the list must be [SPDX](https://spdx.dev/specifications/) compliant. Otherwise, an error will be thrown (exit error 1).
Check the [SPDX license list](https://spdx.org/licenses/)
All the values provided in the `failOn` list must be [SPDX](https://spdx.dev/specifications/) compliant. Otherwise, an error will be thrown (exit error 1).
Check the [SPDX license list](https://spdx.org/licenses/).

```sh
npx @onebeyond/license-checker --failOn MIT GPL-1.0+
npx @onebeyond/license-checker scan --failOn MIT GPL-1.0+
```

The input list is transformed into a SPDX expression with the `OR` logical operator. In the example, that is `MIT OR GPL-1.0+`.
Expand Down
10 changes: 10 additions & 0 deletions cli.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const yargs = require('yargs')(process.argv.slice(2));

// https://github.com/yargs/yargs/blob/main/docs/advanced.md#commanddirdirectory-opts
module.exports = yargs
.parserConfiguration({ 'camel-case-expansion': false })
.commandDir('commands')
.demandCommand()
.help()
.alias('help', 'h')
.argv;
16 changes: 16 additions & 0 deletions commands/check.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
const { check } = require('../src/runner');

exports.command = 'check <license>';

exports.describe = 'check if a license is SPDX compliant';

exports.handler = function (argv) {
const { license } = argv;

try {
check(license);
} catch (e) {
console.error(e.message);
process.exit(1);
}
};
63 changes: 34 additions & 29 deletions src/args.js → commands/scan.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const yargs = require('yargs');
const { scan } = require('../src/runner');

let timestamp = new Date().toISOString();

Expand All @@ -7,49 +7,54 @@ if (process.platform === 'win32') {
timestamp = timestamp.replace(/:/gi, '.');
}

/**
* Definition of all of the different arguments that can be passed to the
* license checker utility via CLI.
*/
module.exports = yargs
.option('start', {
exports.command = ['scan', '$0'];

exports.describe = 'scan licenses of a project looking for forbidden licenses';

exports.builder = {
start: {
description: 'path of the initial json to look for',
type: 'string',
default: process.cwd()
})
.option('failOn', {
},
failOn: {
description: 'fail (exit with code 1) if any package license does not satisfies any license in the provided list',
type: 'array'
})
.option('outputFileName', {
type: 'array',
demandOption: true
},
outputFileName: {
description: 'name of the output file generated',
type: 'string',
default: `license-report-${timestamp}`
})
.option('errorReportFileName', {
},
errorReportFileName: {
description: 'name of the file generated when a license in the failOn option is found',
type: 'string',
default: `license-error-${timestamp}`
})
.option('disableErrorReport', {
},
disableErrorReport: {
description: 'flag to disable the error report file generation',
type: 'boolean',
default: false
})
.option('disableReport', {
},
disableReport: {
description: 'flag to disable the report file generation, whether there is an error or not',
type: 'boolean',
default: false
})
.option('customHeader', {
},
customHeader: {
description: 'name of a text file containing the custom header to add at the start of the generated report',
type: 'string'
})
.option('checkLicense', {
description: 'check if a license is SPDX compliant. It is intended to be used as a standalone command. More info at https://spdx.org/licenses/',
type: 'string',
default: false
})
.help()
.alias('help', 'h')
.argv;
}
};

exports.handler = async function (argv) {
const { _, $0, ...options } = argv;

try {
await scan(options);
} catch (e) {
console.error(e.message);
process.exit(1);
}
};
15 changes: 0 additions & 15 deletions index.js

This file was deleted.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "@onebeyond/license-checker",
"version": "1.0.4",
"description": "Audit your NPM dependencies and reject any forbidden license.",
"main": "index.js",
"main": "cli.js",
"bin": {
"license-checker": "./index.js"
},
Expand Down
45 changes: 23 additions & 22 deletions src/runner.js
Original file line number Diff line number Diff line change
@@ -1,35 +1,35 @@
const isSPDXCompliant = require('spdx-expression-validate');

const checker = require('./checker');
const reporter = require('./reporter');

const {
getPackageInfoList, formatForbiddenLicenseError, generateSPDXExpression, checkSPDXCompliance, checkPackagesLicenses, isLicenseError, checkLicenseError
} = require('./utils');

const run = async (checker, reporter, args) => {
if (args.checkLicense) {
const license = args.checkLicense;

// @TODO Remove after issue has been solved
if (isLicenseError(license)) {
throw new Error(
'GFDL-1.x licenses are temporary unallowed. There\'s an issue pending to solve.'
);
}

if (!isSPDXCompliant(license)) {
throw new Error(`Error: License "${license}" is not SPDX compliant. Please visit https://spdx.org/licenses/ for the full list`);
}
const check = (license) => {
// @TODO Remove after issue has been solved
if (isLicenseError(license)) {
throw new Error(
'GFDL-1.x licenses are temporary unallowed. There\'s an issue pending to be solved. 🙏'
);
}

console.info(`License ${license} is SPDX compliant`);
return;
if (!isSPDXCompliant(license)) {
throw new Error(`Error: License "${license}" is not SPDX compliant. Please visit https://spdx.org/licenses/ for the full list of valid licenses.`);
}

const { failOn } = args;
console.info(`License ${license} is SPDX compliant`);
};

const scan = async (options) => {
const { failOn } = options;

checkLicenseError(failOn); // @TODO Remove after issue has been solved
checkSPDXCompliance(failOn);
const bannedLicenses = generateSPDXExpression(failOn);

const packages = await checker.parsePackages(args.start);
const packages = await checker.parsePackages(options.start);
const packageList = getPackageInfoList(packages);

const { forbidden: forbiddenPackages, nonCompliant: invalidPackages } = checkPackagesLicenses(bannedLicenses, packageList);
Expand All @@ -38,17 +38,18 @@ const run = async (checker, reporter, args) => {
}

if (forbiddenPackages.length) {
reporter.writeErrorReportFile(args.errorReportFileName, forbiddenPackages);
reporter.writeErrorReportFile(options.errorReportFileName, forbiddenPackages);
throw new Error(formatForbiddenLicenseError(forbiddenPackages));
}

console.info('License check completed! No forbidden licenses packages found.');

if (args.disableReport) return;
if (options.disableReport) return;

reporter.writeReportFile(args.outputFileName, packageList, args.customHeader);
reporter.writeReportFile(options.outputFileName, packageList, options.customHeader);
};

module.exports = {
run
check,
scan
};
42 changes: 42 additions & 0 deletions test/check.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
const { check } = require('../src/runner');

describe('check command', () => {
it('should throw an error if the license is not SPDX compliant', async () => {
const license = 'GPL';

let error;
try {
await check(license);
} catch (e) {
error = e;
} finally {
expect(error.message).toMatch(`Error: License "${license}" is not SPDX compliant`);
}
});

it('should not throw an error if the license is SPDX compliant', async () => {
const license = 'MIT';

let error;
try {
await check(license);
} catch (e) {
error = e;
} finally {
expect(error).toBeUndefined();
}
});

it('should throw an error if the alicense is one of the licenses not supported by the spdx-satisfies module', async () => {
const license = 'GFDL-1.1-invariants-or-later';

let error;
try {
await check(license);
} catch (e) {
error = e;
} finally {
expect(error.message).toMatch('GFDL-1.x licenses are temporary unallowed. There\'s an issue pending to be solved. 🙏');
}
});
});
Loading

0 comments on commit cd53a8d

Please sign in to comment.