Rustでのパッケージって…

·
#CLI#Rust#Terminal#cargo#crates.io

はじめて Rust の crate を crates.io に公開した

はじめに

先日、はじめて Rust で CLI ツールを作って crates.io に公開してみました。 内容はとても簡単で、名前を受け取って Hello, <name>! と出力するだけです。 でも「公開」までの道のりが、思っていたよりもかなりたくさんのポイントがありました。

この記事では、実際にハマった点や、やっていくうちに気づいたことを残しておきます。


プロジェクトの構成

まず、プロジェクト自体はとても小さいです。

cargo new my-cli --bin

で作った後、src/main.rsclap を使って引数をパースして、それを表示するだけです。

use clap::Parser;

#[derive(Parser)]
struct Cli {
    /// 名前を入力
    name: String,
}

fn main() {
    let args = Cli::parse();
    println!("Hello, {}!", args.name);
}

Cargo.toml はこんな感じです。

[package]
name = "call-me-in-cli"
version = "0.1.0"
edition = "2021"
authors = ["a-lost-social-misfit <a.lost.social.misfit@gmail.com>"]
description = "A simple Rust CLI for learning purposes"
license = "MIT"
repository = "https://github.com/a-lost-social-misfit/my-cli"

[dependencies]
clap = { version = "4.3", features = ["derive"] }

crates.io に公開するまでの手順

だいたい以下のような流れでした。

  1. crates.io に GitHub アカウントでログイン
  2. API トークンを作成(スコープは publish-newpublish-update だけで十分)
  3. ローカルで cargo login でトークンを登録
  4. cargo publish で公開

一見シンプルに見えますが、ここで気づきたいことがたくさんありました。


ハマった点と気づき

crate 名は世界で一意にする必要がある

最初は my-cli という名前で公開しようとしたんですが、すでに別の人が同じ名前で公開していて、以下のエラーが出ました。

error: failed to publish my-cli v0.1.0 to registry at https://crates.io

Caused by:
  the remote server responded with an error (status 403 Forbidden):
  this crate exists but you don't seem to be an owner.

npm のように @scope/package-name のような名前空間はないため、名前をユニークにする必要があります。 最終的に call-me-in-cli に変えて公開できました。

Cargo.lock は CLI なら commit すべき

Cargo.lock.gitignore に入れていなかったのですが、それが正解でした。 CLI やバイナリの場合は依存関係のバージョンを固定しておくのが推奨されます。 一方、ライブラリの場合は逆に .gitignore に入れるのが一般的です。

公開前に未コミットのファイルがあると失敗する

cargo publish は、git に未コミットのファイルがある場合に以下のようなエラーを出してくれます。

error: 1 files in the working directory contain changes that were not yet committed into git:
Cargo.lock
to proceed despite this and include the uncommitted changes, pass the `--allow-dirty` flag

これは「crates.io には commit 済みのスナップショットを公開する」という考え方から来ているようです。 --allow-dirty で無視することもできますが、普通は先に commit してから publish するのが正しい流れです。

このエラーは cargo bump patch で バージョンを上げた際にも Cargo.lock が変わるため、毎回出ます。

crate 名を変えると Cargo.lock も変わる

Cargo.tomlname を変えると Cargo.lock も更新されます。 Cargo.lock は現在のプロジェクトの依存関係とバージョンを固定するファイルで、 パッケージ ID が変わると依存解決がやり直されるためです。

edition について迷った

最初 edition = "2024" にしていたのですが、「2024 は nightly じゃないと動かないのでは」と不安になって "2021" に直してしまいました。でも実際には Rust 1.85.0 で "2024" も安定版になっていたので、最終的に "2024" に戻しました。慌てて変えてしまったのは勉強になった経験です。

ログイン済みでもメールアドレスの検証が必要

GitHub アカウントで crates.io にログインしていても、メールアドレスが検証されていないと以下のエラーが出ます。

error: failed to publish my-cli v0.1.0 to registry at https://crates.io

Caused by:
  the remote server responded with an error (status 400 Bad Request):
  A verified email address is required to publish crates to crates.io.

crates.io のプロフィール設定からメールを登録して検証するだけで解決しました。

API トークンのスコープは最小限にする

トークン作成時にスコープを選ぶ画面があります。 勉強用・個人開発なら publish-newpublish-update の2つだけで十分です。 Rust の文化的にも「最小権限」が基本的な考え方です。

公開した crate の削除には条件がある

かつて crates.io では公開した crate を削除することはできませんでした。 現在は一定の条件を満たせば削除が可能になっています。具体的には、公開から 72 時間以内であるか、オーナーが1人のみで、かつ毎月のダウンロード数が 500 件未満で、他の crate に依存されていない場合に削除できます。 それ以外の場合は依然として削除できないため、公開には注意が必要です。 今回は勉強用なので、そのまま放置しています。


バージョン管理について

Rust では セマンティックバージョニング(semver) を使います。 Cargo.tomlversion フィールドで管理し、MAJOR.MINOR.PATCH の形になります。

  • PATCH:バグ修正など(0.1.00.1.1
  • MINOR:機能追加(0.1.00.2.0
  • MAJOR:後方互換性を壊す変更(0.1.01.0.0

バージョンアップは cargo-bump というツールで簡単にできます。

cargo install cargo-bump
cargo bump patch   # 0.1.0 → 0.1.1

その後は通常と同じように commit して publish するだけです。

git add .
git commit -m "Bump version to 0.1.1"
git push origin main
cargo publish

.gitignore について

Rust の .gitignore はとても小さく、最初は以下だけで十分です。

/target/

Cargo.lock は CLI なので commit する側にあります。

ただ、今後プロジェクトが複雑になると、都度追加していくことが出てきます。よくある例としては以下のようなものがあります。

環境変数やシークレットは特に注意が必要です。Rust では Node.js のように .env ファイルを前提にすることは少ないですが、開発中に使うことは出てきます。.env は必ず .gitignore に入れておくべき。

.env

エディタやOS固有のファイルは個人環境に依存するため、必要になったら追加していけば十分です。

# macOS
.DS_Store

# エディタ
.vscode/
.idea/

その他としては、以下のようなものも将来的に出てくることがあります。

# ログやデバッグ用の一時ファイル
*.log

# ビルド成果物やバイナリ(/target/ 以外の場合)
dist/
out/

ただし、Rust の文化的には「設定を増やさないこと」が美徳とされるため、必要になってから足していくのが自然なやり方です。最初から全部書いておくよりも、都度調べて追加していく方が、何が必要で何が不要かの理解も深まります。


まとめ

公開したコード自体は本当に小さかったのですが、「公開」という行程には以上のような多くのポイントがありました。 npm と比べると、crates.io は「履歴の一貫性」や「責任ある公開」を非常に重視している印象でした。 はじめて Rust のパッケージ公開を体験した際には、こうした点に注意していただければ幸いです。