はじめに
cargo releaseとはpublishにまつわる作業(gitのタグ付けやバージョン書き換えなど)を自動化してくれるcargoのサブコマンドです。2019-07-17リリースの0.12.0にてワークスペースに対応したのですが、いろいろと罠があるのでまとめておきます。
ワークスペースとは
ワークスペースとは複数のクレートを1つのディレクトリにまとめることができる機能です。詳細は"Cargoのワークスペース"を見てください。
使い道ですが、ドキュメントに書いてあるように「大規模なクレートを分割したい」ということはそれほどない気がします。よくあるのが次の2つです。
(これらもまぁ滅多にないといえばないですが…)
- procedural macro
- FFI
まずprocedural macroは必ず専用のクレートが必要です。そのためprocedural macroを使ったプロジェクトは(procedural macroのみのクレートでない限り)必ず2つ以上のクレートに分かれることになります。またFFIの場合は、-sys
というsuffixの付いたクレートでCの関数を単にRustに変換しただけのものを提供して、別クレートで安全なラッパーを提供する、ということがよく行われます。
大規模なクレートからライブラリを切り出したようなケース(例えばripgrepというgrepツールから汎用ライブラリとしてgrepを切り出したような)は各クレートのバージョンは独立に設定したいと思います。しかし先に挙げたようなケースでは、分かれたクレートは密接に関連していることが多いので、バージョンも同時に上げたいところです。cargo releaseがワークスペースを扱えるようになったことで、複数のクレートのバージョンアップを1コマンドで行えるようになりました。
0.12.0時点の問題点
pre-releaseが使えない
pre-releaseとはバージョンのsuffixに"-alpha.0"や"-rc"などを付けるフローです。これはcargo releaseのデフォルトのフローになっていて、以下のような動作をします。
- version = "0.1.0-alpha.0"で開発
- cargo releaseを実行
- (cargo releaseが)version = "0.1.0" に書き換え
- (cargo releaseが)コミット
- (cargo releaseが)タグ付け
- (cargo releaseが)cargo publish
- (cargo releaseが)version = "0.1.1-alpha.0" に書き換え
つまり普段の開発はずっとpre-releaseバージョンで行って、cargo release実行の瞬間だけ正式バージョンになる、というフローです。単一クレートの場合はこれで問題ないのですが、ワークスペースで複数クレートになって依存関係があると問題になります。
詳細は細かい話になるので関連するIssueだけ挙げておきます。
これを避けるには、pre-releaseを使わずに正式バージョンだけで運用すれば良いです。ワークスペース直下のrelease.tomlに
no-dev-version = true
とするとワークスペース全体でpre-releaseが無効になります。
この状態でcargo release patch
とするとパッチバージョンを上げてくれます。
普通にcargo release
とするとバージョンを上げずにpublishしようとするので注意が必要です。
タグ付けが複数回行われる
gitのタグを付けるようにしている場合、各クレート毎に同じタグを付けようとしてエラーになります。
解決方法ですが、クレート毎のCargo.tomlにて
[package.metadata.release]
disable-tag = true
としてタグ付けを無効にして、ワークスペース全体で1回だけタグ付けが発生するように調整するといいでしょう。
タグ付けを有効にするクレートは、ワークスペース内の依存関係的にツリーのルートに位置するクレートがいいと思います。cargo releaseは依存関係ツリーの末端から順に処理していくので、ルートでタグ付けするとちょうど全クレートのバージョンが上がったところでタグ付けできます。
pre-release-replacementsのパス
pre-release-replacementsとはcargo releaseの機能の一つで、README.mdなどに書かれたバージョン番号を上げるのに使います。
ワークスペースを使う場合、これはワークスペース直下のrelease.tomlではなく、各クレートに書く必要があります。
(ワークスペース直下に書いた場合、単に無視されるようです)
さらに、対象となるファイルパスはそのクレートからの相対パスなので、ワークスペース直下のREADME.mdなら../README.mdになります。
現状のcargo releaseはエラー表示がいまいちで、パスを間違えた場合
Fatal: IO Error: No such file or directory (os error 2)
としか出ず、どのtomlファイルのどのパスが間違っているか全くわかりません。
試行錯誤で乗り切りましょう…。(これはそのうち修正PRを出したいです)
ワークスペースのディレクトリ構成
ワークスペースのディレクトリ構成として、公式ドキュメントではワークスペース直下に各クレートのディレクトリを配置する方法が書かれています。しかし、普通のクレートディレクトリ内に別クレートのディレクトリを置いて、ワークスペースとすることもできます。
(例えばripgrepはこのパターンです)
前者と後者ではcargo release
したときの挙動が違います。前者はcargo release
すると全クレートが処理されますが、後者はルートになっているクレートしか処理されません。後者のパターンで全クレート処理したい場合はcargo release --all
とする必要があります。
まとめ
ここまでで紹介したworkaroundを適用したプロジェクトが以下になります。具体的な構成例を知りたい方は参考にしてください。
cargo releaseのワークスペース対応はまだ出来たばかりなので、ここに挙げた問題点も仕様なのかバグなのかよく分かりません。
おそらくバージョンアップでいろいろ変わると思いますのでご注意下さい。