みなさんは日々の開発でライブラリなどを利用する際に、きちんとライセンスを確認していますか?
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
├─ [email protected]
│ ├─ licenses: ISC
│ ├─ repository: https://github.com/isaacs/abbrev-js
│ ├─ publisher: Isaac Z. Schlueter
│ ├─ email: [email protected]
│ ├─ path: /Users/ynakamura/projects/sandbox/hello/node_modules/abbrev
│ └─ licenseFile: /Users/ynakamura/projects/sandbox/hello/node_modules/abbrev/LICENSE
├─ [email protected]
│ ├─ licenses: MIT
│ ├─ repository: https://github.com/chalk/ansi-styles
│ ├─ publisher: Sindre Sorhus
│ ├─ email: [email protected]
│ ├─ url: sindresorhus.com
│ ├─ path: /Users/ynakamura/projects/sandbox/hello/node_modules/ansi-styles
│ └─ licenseFile: /Users/ynakamura/projects/sandbox/hello/node_modules/ansi-styles/license
├─ [email protected]
│ ├─ licenses: MIT
│ ├─ repository: https://github.com/sindresorhus/array-find-index
│ ├─ publisher: Sindre Sorhus
│ ├─ email: [email protected]
│ ├─ 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
├─ [email protected]
│ ├─ 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"
"[email protected]","ISC","https://github.com/isaacs/abbrev-js"
"[email protected]","MIT","https://github.com/chalk/ansi-styles"
"[email protected]","MIT","https://github.com/sindresorhus/array-find-index"
"[email protected]","MIT","https://github.com/kriskowal/asap"
"[email protected]","MIT","https://github.com/juliangruber/balanced-match"
"[email protected]","MIT","https://github.com/juliangruber/brace-expansion"
"[email protected]","MIT","https://github.com/chalk/chalk"
"[email protected]","MIT","https://github.com/Qix-/color-convert"
"[email protected]","MIT","https://github.com/dfcreative/color-name"
"[email protected]","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自身の依存ツリーも結果に出てきてしまうこと。 ↩