みなさんは日々の開発でライブラリなどを利用する際に、きちんとライセンスを確認していますか?
OSS(に限らないですが)を利用する際には、ライセンスに従う必要があります。
ライセンスによっては著作権表示を求められたり、それらを組み込んだソースコードもまた公開しなければならないものもあります。
今回はlicense-checkerを用いて、Node.jsプロジェクト内で利用しているパッケージのライセンスを確認してみたいと思います。
license-checkerを使ってみる
license-checker
https://www.npmjs.com/package/license-checker
ツールなのでグローバルにインストールしてもいいと思いますが、個人的にはdevDependencies
に足したほうがチームで共有できたりCIなどでも利用できるのでよいかと思います。1
ここでは適当に作った空プロジェクトで試してみましょう。
license-checker
もまたnpmパッケージなので、色々な依存関係のあるパッケージが入ります。試すのにちょうど良いですね。
# 適当にプロジェクトを作る
mkdir hello
cd hello
yarn init -y
# license-checkerを入れる
yarn add -D license-checker
package.json
の中身はこんな感じです。
devDependencies
にlicense-checker
がいます。
{
"name": "hello",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"devDependencies": {
"license-checker": "^25.0.1"
}
}
準備はできました。
では早速実行してみましょう。
yarn license-checker
├─ abbrev@1.1.1
│ ├─ licenses: ISC
│ ├─ repository: https://github.com/isaacs/abbrev-js
│ ├─ publisher: Isaac Z. Schlueter
│ ├─ email: i@izs.me
│ ├─ path: /Users/ynakamura/projects/sandbox/hello/node_modules/abbrev
│ └─ licenseFile: /Users/ynakamura/projects/sandbox/hello/node_modules/abbrev/LICENSE
├─ ansi-styles@3.2.1
│ ├─ licenses: MIT
│ ├─ repository: https://github.com/chalk/ansi-styles
│ ├─ publisher: Sindre Sorhus
│ ├─ email: sindresorhus@gmail.com
│ ├─ url: sindresorhus.com
│ ├─ path: /Users/ynakamura/projects/sandbox/hello/node_modules/ansi-styles
│ └─ licenseFile: /Users/ynakamura/projects/sandbox/hello/node_modules/ansi-styles/license
├─ array-find-index@1.0.2
│ ├─ licenses: MIT
│ ├─ repository: https://github.com/sindresorhus/array-find-index
│ ├─ publisher: Sindre Sorhus
│ ├─ email: sindresorhus@gmail.com
│ ├─ url: sindresorhus.com
│ ├─ path: /Users/ynakamura/projects/sandbox/hello/node_modules/array-find-index
│ └─ licenseFile: /Users/ynakamura/projects/sandbox/hello/node_modules/array-find-index/license
├─ asap@2.0.6
│ ├─ licenses: MIT
│ ├─ repository: https://github.com/kriskowal/asap
│ ├─ path: /Users/ynakamura/projects/sandbox/hello/node_modules/asap
│ └─ licenseFile: /Users/ynakamura/projects/sandbox/hello/node_modules/asap/LICENSE.md
:
:
めちゃくちゃいっぱい出てきました。
プロジェクトの直接の依存パッケージであるlicense-checker
だけでなく、その依存ツリーのパッケージ全てが出力されます。
--direct
という直接の依存パッケージのみに絞るオプションもあるのですが、v25.0.1
の時点ではうまく機能しませんでした。2
色々な出力形式
CSVやJSON
--csv
や--json
オプションを指定すると、CSVやJSONといった形式で出力してくれます。
yarn license-checker --csv
"module name","license","repository"
"abbrev@1.1.1","ISC","https://github.com/isaacs/abbrev-js"
"ansi-styles@3.2.1","MIT","https://github.com/chalk/ansi-styles"
"array-find-index@1.0.2","MIT","https://github.com/sindresorhus/array-find-index"
"asap@2.0.6","MIT","https://github.com/kriskowal/asap"
"balanced-match@1.0.2","MIT","https://github.com/juliangruber/balanced-match"
"brace-expansion@1.1.11","MIT","https://github.com/juliangruber/brace-expansion"
"chalk@2.4.2","MIT","https://github.com/chalk/chalk"
"color-convert@1.9.3","MIT","https://github.com/Qix-/color-convert"
"color-name@1.1.3","MIT","https://github.com/dfcreative/color-name"
"concat-map@0.0.1","MIT","https://github.com/substack/node-concat-map"
:
:
ライセンス毎のサマリー
--summary
を指定するとライセンス毎のパッケージ数が出力されます。
ざっと把握するにはよいでしょう。
yarn license-checker --summary
├─ MIT: 32
├─ ISC: 19
├─ Apache-2.0: 2
├─ BSD-3-Clause: 1
├─ BSD-2-Clause: 1
├─ CC-BY-3.0: 1
├─ CC0-1.0: 1
└─ (MIT AND CC-BY-3.0): 1
その他のオプションなど
他にも特定のパッケージを除外したり、dependencies
のみを対象としたりするオプションがあります。
便利そうなオプションだけピックアップしておきます。
完全なオプション一覧はREADMEを見るか、--help
で確認してください。
オプション | 内容 |
---|---|
--summary |
ライセンス毎のサマリー表示る |
--production |
dependencies のみ出力する |
--development |
devDependencies のみ出力する |
--unknown |
推測したライセンスをunknown として扱う |
--onlyunknown |
unknown と推測のみ出力する |
--json |
JSON形式で出力する |
--csv |
CSV形式で出力する |
--exclude [list] |
指定のライセンスのパッケージを除外する(セミコロン区切り) |
--excludePackages [list] |
指定のパッケージを除外する(セミコロン区切り) |
--packages [list] |
指定のパッケージのみ出力する(セミコロン区切り) |
--excludePrivatePackages |
プラベートパッケージを除外する |
--failOn [list] |
指定のライセンスがあったらエラーにする(セミコロン区切り) |
--onlyAllow [list] |
指定のライセンス以外があったらエラーにする(セミコロン区切り) |
--direct |
直接の依存パッケージについてのみ出力する(v25.0.1現在うまく機能しない) |
どうやってライセンスを検出しているのか
ところでlicense-checker
はどうやってパッケージのライセンスを判定しているのでしょうか。
ドキュメントによると、node_modules
の中を順番に見ていき、各package.json
のlicense
から拾うようです。
package.json
から拾うのに失敗した場合は、LICENSE
, LICENCE
, COPYING
, README
といったファイルの中身を見て、ライセンス固有の文字列が含まれているかどうかで判定するようです。
応用編: CIやコミットフックなどで自動検出する
さて、ここからはCIなどに組み込んで、特定のライセンスが含まれていたら自動的に検出するような仕組みを考えてみたいと思います。
パッと思いつくのは--failOn
オプションですね。
これを使えば特定のライセンスが含まれていた場合に、コマンドの終了ステータスが1
になるので、これで検出することができそうです。
試してみましょう。
yarn license-checker --failOn MIT
Found license defined by the --failOn flag: "MIT". Exiting.
error Command failed with exit code 1.
うまくいったようです。
これをCIやGitのpre-commit
フックなどに組み込んでおくことで、いつのまにかGPLなライブラリが入り込んでいた、といった事態を避けることができます。
Gitのpre-commit
に設定してみる
下記のような感じのスクリプトを.git/hooks/pre-commit
に作ります(chmod +x
するのを忘れずに)。
$EDITOR .git/hooks/pre-commit
chmod +x .git/hooks/pre-commit
#!/bin/sh
# 許容しないライセンス
DISALLOW_LICENSES="GPL;GPL-2.0;GPL-3.0;LGPL;LGPL-3.0"
# package.json, package-lock.json, yarn.lock に変更があったときだけチェックする
if git diff --cached --name-only $against | grep --quiet --word-regexp -E 'package(-lock)?\.json|yarn\.lock'
then
result=0
yarn license-checker --failOn "$DISALLOW_LICENSES" >/dev/null || result=$?
if [[ ! "$result" = "0" ]]; then
yarn license-checker --csv
exit 1
fi
fi
exit 0
これでpackage.json
, package-lock.json
, yarn.lock
に変更があったときにライセンスチェックが走って、許容しないライセンスが含まれていた場合は、コミットに失敗するようになります。
コミットのたびに走ると重いので、パッケージ情報に変更があったときだけ走るようにしてみました。
CIでも同じようなスクリプトを書いて、最初に実行しておけばよいでしょう。
注意点
--exclude
や--failOn
等に指定するライセンス名ですが、完全一致でしかマッチしないようなので注意が必要です。
例えばGPL-3.0
なパッケージがあったとして、--failOn GPL
とか--failOn GPL3.0
とか--failOn gpl-3.0
としても引っかかりません。--failOn GPL-3.0
でなければいけません。
また、(MIT OR GPL-3.0)
のような表記(デュアルライセンス)もMIT
やGPL-3.0
や(GPL-3.0 OR MIT)
(逆順)では引っかかりません。--failOn "(MIT OR GPL-3.0)"
などと指定しないといけません。
このあたりをもう少し柔軟に判定しようとすると、grep
を駆使するとか、JSON形式をパースするといった対応が必要になってきます。
安全側に倒すなら--csv
の結果をgrep -i gpl
とかでチェックして、デュアルライセンスなど問題なさそうなものを随時--excludePackages
に足していくとかでもいいかもしれません。
ただし、--excludePackages
の場合、バージョンが上がってライセンスが変更されている場合に、気づかずに除外してしまうという、別の問題はありそうです。
応用編: ライブラリとして使う
license-checker
はライブラリとしても使えるインターフェースを提供しているので、前項のような複雑な条件に対応するスクリプトを書くとか、パッケージとライセンスの一覧をまとめた画面コードを生成するといった用途にも使えるかと思います。
以下のコードはREADMEからの引用。
var checker = require('license-checker');
checker.init({
start: '/path/to/start/looking'
}, function(err, packages) {
if (err) {
//Handle error
} else {
//The sorted package data
//as an Object
}
});
packages
が各パッケージ情報の配列になっていて、その中にパッケージ名やバージョン、ライセンスなどの情報が入っているようです。
その他のライセンスチェッカー
-
microsoft/license-checker-webpack-plugin
- Microsoft製
- JavaScript製
- Webpack pluginとして動く
- ビルド結果に含まれるものしかチェックできない
-
devDependencies
とかチェックできない
-
-
github/licensed
- GitHub製
- Ruby製
- 色々な言語のプロジェクトに対応している
-
franciscop/legally
- JavaScript製
- Node.jsプロジェクトのみ
- きれいなテーブルで表示してくれる
- package, License, READMEを別々に表示してくれる
-
go-enry/go-license-detector
- Go製
- 特定ディレクトリに対してチェックできる
-
LICENSE
やREADME
から判定 - 依存ツリーのチェックはできない
-
難点は
license-checker
自身の依存ツリーも結果に出てきてしまうこと。 ↩