🐧

Go で CLI を配布する際にライブラリのライセンス文書も含める

に公開2

本記事では Go で CLI を開発して OSS として公開する際に依存するライブラリなどのライセンス文書を一緒に配布するようにした話を紹介します。

Go で CLI をビルドして OSS として公開する場合、バイナリには依存するライブラリ (標準ライブラリを含む) のコードが含まれます。
そのため、 MIT や BSD-3 ライセンスなど多くのライセンスではライセンス文書も一緒に配布する必要があります。

https://opensource.org/license/mit

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

https://opensource.org/license/bsd-3-clause

Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

どういった対応が必要かは当然ライセンスによって異なるので、依存するライブラリのライセンスが何でどういった対応が必要か確認する必要があります。

go-licenses

そこで google/go-licenses というツールを使いました。
例えば aqua について調べたい場合、リポジトリ直下で以下のコマンドを実行します。

問題のあるライセンスのライブラリに依存してないか:

go-licenses check ./cmd/aqua

ライセンスのリストを出力:

go-licenses report ./cmd/aqua > LICENSES

ライブラリとライセンスの一覧が出力されるので、そこからライセンスのリストを抽出します。

$ cut -d, -f3 LICENSES | sort -u
0BSD
Apache-2.0
BSD-2-Clause
BSD-3-Clause
MIT
MPL-2.0

ライセンス文書を出力:

go-licenses save ./cmd/aqua --save_path third_party_licenses

Pull Request の CI で go-licenses check でライセンスをチェックし、リリース時に CI で go-licenses save でライセンス文書をバイナリと一緒に配布すればよいでしょう。

GoReleaser と組み合わせる

自分は GoReleaser を使って tarball (Windows では Zip) を生成しているので、 GoReleaser の前に go-licenses save でライセンス文書を生成し、 GoReleaser で archive を生成する際に含めるようにしました。

.gitignore
third_party_licenses
.goreleaser.yaml
archives:
  - name_template: "{{.ProjectName}}_{{.Os}}_{{.Arch}}"
    format_overrides:
      - goos: windows
        formats: [zip]
    files:
      - LICENSE
      - README.md
      - third_party_licenses/**/*
go-licenses save ./cmd/aqua --save_path third_party_licenses

Go 自体のライセンス文書も含める

Go は BSD-3 ライセンスであり、 Go の標準ライブラリを使っていれば Go のライセンス文書も含める必要があります。

https://golang.org/LICENSE?m=text で最新のライセンス文書が取得できますが、ビルド時のバージョンの Go のライセンス文書を含めるべきでしょう。

1.24.5 のライセンス: https://github.com/golang/go/blob/go1.24.5/LICENSE
Go のバージョンは go env GOVERSION で取得できます。

go-licenses の出力先の directory の go/LICENSE に出力するようにしました。

version=$(go env GOVERSION)
curl -Lq -o third_party_licenses/go/LICENSE --retry 5 "https://raw.githubusercontent.com/golang/go/refs/tags/${version}/LICENSE"

Reusable Workflow で go-licenses を実行

自分は様々な Go の CLI をメンテしているため、メンテを楽にするために Reusable Workflow で共通化しています。

https://github.com/suzuki-shunsuke/go-release-workflow/blob/4e688e779e85af46e57ea7e79dbe0685e20c27c9/.github/workflows/release.yaml#L74-L85

aqua の tarball をみてみる

v2.53.5 の tarball をダウンロード
gh release download v2.53.5 -R aquaproj/aqua -p aqua_darwin_arm64.tar.gz
$ tar ztf aqua_darwin_arm64.tar.gz                                        
LICENSE
README.md
third_party_licenses/dario.cat/mergo/LICENSE
third_party_licenses/github.com/Masterminds/goutils/LICENSE.txt
third_party_licenses/github.com/Masterminds/semver/v3/LICENSE.txt
third_party_licenses/github.com/Masterminds/sprig/v3/LICENSE.txt
third_party_licenses/github.com/STARRY-S/zip/LICENSE
...
third_party_licenses/go/LICENSE
...
aqua

aqua のバイナリと一緒に Go と依存するライブラリのライセンス文書が含まれているので、これで問題ないはずです。
aqua 以外にも tfcmt や pinact, ghalint など自分がメンテしているほぼ全ての Go の CLI で対応しました。

go-licenses check が失敗したケース

lintnet という linter に上記の対応をしている際、 go-licenses check が失敗しました。
v0.4.11 で修正されています。

v0.4.10 のライセンスをチェック
git checkout v0.4.10
go-licenses check ./cmd/lintnet

以下のようなエラーが発生しました。

E0719 10:32:17.485447   86243 library.go:122] Failed to find license for github.com/xi2/xz: cannot find a known open source license for "/Users/shunsukesuzuki/go/pkg/mod/github.com/xi2/xz@v0.0.0-20171230120015-48954b6210f8" whose name matches regexp ^(?i)((UN)?LICEN(S|C)E|COPYING|README|NOTICE).*$ and locates up until "/Users/shunsukesuzuki/go/pkg/mod/github.com/xi2/xz@v0.0.0-20171230120015-48954b6210f8"
Unknown license type  found for library github.com/xi2/xz

https://github.com/xi2/xz に依存しているけど、それのライセンスが見つからないようでした。
確認したところ LICENSE はありました し、 Public Domain なので問題ないように思えます。
ですが、最終更新日時が 8 年前ですし、ちゃんとメンテされているようには見えません。

なぜこれに依存しているのか確認したところ、 https://github.com/mholt/archiver が依存していました。

$ go mod why github.com/xi2/xz
# github.com/xi2/xz
github.com/lintnet/lintnet/pkg/module
github.com/mholt/archiver/v3
github.com/xi2/xz

mholt/archiver は既に archive されていて、後継の https://github.com/mholt/archives への移行が推奨されています (aqua では既に移行していました) 。
なのですが、移行がちょっと面倒なのと、 aqua と違って lintnet は tarball を展開しているだけです。
tarball を展開するだけなら標準ライブラリでできますし、 https://github.com/mholt/archives を使うまでもない気がしたので、標準ライブラリを使って書き換えることにしました。
書き換えたことで https://github.com/xi2/xz への依存がなくなり、 go-licenses のエラーも解消しました。

CLI を開発している人は似たような問題があるかもしれないのでチェックしてみるとよいでしょう (lintnet 以外では上記のような問題は見つかりませんでした)。

ちなみに mholt/archives で関連する修正を見つけました。

https://github.com/mholt/archives/pull/41

PR には

The original module github.com/xi2/xz/ is in the Public Domain but does not explicitly specify a license.

と書いてありますが、 LICENSE には

All these files have been put into the public domain. You can do whatever you want with these files.

と書いてあり、正直よく分かりませんでした。

さいごに

以上、 Go の CLI を配布する際に Go や依存する Go のライブラリのライセンス文書も一緒に配布するようにした話を紹介しました。
ちなみに、著名な OSS でもライセンス文書を適切に配布していないケースは多いと思います。
例えば、 GitHub CLI を確認しましたが、特に依存するライブラリのライセンスの配布はしていませんでした。
なので、やっていないからといって大きな問題になることはそうそうないと思いますし、自分も今までちゃんとしてこなかったので偉そうなことは言えません。
ですが、ライセンス的には本来必要なことだと思いますし、胸を張って OSS 開発してますというためにも今後はちゃんとしていきたいなと思いました (ちゃんとしてなかったら優しく突っ込んでください)。

Discussion

あいや - aiya000あいや - aiya000

Go で CLI をビルドして OSS として公開する場合、バイナリには依存するライブラリ (標準ライブラリを含む) のコードが含まれます。

一般的な言語でのコンパイルは、バイトコードやminifyされたコードに変換すると思うのですが、Goってオリジナルのコードが含まれるんですか!?
それでは確かに、派生物になっちゃいますね⋯
知らなかった⋯。

あいや - aiya000あいや - aiya000

いや、そういえばライセンスにより、たしか「派生物」の定義(? ライセンスを載せる義務が発生する定義)が違いましたっけ⋯。

gccが使ってるCの標準ライブラリだったか? は、オリジナルのコードを利用するときだけじゃなくてバイトコードで静的リンクするときも発生して、動的リンクだと発生しなかった⋯?