Skip to content

Commit 2c511bc

Browse files
authored
『cargoプロジェクトで依存関係を継続的に管理する』を書いた (#217)
* 『cargoプロジェクトで依存関係を継続的に管理する』を書いた * fix * images
1 parent d5ab03a commit 2c511bc

File tree

3 files changed

+299
-0
lines changed

3 files changed

+299
-0
lines changed
47.4 KB
Loading
56.3 KB
Loading
Lines changed: 299 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,299 @@
1+
---
2+
title: "cargoプロジェクトで依存関係を継続的に管理する"
3+
tags: ["cargo", "rust", "OSS"]
4+
5+
cover: "https://blog.kyu08.com/cover.png"
6+
description: ""
7+
date: 2025-05-20T14:22:40+09:00
8+
author: "kyu08"
9+
authorTwitter: "kyu08_"
10+
draft: false
11+
showFullContent: false
12+
readingTime: true
13+
hideComments: false
14+
color: ""
15+
---
16+
17+
OSSに依存するプロジェクトでは依存しているライブラリのライセンス表記が必要になることがある。(体感的にはほとんどのライブラリはライセンス表記を求めているように思う)
18+
19+
そのため、依存ライブラリが増えた場合にはライセンス表記を更新する必要があるが、依存ライブラリの依存ライブラリに対しても再帰的にライセンス表記を見に行って自リポジトリの依存ライセンス表記ファイルを更新して...という作業を行うのは現実的ではない。
20+
21+
他にもうっかりコピーレフトなライブラリに依存してしまうとプロジェクトのライセンスを変更しなければならなかったりと案外注意すべきポイントが多い。
22+
23+
cargoプロジェクトにおいてこれらの労力を削減してくれるツールがいくつか存在している。
24+
25+
筆者は趣味プロジェクトとして`fzf-make`というmake targetやpnpm script, yarn script, just recipeをfuzzy finder形式で選択、実行できるCLIツール[^1]をRustで開発しており、このプロジェクトで最近それらのツールを導入したので使い方とともに紹介する。
26+
27+
## tl;dr
28+
以下のチェックをCIで実行することでcargoプロジェクトの依存関係を継続的に省力で管理できる。
29+
30+
- [`cargo-machete`](https://github.com/bnjbvr/cargo-machete): 不要な依存関係がないか
31+
- [`cargo-deny`](https://github.com/EmbarkStudios/cargo-deny): 依存ライブラリに許容しないライセンスが含まれていないか
32+
- [`cargo-about`](https://github.com/EmbarkStudios/cargo-about): リポジトリのライセンス表記ファイルに更新漏れがないか
33+
34+
それぞれ紹介する。なお、コード例はそれぞれ以下のランナーを想定している。
35+
36+
| 環境 | ランナー |
37+
|-- | -- |
38+
| ローカル | make |
39+
| CI | GitHub Actions |
40+
41+
## `cargo-machete`で不要な依存関係を検出する
42+
### why
43+
ある程度の期間開発を続けていると以前追加した依存関係が不要になったことに気付かずに`cargo.toml``dependencies``dev-dependencies`に残ったままになることがある。
44+
不要な依存関係が残ったままになっているとrenovateやdependabotによるバージョン更新の手間が不必要に増えてしまうのでPRの時点で気付けるようにしたい。
45+
46+
### what
47+
cargo-macheteを使って`cargo.toml`に記述されている依存関係のうち、プロジェクトが実際には依存していない依存関係を検出する。
48+
49+
### how(ローカル)
50+
以下のようなmake targetを定義しておくと`make detect-unused-dependencies`で不要な依存関係を検出できる。
51+
```makefile
52+
.PHONY: tool-detect-unused-dependencies
53+
tool-detect-unused-dependencies:
54+
@if ! which cargo-machete > /dev/null; then \
55+
cargo install --locked cargo-machete; \
56+
fi
57+
58+
.PHONY: detect-unused-dependencies
59+
detect-unused-dependencies: tool-detect-unused-dependencies
60+
cargo machete
61+
```
62+
63+
以下は不必要な依存関係として`tempfile`が検出されている様子。
64+
65+
![cargo-machete](cargo-machete.webp)
66+
67+
### how(CI)
68+
以下のようなyamlを定義しておくと不要な依存関係がある場合にCIが落ちてくれる。
69+
```yaml
70+
name: Detect unused dependencies
71+
on:
72+
push:
73+
branches: [ "main" ]
74+
pull_request:
75+
76+
jobs:
77+
detect-unused-dependencies:
78+
runs-on: ubuntu-latest
79+
steps:
80+
- name: Checkout
81+
uses: actions/checkout@v4
82+
- name: Machete
83+
uses: bnjbvr/cargo-machete@v0.8.0
84+
```
85+
86+
## `cargo-deny`で依存ライブラリに許容しないライセンスが含まれていないか検証する
87+
### why
88+
筆者は`fzf-make`をより自由に利用してほしいと考えているためMITライセンスでプロジェクトを公開したい。
89+
コピーレフトのライセンスに依存してしまうとライセンス変更を余儀なくされるため、コピーレフトのライブラリに依存することを避けたい。
90+
91+
### what
92+
`cargo-deny`は依存関係のlint機能を提供する。`fzf-make`では、`cargo deny check licenses`を用いてホワイトリストに登録したライセンス以外のライセンスが依存ライブラリに含まれていないか検証している。
93+
94+
```toml
95+
# deny.toml
96+
[licenses]
97+
allow = [
98+
"MIT",
99+
"ISC",
100+
"Apache-2.0",
101+
"MPL-2.0",
102+
"BSD-2-Clause",
103+
"BSD-3-Clause",
104+
"Unicode-DFS-2016",
105+
"OpenSSL",
106+
]
107+
confidence-threshold = 0.8
108+
exceptions = []
109+
110+
[[licenses.clarify]]
111+
name = "ring"
112+
expression = "MIT AND ISC AND OpenSSL"
113+
license-files = [
114+
{ path = "LICENSE", hash = 0xbd0eed23 }
115+
]
116+
```
117+
118+
### how(ローカル)
119+
以下のようなmake targetを定義しておくと`make check-licenses`で依存ライブラリに許容しないライセンスが含まれていないかを検証できる。
120+
```makefile
121+
.PHONY: tool-check-licenses
122+
tool-check-licenses:
123+
@if ! which cargo-deny > /dev/null; then \
124+
cargo install --locked cargo-deny; \
125+
fi
126+
127+
.PHONY: check-licenses
128+
check-licenses: tool-check-licenses
129+
cargo deny check licenses
130+
```
131+
132+
許可していないライセンスのライブラリに依存していることがわかると以下のようなエラーが出力される。
133+
134+
![cargo-deny.webp](cargo-deny.webp)
135+
136+
### how(CI)
137+
以下のようなyamlを定義しておくと不要な依存関係があるとCIが落ちてくれる。
138+
```yaml
139+
name: Check licenses
140+
on:
141+
push:
142+
branches: [ "main" ]
143+
pull_request:
144+
145+
jobs:
146+
check-licenses:
147+
runs-on: ubuntu-latest
148+
steps:
149+
- uses: actions/checkout@v4
150+
- uses: EmbarkStudios/cargo-deny-action@v2
151+
with:
152+
command: check licenses
153+
```
154+
155+
## `cargo-about`でリポジトリのライセンス表記が最新か検証する
156+
### why
157+
冒頭でも触れた通り、多くのOSSでは依存ライブラリのライセンス表記を求めている。
158+
159+
`fzf-make`では`CREDITS.html`というファイルを用意しておりそこに依存ライブラリのライセンス表記を記載している。
160+
161+
これまでは依存ライブラリを追加/削除した際に手作業でライセンス表記ファイルを更新していたが、漏れがちになっていた。
162+
163+
また、本来は依存ライブラリの依存ライブラリを再帰的にチェックしてすべてのライセンス表記をすべきなのだと思うが、手作業でそれをする気にはなれなかったので直接依存しているライブラリのライセンスしか表記できていなかった。
164+
165+
これらの課題を`cargo-about`を用いることで解決できそうだったので導入してみた。
166+
167+
### what
168+
`cargo-about`は依存ライブラリのライセンス表記ファイルを自動生成する機能を提供する。
169+
170+
以下のようなテンプレートを用意し、`cargo about generate about.hbs > CREDITS.html`を実行すると`CREDITS.html`にライセンス表記を出力してくれる。
171+
172+
```html
173+
<html>
174+
<head>
175+
<style>
176+
@media (prefers-color-scheme: dark) {
177+
body {
178+
background: #333;
179+
color: white;
180+
}
181+
a {
182+
color: skyblue;
183+
}
184+
}
185+
.container {
186+
font-family: sans-serif;
187+
max-width: 800px;
188+
margin: 0 auto;
189+
}
190+
.intro {
191+
text-align: center;
192+
}
193+
.licenses-list {
194+
list-style-type: none;
195+
margin: 0;
196+
padding: 0;
197+
}
198+
.license-used-by {
199+
margin-top: -10px;
200+
}
201+
.license-text {
202+
max-height: 200px;
203+
overflow-y: scroll;
204+
white-space: pre-wrap;
205+
}
206+
</style>
207+
</head>
208+
<body>
209+
<main class="container">
210+
<div class="intro">
211+
<h1>Third Party Licenses</h1>
212+
<p>This page lists the licenses of the projects used in fzf-make.</p>
213+
</div>
214+
215+
<h2>Overview of licenses:</h2>
216+
<ul class="licenses-overview">
217+
{{#each overview}}
218+
<li><a href="#{{id}}">{{name}}</a> ({{count}})</li>
219+
{{/each}}
220+
</ul>
221+
222+
<h2>All license text:</h2>
223+
<ul class="licenses-list">
224+
{{#each licenses}}
225+
<li class="license">
226+
<h3 id="{{id}}">{{name}}</h3>
227+
<h4>Used by:</h4>
228+
<ul class="license-used-by">
229+
{{#each used_by}}
230+
<li><a href="{{#if crate.repository}} {{crate.repository}} {{else}} https://crates.io/crates/{{crate.name}} {{/if}}">{{crate.name}} {{crate.version}}</a></li>
231+
{{/each}}
232+
</ul>
233+
<pre class="license-text">{{text}}</pre>
234+
</li>
235+
{{/each}}
236+
</ul>
237+
</main>
238+
</body>
239+
</html>
240+
```
241+
242+
243+
### how(ローカル)
244+
以下のようなmake targetを定義しておくと`make update-license-file`で依存ライブラリのライセンス表記が最新か検証できる。
245+
```makefile
246+
.PHONY: tool-update-license-file
247+
tool-update-license-file:
248+
@if ! which cargo-about > /dev/null; then \
249+
cargo install --locked cargo-about; \
250+
fi
251+
252+
.PHONY: update-license-file
253+
update-license-file: tool-update-license-file
254+
cargo about generate about.hbs > CREDITS.html
255+
```
256+
257+
### how(CI)
258+
以下のようなyamlを定義しておくと依存ライブラリのライセンス表記が最新でないとCIが落ちてくれる。
259+
```yaml
260+
name: Check license file
261+
on:
262+
push:
263+
branches: [ "main" ]
264+
pull_request:
265+
266+
jobs:
267+
check-license-file:
268+
runs-on: ubuntu-latest
269+
steps:
270+
- name: Checkout
271+
uses: actions/checkout@v4
272+
- name: Check
273+
run: make update-license-file
274+
- name: Check diff
275+
run: |
276+
git add .
277+
git diff --cached --exit-code
278+
```
279+
280+
なお、デフォルトだと以下の部分の`This page...fzf-make`の部分が`This page lists the licenses of the projects used in cargo-about.`として出力される。そのため、そのまま利用すると「`cargo-about`プロジェクトでは以下のライセンスに依存しています」という意味の表示になってしまうためユーザーが忘れずに`cargo-about`の部分をプロジェクト名に変更する必要があるという問題がある。[^2]
281+
282+
```html
283+
<div class="intro">
284+
<h1>Third Party Licenses</h1>
285+
<p>This page lists the licenses of the projects used in fzf-make.</p>
286+
</div>
287+
```
288+
289+
これに関しては以下のissueを立ててみたので`cargo-about`側と意見が一致したら修正するPRを送ってみようと思う。
290+
291+
[[Feature request] change `cargo about init` to generate the statement including user's project name.](https://github.com/EmbarkStudios/cargo-about/issues/281)
292+
293+
## おわりに
294+
繰り返しの定型タスクをCIに任せることができたので開発により集中できるようになった。(趣味開発だと数ヶ月ぶりにコミットするとかもザラにあり、開発に必要なタスクを忘れがちなのでそういった意味でも自動化の範囲を増やすことができてよかった)
295+
296+
便利ツールをOSSとして公開してくれている人/企業に感謝しつつ利用するだけでなく自分にできる貢献はやっていきたい。
297+
298+
[^1]: `fzf-make`の紹介記事: [[make,pnpm,yarn,justに対応]コマンドをfuzzy finder形式で選択できるCLIツール fzf-makeの紹介](https://zenn.dev/kyu08/articles/974fd8bc25c303)
299+
[^2]: そして実際に[90件近くのプロジェクトで`cargo-about`のままになっている。](https://github.com/search?q=%22This+page+lists+the+licenses+of+the+projects+used+in+cargo-about.%22&type=code)

0 commit comments

Comments
 (0)