1

I am trying to implement the license checking of the installed packages in my react project on a GitLab CI/CD pipeline. Before the build stage, the pipeline should check all the available licenses and then whitelist or blacklist certain specified licenses.

I am using the license-checker package to implement a list of available licenses in a JSON file. After executing the required command: license-checker --json > ./license.json, the output is:

license.json

{
  "@babel/plugin-transform-parameters@7.10.1": {
    "licenses": "MIT",
    "repository": "https://github.com/babel/babel",
    "path": "..../node_modules/@babel/plugin-transform-parameters",
    "licenseFile": "...../node_modules/@babel/plugin-transform-parameters/LICENSE"
  },
  "@babel/plugin-transform-property-literals@7.10.1": {
    "licenses": "MIT",
    "repository": "https://github.com/babel/babel",
    "path": "..../node_modules/@babel/plugin-transform-property-literals",
    "licenseFile": "...../node_modules/@babel/plugin-transform-property-literals/LICENSE"
  },
  "@babel/plugin-transform-react-constant-elements@7.10.1": {
    "licenses": "MIT",
    "repository": "https://github.com/babel/babel",
    "path": "..../node_modules/@babel/plugin-transform-react-constant-elements",
    "licenseFile": "...../node_modules/@babel/plugin-transform-react-constant-elements/LICENSE"
  }
  // .........and list goes on
}

.gitlab-ci.yml

include:
  - local: license-checker-config.yml

stages: 
  - dependency

dependency:
  image: node:12
  stage: dependency
  script: 
    - npm ci
    - echo "main file...."

license-checker-config.yml

before_script: 
  - ./license.json
  - echo "Checking licenses..."

License scanning should be initiated before the build process, so I included as a part of before_script. In license-checker-config.yml, I need to include my JSON file and then check by iterating over it, if it contains license like MIT, then only the build stage should continue otherwise the build should fail.

With my current code setup, I executed the pipeline and got the error:

Executing "step_script" stage of the job script
00:01
$ ./license.json
/bin/bash: line 99: ./license.json: Permission denied
ERROR: Job failed: exit code 1

Even though the file license.json exists in the same root folder, it shows permission denied. Further, I am unable to figure out how to implement JSON file looping inside the yml file and then achieve the required.

Any help to get me through this is highly appreciated.

bubble-cord
  • 101
  • 7
  • 30
  • Did you solve your problem? – JRichardsz Aug 01 '20 at 15:25
  • @JRichardsz Nope. Still figuring it out – bubble-cord Aug 01 '20 at 16:46
  • #1 Where are you running this sentence `license-checker --json > ./license.json` ? Before or after **echo "Checking licenses..."** in your **license-checker-config.yml**? #2 Is mandatory to check licenses before build? I have an idea that could work. #3 Where are you read **license.json** and search for MIT string ? – JRichardsz Aug 01 '20 at 16:56
  • @JRichardsz #1. I run `license-checker --json > ./license.json` manually first into the CLI for now, but it also works if I include it in the script of the gitlab-ci.yml file. #2. Not mandatory to check the licenses before the build but I thought it would save pipeline operation for creating a react build first and then checking the licenses used in the code. But suggestions are welcome if things work. #3. In my file `license-checker-config.yml`, I m reading the license.json. I want to include the search for MIT license in this file, but is unable to figure out how to do that. – bubble-cord Aug 01 '20 at 17:22
  • #1 Do you have access to the source code in **before_script** stage? #2 Are you able to call an sh script in **before_script** ? – JRichardsz Aug 01 '20 at 17:37
  • What is meant by the 'access to source code in before_script'? Can you please elaborate? – bubble-cord Aug 01 '20 at 17:52
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/219062/discussion-between-bubble-cord-and-jrichardsz). – bubble-cord Aug 01 '20 at 18:33

2 Answers2

2

Easy mode (nodejs)

You can add a script in your package.json (source code of your app) with name validate_licenses.js

  "scripts": {
    "start": "...",
    "build": "...",
    "validate_licenses": " node validate_licenses.js"
  }

Put the logic of license validation in validate_licenses.js:

Finally just execute in any part of your pipeline:

npm run validate_licenses

This will fail like a test and the build process will be interrupted.

- force json creation and parse it

const { exec } = require("child_process");
const license_checker = require('license-checker')
var fs = require('fs');

exec("license-checker --json > ./license.json", (error, stdout, stderr) => {
    if (error) {
        console.log(`error: ${error.message}`);  return;
    }
    if (stderr) {
        console.log(`stderr: ${stderr}`);return;
    }
    console.log(`json created`);
    parseJsonLicenses();
});

function parseJsonLicenses(){
  var licenses = JSON.parse(fs.readFileSync('/license.json', 'utf8'));
  //iterate licenses and fail if exist one licence differet of MIT
  for(var npmModule in licenses){
   if(licenses[npmModule].licenses != 'MIT')
     throw new Error(npmModule+' has a not allowed license');
  }  
}

More information in its official site:

- avoid json generation and use [onlyAllow] option

var checker = require('license-checker');
var config = {
  start: '.' ,
  onlyAllow: 'MIT'
};
checker.init(config, function(json, err) {
    if (err) {
        throw new Error(err);
    } else {
        console.log (JSON.stringify (json))
    }
});

I tested in one of my on projects and I get this error:

Package "@csstools/convert-colors@1.4.0" is licensed under "CC0-1.0" which is not permitted by the --onlyAllow flag. Exiting.

Hard mode (shell commands)

- parse previously generated json

You can read, iterate an find MIT word with pure shell commands but it will be a hard task.

Check this: Iterating through JSON array in Shell script

If you succeed, you can invoke the validate_licenses.sh in your git lab ci:

test:
    stage: test
    script:
        - echo 'starting licenses validation'
        - ./validate_licenses.sh

- parse previously generated simple file

According to this post : https://medium.com/@fokusman/the-easiest-way-to-check-all-your-npm-dependency-licenses-753075ef1d9d

It is possible to get a summarized count of licenses

> license-checker --summary

├─ MIT: 949
├─ ISC: 115
├─ BSD-2-Clause: 24
├─ CC0-1.0: 23
├─ BSD-3-Clause: 18
├─ Apache-2.0: 18
├─ CC-BY-4.0: 2
├─ BSD*: 2

Finally you can check if this file contains a specific string and throw an error


Mixture (nodejs commands in git lab ci)

If you can execute nodejs commands in your git lab ci, you can invoke validate_licenses.js directly if exists:

test:
    stage: test
    script:
        - echo 'starting licenses validation'
        - node validate_licenses.js

References

JRichardsz
  • 7,787
  • 2
  • 36
  • 61
  • Can you help it elaborate more on this, on how I can include it in .yml file? I am not very familiar with CI processes – bubble-cord Aug 01 '20 at 17:46
  • @JRichardz. As per my question, just for knowing, Is it not possible to directly manipulate the JSON file to search for a license in the .yml file? Without the need to create the extra .js file? – bubble-cord Aug 01 '20 at 18:12
  • Hi. This new js file must be part of your code like the js files used in unit test. – JRichardsz Aug 01 '20 at 18:32
  • I used the easy mode, and it gives error: `node validate_licenses.js internal/modules/cjs/loader.js:968 throw err; ^ Error: Cannot find module 'license-checker' Require stack:` – bubble-cord Aug 02 '20 at 08:54
  • HI. It was a snippet. You can check the official repository : https://www.npmjs.com/package/license-checker. Also did you install it npm install -g license-checker ? – JRichardsz Aug 02 '20 at 15:53
  • I changed from this `const license_checker = require('license-checker');` to this `import license_checker from "license-checker";`. And got new error `Warning: To load an ES module, set "type": "module" in the package.json or use the .mjs extension.` – bubble-cord Aug 02 '20 at 17:19
  • If `const license_checker = require('license-checker');` is used, it still cannot find the package even if it has been installed. – bubble-cord Aug 02 '20 at 17:23
  • I think, the `generateJson()` in license_checker.generateJson("/license.json");, is not a valid function? – bubble-cord Aug 02 '20 at 17:58
  • Yes. I said it was a not tested snippet. I update it – JRichardsz Aug 02 '20 at 19:24
  • Thanks a lot. It seemed to work. Can you help me a bit just to explain what these lines are doing? In this, if I do `console.log(licenses)` it gives an object: `{ licenses: 'UNLICENSED', private: true, path: '/builds/priya0607/pipeline-test', licenseFile: '/builds/priya0607/pipeline-test/license.json' }`. How is this object checking the licenses? – bubble-cord Aug 03 '20 at 15:08
  • Also, if using this method: `avoid json generation and use [onlyAllow] option`, it alwasys gives error: `Error: Cannot find module 'license-checker'`, Even when it is installed – bubble-cord Aug 03 '20 at 15:52
  • There is something rare in your workspace. That code was tested without problems. Did you install the required npm module? – JRichardsz Aug 03 '20 at 17:00
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/219136/discussion-between-bubble-cord-and-jrichardsz). – bubble-cord Aug 03 '20 at 17:34
1

I think this is because of the folder didn't exist till the time you were checking json file in it.

What my recommendation is provide the before script for running the build and then search for the json file in the folder like you are doing in your step definition.

Dharman
  • 21,838
  • 18
  • 57
  • 107
Vishal Gupta
  • 211
  • 1
  • 8