好奇心の足跡

飽きっぽくすぐ他のことをしてしまうので、忘れないため・形にして頭に残すための備忘録。

CIでOSSライセンスを自動チェック ~npm編2~

今日は下記の記事で紹介していた、CIでOSSライセンスを自動チェック、のnpm辺について、別のツールを使った方法を紹介します。

kusuwada.hatenablog.com

というのも、こちらの記事で紹介した grunt-license-report を職場で他のチームに導入しようとしたところ、同僚に「え、今更gruntとか将来性がなくね?このモジュール4年も更新されてないし」的なコメントを頂き、「あー、ツールの活性度とかあまり考えてなかったわ!」と反省したところなのでした。

そこで今回は、数あるnpmで管理されているライブラリのライセンスを収集するツールの中から、 license-checker を使ってCIに組み込むところまでをやってみます。

github.com

2019年2月現在、Star数781, Fork 111, last published が a month ago、つまり先月ってことで十分活発なOSSではないでしょうか!

OSS情報一覧を生成する

対象のリポジトリ構成は前回と同じにします。

.
├── app
│   ├── front
│   │   ├── main.js
│   │   ├── package.json
│   └── test
│       ├── package.json
│       └── sample_test.js
├── infra
└── tool
    └── license_check
        ├── license_config.yml
        └── npm
            ├── judge_npm_license.js
            └── package.json

ライセンスを確認したいモジュールは、 app/front/package.json の dependencies とします。※package.jsonの devDependencies は対象外とします。

tool 配下は今回のライセンスチェックのためのツールです。

まずは license-checker をglobalにinstallします。

$ npm install -g license-checker

app/front ディレクトリで、package.json に記載されたpackage(とそれが依存しているpackage)をinstallします。

$ npm install

あとは license-checker --production コマンドを走らせるだけです。
ここで --production を指定しなければ、devDependencies も対象になります。

$ license-checker --production
├─ accepts@1.3.5
│  ├─ licenses: MIT
│  ├─ repository: https://github.com/jshttp/accepts
│  ├─ path: /Users/natsumi/workspace/git/npm_license_ci/app/front/node_modules/accepts
│  └─ licenseFile: /Users/natsumi/workspace/git/npm_license_ci/app/front/node_modules/accepts/LICENSE
├─ array-flatten@1.1.1
│  ├─ licenses: MIT
│  ├─ repository: https://github.com/blakeembrey/array-flatten
│  ├─ publisher: Blake Embrey
│  ├─ email: hello@blakeembrey.com
│  ├─ url: http://blakeembrey.me
│  ├─ path: /Users/natsumi/workspace/git/npm_license_ci/app/front/node_modules/array-flatten
│  └─ licenseFile: /Users/natsumi/workspace/git/npm_license_ci/app/front/
~~(中略)~~
node_modules/xml2js/LICENSE
└─ xmlbuilder@9.0.7
   ├─ licenses: MIT
   ├─ repository: https://github.com/oozcitak/xmlbuilder-js
   ├─ publisher: Ozgur Ozcitak
   ├─ email: oozcitak@gmail.com
   ├─ path: /Users/natsumi/workspace/git/npm_license_ci/app/front/node_modules/xmlbuilder
   └─ licenseFile: /Users/natsumi/workspace/git/npm_license_ci/app/front/node_modules/xmlbuilder/LICENSE

こんな感じでだーーーーっと出力されます。
実行時のOptionで、開発用のpackageのみを対象にしたり、推測によるライセンス出力をすべてunknownと出力させたり、出力形式をjsoncsvにしたりできます。Optionの詳細はこちらを参照

--unknown optionは、個人的には安全側に倒すために入れておいたほうが良さそうかなーと思います。

csv形式で出力するとこんな感じ。非常に見やすいですね。

f:id:kusuwada:20190206113555p:plain
csv_output

今回、このファイルは下記のようにして作成、出力しています。

$ license-checker --production --csv --out report/licernses.csv --unknown

許可していないライセンスのOSSがないかをチェック

ここからはCIに組み込んだりして、許可していないライセンスのOSSがないかをチェックします。

上記で tool 配下に配置されているライセンスチェックのためのスクリプト群の中身はこんな感じ。

license_config.yml

# 許可するライセンスのリスト。以下のリストは例。
allowed:
  - MIT
  - Apache-2.0
  - BSD-3-Clause
  - ISC
  - Apache 2.0

# 許可するライセンス一覧にはないが、ライセンスに問題が無いことが確認できたライブラリ
reviewed:
  npm:
    - hogehoge
  pip:
    - hogehoge

# チェック対象外のライブラリ
ignored:
  npm:
    - my-app # my-app
  pip:
    - my-app # my-app

tool/license_check/npm/package.json

{
  "name": "grunt-license-tools",
  "version": "0.0.0",
  "license": "UNLICENSED",
  "dependencies": {},
  "devDependencies": {
    "fs": "latest",
    "js-yaml": "latest"
  },
  "private": true
}

tool/license_check/npm/judge_npm_license.js

/********************************************************************
* 説明
* ==========
*
* license-checker で作成したライセンス一覧ファイル(csv)をパースし、
* 指定されたconfigファイルで許可されていないライセンスのライブラリがないかをチェックします。
* ※csv形式のファイル出力オプション付き license-checker コマンド
* license-checker --csv --out {report_file_name}.csv
*
* パラメータ
* ==========
*
* 1. パース対象のlicense一覧 csv ファイルパス
*    license-checker で作成したライセンス一覧ファイルのパス
*    e.g.) report/licenses.csv
* 
* 2. configファイルパス
*    使用を許可するライセンスや、確認済みのライブラリの設定が書かれたconfig fileのパス
*    e.g.) license_check_config.yml
*
* 返却値
* ==========
*
* exit 0: warningなしの場合
* exit 1: warningありの場合
*
********************************************************************/

const fs = require('fs');
const yaml = require('js-yaml')

if (process.argv.length < 4) {
    throw new Error('Insufficient number of arguments.');
}

const target_path = process.argv[2];
const config_path = process.argv[3];

var lines = fs.readFileSync(target_path, 'utf-8');

let libraries = {};
let warnings = {};

for (line of lines.split('\n')) {
    let project_name = ''
    name_with_version = line.split(',')[0].replace(/\"/g,'');
    library_name = name_with_version.split('/').pop().split('@')[0];
    if (library_name != 'module name') {
        license = line.split(',')[1].replace(/\"/g,'');
        libraries[library_name] = license;
    }
};

const config = yaml.safeLoad(fs.readFileSync(config_path), 'utf-8')

Object.keys(libraries)
    .filter(key => !config.allowed.includes(libraries[key]))
    .filter(key => !config.reviewed.npm.includes(key))
    .filter(key => !config.ignored.npm.includes(key))
    .forEach(key => {
        warnings[key] = libraries[key]
    });

// result
console.log('warnings: ')
console.log(warnings)
const result = 'RESULT: ' + Object.keys(libraries).length + ' dependencies checked, ' + Object.keys(warnings).length + ' warnings found.'
console.log(result)


// judge
if (Object.keys(warnings).length > 0) {
    console.log('license check failed.')
    process.exit(1)
}

必要なモジュールをinstallして、スクリプトを実行します。

$ cd tool/license_check/npm
$ npm install
$ node judge_npm_license.js ../../../app/front/report/licenses.csv ../license_config.yml 

実行結果(例)

warnings: 
{ argv: 'MIT*',
  atob: '(MIT OR Apache-2.0)',
  'css-select': 'BSD-like',
  domutils: 'BSD*',
  'my-app': 'UNKNOWN' }
RESULT: 290 dependencies checked, 5 warnings found.
license check failed.

warningが1つでもあれば異常終了、0件で正常終了します。
今回は特殊な書き方のライセンスが見つかっているので、一つ一つチェックし、問題なければ設定ファイル (license_check_config.yml) の allowed, reviewed, ignored の適切な箇所に追記します。
すべてのライセンスが問題なし、もしくはレビュー済・対象外になっている場合、下記のような出力になります。

warnings: 
{}
RESULT: 290 dependencies checked, 0 warnings found.

まとめ

今回は、license-checkerを使って、npmで管理されているOSSライブラリのライセンスに問題ないかを確認する方法を紹介しました。また、これをCIに組み込むなどして、自動でチェックする際に利用できるスクリプト群を紹介しました。
前回紹介した grunt-license-report と比較して、今回のpackage.jsonでは特に精度・パフォーマンス的な違いは見られませんでしたが、同僚からのアドバイスの通り、license-checkerのほうがよく使われている・メンテナンスがされている、という点でおすすめです。

関連記事

kusuwada.hatenablog.com kusuwada.hatenablog.com kusuwada.hatenablog.com kusuwada.hatenablog.com