Posted at

[Rust] ワークスペースでcargo releaseを使う


はじめに

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に


release.toml

no-dev-version = true


とするとワークスペース全体でpre-releaseが無効になります。

この状態でcargo release patchとするとパッチバージョンを上げてくれます。

普通にcargo releaseとするとバージョンを上げずにpublishしようとするので注意が必要です。


タグ付けが複数回行われる

gitのタグを付けるようにしている場合、各クレート毎に同じタグを付けようとしてエラーになります。

解決方法ですが、クレート毎のCargo.tomlにて


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を適用したプロジェクトが以下になります。具体的な構成例を知りたい方は参考にしてください。

https://github.com/dalance/nom-packrat

cargo releaseのワークスペース対応はまだ出来たばかりなので、ここに挙げた問題点も仕様なのかバグなのかよく分かりません。

おそらくバージョンアップでいろいろ変わると思いますのでご注意下さい。