Keyboard shortcuts

Press or to navigate between chapters

Press ? to show this help

Press Esc to hide this help

FND-004: エンジニアリング基盤: CI/CDパイプラインとリリース自動化

v0.7.0 → v1.0.0 のサポート · 種類: アーキテクチャ · 改訂: 8

正規リファレンス · チームにより承認済み · 改訂 8 元の RFC 議論: #5579


これを読む前にチームへの注意。

このドキュメントは、コードを取り巻く足場、つまりコードをビルドし、テストし、監査し、出荷する自動化について扱います。この足場はうまく機能しているときには見えず、機能していないときには苦痛をもたらします。ほとんどのチームは、苦痛になるまでそれについて考えず、その頃には誰も完全には理解できない何かに成長しています。このRFCは、その事態に先手を打とうとする試みです。これまでCI/CDについて深く考えたことがなければ、ここが良い出発点になります。すでに考えたことがあるなら、おなじみのパターンに気づくでしょう。いずれにせよ、目標は同じです。それは、チームの邪魔をすることなく自信を与えてくれるパイプラインです。


目次

  1. コンテキスト: パイプラインはアーキテクチャ
  2. 正直な評価:現在の状況
  3. ターゲットパイプラインの設計
  4. セキュリティスキャンをライフサイクルとして
  5. 配布モデルに合わせたリリース自動化
  6. 採用すべき標準
  7. フェーズ別ロードマップ
  8. コントリビューターへの影響

改訂履歴

Rev日付目次
12026年4月9日初期ドラフト
22026-06-04format と lint の直列ゲーティングを、format のみのゲーティングと、それに続く並列の必須 Rust ジョブに置き換えました(#7111
32026-06-10プルリクエストで使用されるキャッシュをシードするため、信頼された master の実行を必須化 (#7355)
42026-06-21プラグインのビルドおよびリリースターゲットを wasm32-wasip1 から wasm32-wasip2 に変更しました (#8061)
52026-06-30デスクトップアーティファクトとそのリリースパイプラインに関する義務を削除しました(#8544
62026-07-04デスクトップアーティファクトとそのリリースパイプラインの要件を復元しました(#8565
72026-08-07actions/attest-build-provenance のガイダンスを、直接 actions/attest を使用したアーティファクトのアテステーションに置き換えました(#9717
82026-08-20aardvark-syszeroclaw-robot-kit がワークスペースから離脱したため、独立リリースのガイダンスから廃止されたハードウェアライブラリの分類を削除しました(#10152

1. コンテキスト: パイプラインはアーキテクチャである

アーキテクチャRFC(#5574)では、ある原則が確立されました。依存関係は内側に向かって流れ、構造はコンパイラによって強制される。 同じ原則が、コードを取り巻くパイプラインにも適用されます。パイプラインは単なる自動化ではありません。それは、何を信頼し、何を検証し、いつ検証し、どのように出荷するかについての一連のアーキテクチャ上の決定です。

これらの決定には結果が伴います。モノリシック向けに設計されたパイプラインはマイクロカーネルに積極的に抵抗します。トリアージプロセスがないセキュリティゲートは、すべてをブロックするか、回避されるかのどちらかになります。1つのバイナリを中心に構築されたリリースワークフローは、5つのアーティファクトタイプを持つ配布モデルでは維持できません。これらは設定の問題ではありません。設計の問題であり、コードアーキテクチャと同じ意図的な扱いが必要です。

現在のパイプラインは、loop_.rs が9,500行まで膨れ上がったのと同じように、場当たり的に成長してきました。誰も現在の状態を選んだわけではありません。積み重なった結果です。マイクロカーネル移行の最初の大きな一歩となったPR #5559により、パイプラインの前提がもはや成り立たない箇所がいくつか明らかになりました。これは有益なシグナルです。つまり今こそ、立ち止まり、現状を評価し、意図を持って設計し直すべき絶好のタイミングなのです。

このRFCは、アーキテクチャRFCがコードベースに対して行うことと同様に、パイプラインに対して行います:存在するものを明確にし、構造的な問題を特定し、プロジェクトの方向性と整合性のある次のステップを提案します。


2. 正直な評価:現在の状況

このセクションは批判ではありません。これは診断です。現在のパイプラインは、当時妥当だった判断を反映しています。目標は、それを明確に理解し、改善することです。

2.1 同じ作業を行う2つのワークフロー

現在、リポジトリには master に対するプルリクエストで実行される2つの別々のワークフローがあります。

  • checks-on-pr.yml(「Quality Gate」と表示)
  • ci-run.yml(「CI」として表示)

両方のリポジトリで、すべてのPRに対してLint、ビルド、テスト、セキュリティジョブを独立して実行します。つまり、各PRで2つのパイプラインが並列に実行されます。単一のコンパイルユニットを持つモノリシックなリポジトリでは、これはコストがかかりますが管理可能な範囲でした。しかし、マルチクレートワークスペースでは、すでに大きなCI予算がさらに倍増し、追加のシグナルは得られません。

重複には、計算時間の消費以外にも、より微妙なコストがあります。あるワークフローでチェックが失敗し、別のワークフローでは失敗しない場合、コントリビューターはどちらの結果を信頼すべきか分かりません。新しいチェックを追加する必要がある場合、それを2か所に追加しなければなりません。動作を変更する必要がある場合、それを2か所で変更しなければなりません。真の情報が2つあることは、コードにおける真の情報が2つあるのと同じ問題です。

2.2 シングルバイナリの前提は至る所に組み込まれている

リリース自動化(release-stable-manual.ymlrelease-beta-on-push.ymlpublish-crates.ymlpub-aur.ymlpub-homebrew-core.ymlpub-scoop.ymldiscord-release.ymltweet-release.yml)は、リリースが1つのバイナリであるという前提のもとに設計されていました。それをビルドし、署名し、パッケージマネージャーへ公開し、アナウンスするという流れです。

アーキテクチャRFCでは、5つの異なるアーティファクトタイプを持つ配布モデルを定義しています。すなわち、カーネルバイナリ(複数のプラットフォームターゲット)、ハードウェアバリアントのカーネルバイナリ、ゲートウェイバイナリ、WASMプラグインファイル、そしてTauriデスクトップインストーラーです。現在のリリースワークフローのいずれも、この構造を考慮していません。アーキテクチャ移行がフェーズ3およびフェーズ4に到達すると、いまそのモデルを念頭に置いて再設計しない限り、これらのワークフローはすべて変更が必要になります。

2.3 ライフサイクルなしのセキュリティスキャン

セキュリティジョブは cargo audit をハードゲートとして実行します。依存関係ツリーにアドバイザリが存在する場合、ゲートが失敗し、PR はマージできません。意図は正しいですが、実装には構造的な問題があります。

cargo audit は依存関係ツリー内のすべてのアドバイザリを報告します:アクティブな脆弱性、メンテナンスされていないクレート、および情報通知。しかし、以下を区別しません:

  • プロジェクトが積極的に呼び出しているクレートにおける重大な脆弱性
  • オプション機能の3段階下にある間接依存関係における脆弱性
  • プロジェクトが依存しているクレートが、制御できないサードパーティのライブラリを通じて間接的に「メンテナンスされていない」ことを示す通知
  • このPRがオープンされる前に存在していた既存のアドバイザリ

これらすべてが同じハードフェイルを生成する場合、ゲートはノイズになります。ノイズに対する現実的な対応は、ゲートを下げる、失敗を無視する、またはチェックを抑制することです。これら3つの対応はいずれも、プロジェクトのセキュリティを高めるのではなく、むしろ低下させます。維持できないセキュリティゲートは、維持されません。

PR #5559 で RUSTSEC-2026 のアドバイザリが12件同時に浮上しました。「このPRによって新たに導入されたアドバイザリ」と「masterブランチに既に存在していたアドバイザリ」を区別するツールがないため、PRの著者やレビュアーはこのPRがセキュリティ姿勢を悪化させたかどうかを判断できません。

2.4 厳格なデルタリンティングスクリプト

ci-run.ymlには、scripts/ci/rust_strict_delta_gate.shを実行するジョブが含まれています。これは、clippyの出力をPRのベースSHAと比較するカスタムスクリプトです。この考え方は理にかなっています。コードベース内に警告が存在するかどうかだけでなく、このPRが新たな警告を導入したかどうかを知りたいわけです。この実装は、モノリシックなクレートに対する小規模で焦点を絞ったPRに対しては問題なく機能します。

260,000 行のコードを 10 個の新しいクレートにまたがって移動し、数百のファイルに影響を与える PR は、このスクリプトが想定していなかった領域に踏み込んでいます。変更されたファイルの範囲が大きすぎて、増分比較では意味のあるシグナルを生成できません。このスクリプトはワークスペースの構造を理解する必要があります。具体的には、crates/zeroclaw-channels/ 内のファイルへの変更は、ルートではなくそのクレートのコンテキストで評価されるべきだということです。

2.5 ワークスペース対応のキャッシュやスコープなし

現在のRustキャッシュの設定(Swatinem/rust-cache)は単一のクレートには十分ですが、マルチクレートワークスペースでは、どのクレートが変更されたか、およびどのコンパイル済みアーティファクトを再利用できるかを理解することがキャッシュの有効性に依存します。明示的なワークスペーススコーピングがない場合、任意のクレートへの変更が他のクレートが依存するキャッシュを無効化し、すべてのPRで完全な再コンパイルを引き起こす可能性があります。

さらに重要なのは、特定の変更によって影響を受けるクレートに対してのみ CI を実行する仕組みがないことです。zeroclaw-tool-call-parser の typo を修正する PR において、ゲートウェイの再ビルドや再テストは不要です。ワークスペースがアーキテクチャ RFC で想定されている 30 以上のクレート規模へと成長するにつれて、すべての PR に対してフルパイプラインを実行するコストは、コントリビューションにとって意味のある障壁となります。

2.6 アクションのピン留めは有効:ただしドキュメント化されていない

既存のワークフローでは、アクションを完全なコミットSHAに固定しており、これはセキュリティ上正しいプラクティスであり、評価に値します。しかし、その理由を説明する文書化されたポリシーがなく、それらのSHAをいつ更新すべきかをレビューするプロセスもなく、それらを最新に保つための自動化もありません。ポリシーのない適切な振る舞いは脆弱です。次にワークフローステップを追加する貢献者は、SHA固定がなぜ重要なのかを知らず、代わりに可変タグを使用してしまうかもしれません。


3. ターゲットパイプラインの設計

3.1 1つのパイプライン、1つの真実のソース

2つの並列なワークフローは、適切に構造化された単一のパイプラインに統合すべきです。「Quality Gate」と「CI」の区別はコントリビューターにとって意味がありません。どちらもPRが通過しなければならないチェックです。統合により、チェック結果を確認する場所、動作変更時に更新する場所、各チェックが何をどのような理由で実行しているかをドキュメント化する場所が、それぞれ1か所に集約されます。

統合パイプラインは段階的な構造に従っており、まず非常に低コストなフォーマットチェックが実行され、その後Rust中心のジョブが並列でファンアウトします。Lintは引き続き必須ですが、グリーンのクリティカルパスを短縮することが目的の場合、ビルドとテストのキャッシュのウォームアップを不必要に妨げるべきではありません:

Stage 1: Format (cheap serial gate)
  └── cargo fmt --check

Post-format quality gate (parallel, required)
  └── cargo clippy --workspace --all-targets -- -D warnings
  └── Docs quality gate

Post-format Build + Check (parallel, 5–15 min)
  └── Build matrix (Linux x86_64, macOS ARM, Windows)
  └── cargo check --features ci-all
  └── cargo check --no-default-features (kernel profile)
  └── cargo check --target i686 (32-bit)

Post-format Test (parallel, 10–30 min)
  └── cargo nextest run --workspace

Post-format Security (parallel)
  └── cargo deny check (licenses, sources, advisories)
  └── Advisory triage gate (see §4)

Required Gate
  └── Composite status — branch protection requires only this job

post-format ジョブはフォーマットのパスが完了した後に並列で実行されます。これにより、フォーマットエラーは破棄されるビルドに計算リソースを浪費することなく早期に失敗し、その一方で clippy、build、test、security は、適切にフォーマットされた PR に対して同時に処理を進めることができます。Required Gate ジョブはすべての結果を集約するため、ブランチ保護は1つのジョブ名のみを追跡すればよく、これは現在の両方のワークフローにすでに存在するパターンです。

3.2 ワークスペース対応のClippy

現在の clippy の呼び出しは、ルートクレートのデフォルトの機能セットに対して実行されます。マルチクレートワークスペースに対して正しい呼び出しは以下の通りです:

sh

cargo clippy --workspace --all-targets -- -D warnings

--workspace フラグは、ルートだけでなくワークスペース内のすべてのクレートがリンティングされるようにします。--all-targets フラグは、テスト、ベンチマーク、および例を含みます。フィーチャゲート付きチェックに対して --features ci-all と組み合わせることで、包括的な状況把握が可能になります。

厳密な差分lintの考え方、つまり警告が存在するかどうかではなく、このPRが新たな警告を導入したかどうかをチェックする点は、維持する価値があります。実装は、diff出力を比較するシェルスクリプトから、影響を受ける各クレートを個別に評価する適切なワークスペース対応の呼び出しへと移行すべきです。よりシンプルで信頼性の高いアプローチとしては、--workspace -D warnings が常にクリーンに通過することを必須とし、差分の考え方を暗黙的にすることです。ベースラインが常にクリーンであれば、警告を導入したPRはすべて失敗します。これにより、カスタム比較スクリプトの必要性が完全になくなります。

3.3 変更されたクレートの検出

30以上のクレートに成長したワークスペースでは、変更内容に関わらずすべてのプルリクエストでフルテストスイートを実行するのは非効率です。パイプラインは、プルリクエストによって影響を受けたクレートを検出し、テストの実行範囲をそれに合わせて絞り込む必要があります。

仕組みは単純です。PR で変更されたファイルをワークスペースのメンバーリストと比較し、変更されたファイルを含むクレートを特定し、変更されたクレートに依存するすべてのクレート(下流への影響)を含むセットに拡張し、そのセットに対してのみテストを実行します。

PR changes: crates/zeroclaw-tool-call-parser/src/lib.rs

Affected crates:
  zeroclaw-tool-call-parser     ← directly changed
  zeroclaw-misc                 ← depends on it
  zeroclaw (root)               ← depends on it

Not affected:
  zeroclaw-channels             ← no dependency path
  zeroclaw-memory               ← no dependency path
  zeroclaw-providers            ← no dependency path

これは、依存関係グラフを抽出するために cargo metadata を使用し、それを走査する短いスクリプトによって実装されています。完全なテストスイートは master へのプッシュやリリースブランチで引き続き実行されます。PR では、影響を受けるクレートのサブセットが実行されます。

3.4 キャッシュ戦略

Swatinem/rust-cache は、workspaces 設定を通じてワークスペース対応のキャッシュをサポートしています。キャッシュキーにはワークスペースのメンバーリストを含める必要があり、新しいクレートが追加された際に、関連しないクレートのキャッシュを無効化することなく適切に無効化されるようにします。

- 使用: Swatinem/rust-cache@<sha>
  with:
    ワークスペース: |
      . -> target
    cache-on-failure: true
    save-if: ${{ github.ref == 'refs/heads/master' }}

キャッシュの保存は refs/heads/master に限定されているため、ワークフローは信頼された master へのプッシュ時に実行する必要があります。PR は master でシードされたキャッシュを読み取りますが、競合するブランチのアーティファクトは書き込みません。これにより、複数の PR が同時にオープンしているときのキャッシュのスラッシングを回避しつつ、マージ後の実行で次のレビューループに向けて Linux、macOS、Windows のビルドキャッシュをウォームアップできます。


4. ライフサイクルとしてのセキュリティスキャン

4.1 二値ゲートの問題

コンテキストなしで任意のアドバイザリに対してブロックするセキュリティゲートは、チームにセキュリティ上の失敗をノイズとして扱うよう訓練してしまいます。これは意図した効果の反対です。目指すべきゲートは以下の通りです:

  • 高シグナル: 失敗はこのPRが影響を与えた実際の問題を意味します
  • 実行可能: コントリビューターは何をすべきか、そしてその理由を理解しています
  • 持続可能: ゲートは継続的な手動介入なしで維持できます

cargo audit だけではこれを実現できません。cargo deny がそれを行います。

4.2 cargo-deny を主要なセキュリティツールとして

cargo deny は、プロジェクトレベルの依存関係ポリシーにおいて、cargo audit より高機能な後継ツールであり、以下を強制します:

  • Advisories: RUSTSEC データベース。特定のアドバイザリを拒否、警告、または文書化された正当な理由とともに明示的に無視する機能を備えています
  • ライセンス: すべての依存関係が許容されるライセンスを使用していることを保証します(ワークスペースが拡大し、新しいコントリビューターが依存関係を追加するにつれて重要になります)
  • Sources: 承認されたレジストリ(crates.io、path、特定のホストを使用するgit)からのみ依存関係を取得することを保証します
  • 重複: 同じクレートの複数のバージョンが依存関係ツリーに現れた場合に警告します

重要な機能は deny.toml[advisories] セクションであり、これにより明示的で正当な理由のある除外が可能になります。このアプローチは、セキュリティスキャンを二者択一の合否判定から、文書化された監査可能なポリシーへと変えます。除外されたすべてのアドバイザリには、書面による正当性の説明とトラッキングイシューがあります。レビュアーは、どのアドバイザリがどのような理由で抑制されているかを正確に確認できます。抑制されたアドバイザリの深刻度が高まったとき(新たな脆弱性が見つかった、修正が利用可能になった、など)、トラッキングイシューがそのリマインダーとなります。

4.3 アドバイザリトライアージプロセス

新しいアドバイザリが依存関係ツリーに出現した場合、PR からであれ毎日のアドバイザリデータベース更新からであれ、その処理は次のとおりです。

  1. アドバイスを分類する: 影響を受けるクレートは直接依存関係にありますか、それとも間接依存関係にありますか?ZeroClaw は脆弱なコードパスを呼び出しますか?修正済みのバージョンが利用可能ですか?
  2. 応答の決定:
    • 修正可能な直接依存関係の脆弱性 → 依存関係を更新し、無視は不要
    • 修正可能な依存関係の脆弱性 → 依存関係を固定するか、直接の依存関係が更新されるまで待つか、追跡用Issueを作成する
    • メンテナンス終了の通知、アクティブなエクスプロイトなしdeny.toml の無視リストに正当化と追跡用イシューを追加
    • 修正のない重大な脆弱性 → ワークアラウンドを評価;PRをブロックする可能性があります
  3. 決定事項deny.toml に記録し、アドバイザリ ID、簡潔な理由、および追跡イシューへのリンクを含めます。

このプロセスにより、#5559 のような既存のアドバイザリを12件表示するPRが、文脈なしでゲートに失敗することがなくなります。アドバイザリは分類され、既存のものは文書化され、ゲートはPRによって導入された新しい未分類のアドバイザリのみを報告します。

4.4 日次アドバイザリスキャン

セキュリティアドバイザリは継続的に公開されています。マージ時にセキュリティゲートを通過したPRでも、翌週に公開された脆弱性を含んでいる可能性があります。パイプラインには、master ブランチに対して毎日定期的に実行され、アドバイザリデータベースを確認して、新しい未分類のアドバイザリが見つかった場合にGitHub Issueをオープンする処理を含める必要があります。

on:
  スケジュール:
    - cron: '0 9 * * *'  # 毎日 09:00 UTC

これにより、アドバイザリ(advisory)のトリアージサイクルとPRのマージサイクルが分離されます。コントリビューターは、PR作成後に出現したアドバイザリによってブロックされなくなります。セキュリティチーム(またはローテーション担当)は、デイリースキャンの出力を通常の保守タスクとして処理します。


5. ディストリビューションモデルに合わせたリリース自動化

5.1 現在のミスマッチ

アーキテクチャ RFC §4.4.2 では、以下のリリース成果物が定義されています:

アーティファクトビルドターゲット公開先
カーネルバイナリ(標準)x86_64-linux-musl、aarch64-linux-gnu、armv7-linux-gnueabihf、x86_64-darwin、aarch64-darwin、x86_64-windowsGitHub リリース
カーネルバイナリ(ハードウェア)aarch64-linux-gnu, armv7-linux-gnueabihfGitHub リリース
ゲートウェイバイナリ同じプラットフォームのマトリックスGitHub リリース
WASM プラグインファイルwasm32-wasip2プラグインレジストリ
デスクトップインストーラーx86_64 + aarch64、macOS/Windows/LinuxGitHub Releases、プラットフォームストア

現在のリリースワークフローが認識しているのは、これらのうちのちょうど1つ、すなわち標準バイナリのみです。残りは自動化にまだ存在しません。これは今のところ妥当です。プラグインシステムはまだ完成していません。しかし、リリースワークフローは、新しいアーティファクトタイプが導入されるたびに書き直す必要がないよう、このモデルを念頭に置いて設計されるべきです。

5.2 リリースパイプラインの構造

ターゲットリリースパイプラインは、モノリシックなワークフローではなく、ジョブの有向グラフです。

version-bump (release-plz PR merged)
    │
    ├── build-kernel-standard (matrix: 6 targets)
    ├── build-kernel-hardware (matrix: 2 ARM targets + hardware flags)
    ├── build-gateway (matrix: 6 targets)
    ├── build-plugins-wasm (matrix: all plugin crates → wasm32-wasip2)
    └── build-desktop (matrix: macOS, Windows, Linux AppImage/deb)
            │
            ├── publish-github-release (attaches all kernel + gateway binaries)
            ├── publish-plugin-registry (uploads WASM files)
            ├── publish-aur (kernel binary for Arch Linux)
            ├── publish-homebrew (kernel binary for macOS)
            ├── publish-scoop (kernel binary for Windows)
            └── announce (Discord, social)

各ビルドジョブは独立しており、ホットフィックスリリースごとに個別にトリガーできます。公開ジョブは、関連するすべてのビルドジョブが成功した後に依存関係が満たされます。アナウンスジョブは最後に実行されます。

この構造により、プラグイン専用のリリース(channel-discord.wasm の新バージョン)では、build-plugins-wasmpublish-plugin-registry のジョブのみを実行し、カーネルのフルビルドをトリガーすることなく実行できます。カーネルのパッチリリースでは、build-kernel-* と下流の公開ジョブを実行し、プラグインレジストリには影響を与えません。

5.3 リリース管理のためのワークスペース対応バージョン管理

アーキテクチャ RFC §4.4.1 では、リリース自動化ツールとして release-plz を指定しています。release-plz はこのパイプラインモデルに直接統合されます:

  • master へのプッシュ時に、release-plz はワークスペースのバージョンを更新し、従来のコミット履歴から変更履歴を更新し、最後のリリース以降に変更されたすべてのクレートをリストアップする「リリースPR」を開きます。
  • Release PRがマージされると、リリースパイプラインが自動的にトリガーされます。
  • version.workspace = true を持つクレートはまとめてバージョンが更新されます。独立してバージョン管理される zeroclaw-api クレートは、バージョン管理ポリシーに従って個別に扱われます。

リリースPRはレビューのチェックポイントとして機能します。チームは、何も公開される前に、どのバージョンが公開されるか、そして変更履歴に何が記載されているかを正確に把握できます。これにより、手動でのバージョン番号の更新や version-sync.yml ワークフローの必要性がなくなります。

5.4 アクション固定ポリシー

現在のワークフローでは、アクションを完全なコミットSHAに固定しています。これは正しく、貢献者の入れ替わりにも耐えられるように、明示的なポリシーとして正式に定めるべきです。

ポリシー: ワークフローファイル内のすべての uses: 参照は、バージョンコメント付きの完全なコミット SHA に固定する必要があります。変更可能なタグ(@v4@main@latest)は許可されません。例外はありません。

# 正しい
- 使用: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

# 許可されていません
- 使用: actions/checkout@v4
- 使用: actions/checkout@main

理由: ミュータブルなタグは、サードパーティがアクションの動作が変更されないことを約束するものです。しかし、この約束は GitHub Actions エコシステム全体で何度も破られてきました。SHA ピンを使用すると、アクション作者が後から何を変更しても、ワークフローはレビューされた内容と完全に一致して実行されます。これは、書き込み権限やシークレットにアクセスするアクションにとって特に重要です。

更新プロセス: GitHub Actions用に設定された dependabot または renovate を使用して、新しいSHAバージョンが利用可能になった際にPRを作成します。チームはこれらのPRを確認し、マージします。これにより、手動での監視を行うことなく、アクションを最新に保つことができます。


6. 採用すべき標準

6.1 SLSA: サプライチェーンセキュリティフレームワーク

SLSA(Supply-chain Levels for Software Artifacts、発音は「salsa」)は、Googleによって開発され、業界全体で採用されているソフトウェアサプライチェーンのセキュリティを強化するためのフレームワークです。これは、基本的なレベルから完全な分離まで、ビルドの整合性に関する4つのレベルを定義しています。

ZeroClawの現在の規模とチーム規模を考慮すると、SLSA Level 2 が適切な目標となります:

  • ビルドはホスト型CIプラットフォーム上で実行されます(既に該当、GitHub Actions)
  • ビルドスクリプトはバージョン管理されています(すでに適用済み)。
  • ビルドの起源情報は生成され、リリースアーティファクトに付与されます(追加するステップ)。

SLSA Level 2 のプロベナンスとは、各リリースアーティファクトに、暗号学的に署名されたアテステーションが添付されていることを意味します。このアテステーションには、どのソースコミットから生成されたか、どのワークフローでビルドされたか、そしてそのワークフローが期待されるプラットフォーム上で実行されたかという情報が記録されます。ユーザーやパッケージマネージャーはこのアテステーションを検証できます。これにより、「このバイナリはこのソースから来た」という主張と、「このバイナリが実際にこのソースから来たことを証明できる」という事実との間のギャップを埋めます。

GitHub Actions は actions/attest アクションを通じて、SLSA Level 2 のプロヴェナンス生成をネイティブにサポートしています。追加に必要なのは、各ビルドジョブにつき 1 ステップだけです。

6.2 Conventional Commits(既に暗黙的に使用、正式化する)

アーキテクチャ RFC のバージョニングポリシーと release-plz の統合は、どちらも変更履歴の生成に conventional commit フォーマットに依存しています。ガバナンス RFC ではすでに PR タイトルの規約を参照しています。この RFC はその接続を正式に定義します:コミットメッセージおよび PR タイトルにおける conventional commit フォーマットは、変更履歴の自動生成を駆動する入力であるため、推奨事項ではなく必須事項です。

ZeroClawの更新履歴で重要なカテゴリ:

プレフィックス変更履歴セクションバージョンの影響
feat:新機能マイナー
fix:バグ修正PATCH
feat!: または fix!:破壊的変更メジャー
chore:メンテナンスリリースエントリがありません
docs:ドキュメントリリースエントリがありません
perf:パフォーマンスPATCH
security:セキュリティ修正PATCH(少なくとも)

CI は、他のチェックが実行される前に、PR のタイトルが conventional commit フォーマットに一致していることを検証する PR タイトル lint ジョブによってこれを強制します。

6.3 再利用可能なワークフロー

クレートとアーティファクトの種類が増えるにつれて、ワークフローの重複は保守上の問題になります。GitHub Actions は再利用可能なワークフローをサポートしています。これは、関数のように別のワークフローから呼び出せるワークフローです。ビルドマトリックス、セキュリティスキャン、テストランナーは、それぞれ再利用可能なワークフローとして抽出すべきです。

.github/
  workflows/
    ci.yml               ← PR checks (calls reusable workflows)
    release.yml          ← Release pipeline (calls reusable workflows)
    daily-audit.yml      ← Scheduled security scan
  _workflows/            ← Reusable workflow definitions
    build-rust.yml       ← Parameterised build job
    test-workspace.yml   ← Parameterised test job
    security-scan.yml    ← cargo-deny invocation + triage
    publish-release.yml  ← Parameterised publish job

再利用可能なワークフローは、パラメータを使用して呼び出されます:

ジョブ:
  カーネルのビルド:
    使用: ./.github/_workflows/build-rust.yml
    with:
      ターゲット: x86_64-unknown-linux-musl
      機能: ""
      プロファイル: dist

これは、CI ワークフローとリリース ワークフローが同じビルド定義を共有していることを意味します。ビルド プロセスに対する修正は、すべての場所で同時に適用されます。


7. フェーズ別ロードマップ

パイプラインの移行は、コード移行と同じくStrangler Figのアプローチに従います:並行して構築し、着実に移行し、既存のゲートを壊さないようにします。


フェーズ1 · v0.7.0: 「合理化」

テーマ: 1つのパイプライン、クリーンなシグナル、重複なし。

このフェーズの理由: アーキテクチャの移行はすでに進行中であり、パイプラインがそれに逆らうことで、実装作業が不必要に複雑になるのを避ける必要があります。

フェーズ1の成果物

D1: checks-on-pr.ymlci-run.yml を単一のワークフローに統合する

2つのPRワークフローを1つに統合します。統合されたワークフローは§3.1で定義されたステージ構造を維持します。Quality GateCIの命名上の区別はなくなります。ワークフローは1つ、結果は1セット、確認する場所も1箇所です。

複合ゲートジョブ(CI Required Gate)は保持されます。ブランチ保護は引き続き、その単一のジョブのみを必要とします。これにより、パイプラインの内部構造が変更されても、ブランチ保護ルールの更新は不要になります。

D2: cargo auditcargo deny に置き換える

deny.toml をリポジトリのルートに追加します。[advisories][licenses][sources] のセクションを設定します。master ブランチ上のすべての現在の RUSTSEC アドバイザリを処理します:更新可能なものは更新し、更新できないものは正当性と追跡用イシューを記載して文書化します。このフェーズが完了する前に、master ブランチ上のセキュリティゲートは正常に通過している必要があります。

D3: workspace 対応の clippy 呼び出しを修正

統合ワークフローで cargo clippy --all-targets -- -D warningscargo clippy --workspace --all-targets -- -D warnings に変更します。rust_strict_delta_gate.sh スクリプトを削除します。--workspace -D warnings が常にクリーンであることを強制するため、デルタの概念は暗黙的になります。

D4: アクションのピン留めポリシーを正式化する

SECURITY.md に注釈を追加し、ワークフローファイル内のすべての uses: 参照が SHA で固定されていることを検証する CI チェックを追加します。GitHub Actions の更新用に dependabot の設定を追加します。

D5: 日次アドバイザリースキャンワークフローを追加

master ブランチに対して cargo deny check advisories を実行するスケジュールされたワークフローとして daily-audit.yml を追加します。失敗した場合は、gh issue create を使用してアドバイザリの詳細を含む GitHub Issue を開きます。

フェーズ1の成功指標

  • 単一のPRワークフローファイル、重複なし
  • master ブランチでセキュリティゲートがクリーンに通過し、既存のすべてのアドバイザリに対する文書化されたトリアージが完了しています。
  • cargo clippy --workspace を実行し、クリーンにパスしました。
  • どのワークフローファイルにも、ミュータブルなアクションタグの参照が含まれていません。
  • デイリーアドバイザリスキャンが稼働中

フェーズ 2 · v0.8.0:「Workspace-Aware」

テーマ: パイプラインはワークスペースを理解します。焦点を絞った変更に対する高速フィードバック。

このフェーズの理由: v0.8.0 までにワークスペースはさらに拡大します。すべての PR に対してフルパイプラインを実行するのは、ますますコストがかかります。zeroclaw-tool-call-parser のコントリビューターは、ゲートウェイの再構築に 30 分も待つべきではありません。

フェーズ2の成果物

D1: 変更されたクレートの検出

PRで変更されたファイルの影響を受けるクレートのセットを返すために、cargo metadata を使用して依存関係グラフを構築する scripts/ci/affected_crates.sh スクリプトを追加します。CIワークフローはこの出力を使用して、テスト実行のスコープを絞り込みます。

D2: クレートごとのテストスコープ設定

cargo nextest に、影響を受けたクレートの出力に基づいて --package フラグを追加します。master へのプッシュとナイトリービルドでは、ワークスペース全体のテストが引き続き実行されます。PR では、影響を受けたサブセットのテストが実行されます。

D3: ワークスペースを考慮したキャッシュ設定

Swatinem/rust-cache の設定を更新し、明示的なワークスペーススコーピングと save-if: ${{ github.ref == 'refs/heads/master' }} を追加して、並列 PR によるキャッシュのフラッシュを防止します。

D4: 再利用可能なワークフロー定義を抽出する

ビルド、テスト、セキュリティのジョブを .github/_workflows/ 配下の再利用可能なワークフローファイルに抽出します。ci.yml と新しい release.yml のスケルトンを更新して、それらを呼び出すようにします。

フェーズ2の成功指標

  • zeroclaw-tool-call-parser のみを変更する PR は、ワークスペース全体のテストではなく、そのクレートとその依存関係のテストを実行します。
  • インクリメンタルビルドにおけるCIのキャッシュヒット率が80%を超えている
  • ビルド、テスト、セキュリティジョブ用の再利用可能なワークフローが用意されています

フェーズ3 · v0.9.0:「リリースパイプライン」

テーマ: ディストリビューションモデルに適合するリリース自動化

このフェーズの理由: アーキテクチャ RFC のフェーズ 3 では、zeroclaw-gw を別のバイナリとして抽出します。ここで最初のマルチアーティファクトリリースが行われます。リリースパイプラインは、必要になる前に準備しておく必要があります。

フェーズ3 成果物

D1: release-plz の導入と version-sync.yml の削除

ワークスペース用に release-plz を設定します。ワークスペースのアプリケーションクレートでは version.workspace = true を使用します。独立してバージョン管理される zeroclaw-api クレートでは、独自のリリース設定を使用します。version-sync.yml ワークフローは廃止されました。

D2: release.yml で構造化されたリリースパイプラインを構築する

§5.2 の有向リリースグラフを実装します。build-kernel-standardbuild-kernel-hardwarebuild-gateway と、その下流のパブリッシュジョブを含みます。プラグインのビルドジョブはスタブ化されており、フェーズ 4 までは no-op で成功します。

D3: SLSA レベル 2 のプロベナンスを追加

各ビルドジョブに actions/attest を追加します。プロヴェナンス証明は GitHub Release アセットに添付されます。SECURITY.md に検証手順を記載します。

D4: 冗長なリリースワークフローの廃止

release-stable-manual.ymlrelease-beta-on-push.ymlpub-aur.ymlpub-homebrew-core.ymlpub-scoop.ymldiscord-release.ymltweet-release.yml を構造化された release.yml パイプラインに統合します。これらのワークフローは独立して成長してきましたが、構造化されたパイプラインはこれらを単一の監査可能なフローに置き換えます。

フェーズ3の成功指標

  • release-plzmaster ブランチでリリースPRの作成と管理を行います。
  • カーネルとゲートウェイのバイナリは、単一の release.yml ワークフローからビルドされ、公開されます。
  • すべてのリリースアセットにSLSA Level 2の進出情報を付与
  • 冗長なリリースワークフローは廃止されました

フェーズ4 · v1.0.0:「プラットフォームパイプライン」

テーマ: パイプラインはバイナリだけでなくプラットフォームも出荷する。

このフェーズの理由: v1.0.0 では WASM プラグインが公開可能になります。パイプラインは、プラグインの公開、レジストリへのアップロード、そして Tauri デスクトップインストーラーを主要なリリースアーティファクトとして処理する必要があります。

フェーズ4の成果物

D1: WASMプラグインのビルドジョブを有効化

リリースパイプラインに build-plugins-wasm を実装する。各プラグインクレートは専用のジョブで wasm32-wasip2 にビルドされる。プラグインマニフェストが生成され、署名される。publish-plugin-registry ジョブは署名済み WASM ファイルをプラグインレジストリにアップロードする。

D2: デスクトップインストーラーのビルドと公開

macOS、Windows、Linux 用の Tauri ビルドジョブを完了します。インストーラーにはカーネルとゲートウェイのバイナリが含まれます。macOS と Windows のコード署名資格情報は、必要なリポジトリシークレットとして文書化されており、セットアップガイドも用意されています。

CI/CD標準を docs/book/src/maintainers/ci-and-actions.md に公開する

このRFCで定義されているアクションのピン留めポリシー、アドバイザリートリアージプロセス、コンベンショナルコミットの要件、およびリリースパイプラインの構造は、恒久的な参照資料として docs/book/src/maintainers/ci-and-actions.md に抽出されています。このRFCは意思決定の歴史的記録であり、コントリビューターが日常的に参照するのは抽出された文書です。

D4: パイプラインへのコントリビューターオンボーディング

コントリビューションに関するドキュメントに「ローカルでのCI実行」セクションを追加し、コントリビューターがプッシュする前に自分のマシンでCIチェックを再現する方法を示してください:

sh

# CIで実行される内容 — プッシュ前にこれらを実行してください
cargo fmt --all -- --check
cargo clippy --workspace --all-targets -- -D warnings
cargo nextest run --workspace
cargo deny check

フェーズ4の成功指標

  • WASM プラグインファイルは、リリースパイプラインの一部としてレジストリに公開されます。
  • Tauri デスクトップインストーラーは、リリース時に自動的にビルドされ、公開されます。
  • docs/book/src/maintainers/ci-and-actions.md は存在し、アクションのピン留め、アドバイザリ_triage_、および conventional commits についてカバーしています。
  • コントリビューターは、4つのコマンドでローカル環境ですべてのCIチェックを再現できます。

8. 貢献者にとっての意味

PRを提出するコントリビューター向け

統合パイプラインにより、結果を確認する場所が1か所にまとまります。ステージ1(フォーマットとlint)は早期に失敗します。フォーマットエラーがある場合、ビルドを待たずに2分で把握できます。ステージ1が通れば、ビルドとテストのステージが並行して実行され、ほとんどの変更で30分以内に完全な結果が得られます。

PRタイトルにおけるconventional commitの要件は、CIによって強制されます。タイトルがフォーマットに一致しない場合、lintジョブは即座に失敗し、明確なメッセージが表示されます。これは形式的な手続きではありません。これはchangelogを自動生成するための入力であり、つまりリリースがより速く、手作業が少なく行えることを意味します。

依存関係を追加するコントリビューター向け

新しい依存関係はすべて cargo deny を通過します。依存関係に既知の脆弱性がある場合、受け入れられないライセンスがある場合、または信頼できないソースから来ている場合、セキュリティゲートは失敗し、その理由を通知します。これは設計上の意図です。正しい対応は、チェックを抑制するのではなく、依存関係を調査することです。

依存関係に修正不可能なアドバイザリ(更新が利用できないトランジティブ依存関係)が含まれている場合、§4.3 のトライアージプロセスがその文書化の方法となります。トラッキングイシューを作成し、正当性を記載した上で deny.toml に無視エントリを追加し、次に進みます。セキュリティ姿勢は、アドバイザリが解消されることを期待するのではなく、文書化を通じて維持されます。

ワークフローファイルを追加するコントリビューター向け

新しいワークフローファイルは、例外なく以下の3つのルールに従います:

  1. すべての uses: リファレンスは、バージョンコメント付きで SHA ピンされています。
  2. 既存のジョブからロジックを再利用する新しいジョブは、再利用可能なワークフローとして抽出されます。
  3. 新しいリリース関連のジョブが、新しいワークフローファイルとしてではなく、release.yml に追加されました。

判断に迷う場合は、追加する前に確認してください。ワークフローファイルはリスクの高い変更です。CIインフラ上で昇格された権限で実行され、サプライチェーンのセキュリティに影響を及ぼす可能性があります。これらは src/security/ と同じレビュー基準で扱う必要があります。

メンテナ向け

毎日のアドバイザリスキャンにより、セキュリティは危機的な状況ではなく、通常の保守タスクとなります。新しいアドバイザリが検出されると、トリアージプロセスは明確に定義されており、その結果は deny.toml と追跡用イシューに文書化されます。レビュアーは、git の履歴内でアドバイザリに関する意思決定の完全な履歴を検証できます。

release-plz からのリリースPRは、リリースレビューのチェックポイントです。何も公開される前に、チームはバージョン、変更履歴、および変更されたクレートのリストを確認します。リリースは偶然に起こるものではありません。


付録 A: 用語集

SLSA (Supply-chain Levels for Software Artifacts): 基本的なプロビナンスから完全に密閉されたビルドまで、ビルド整合性のレベルを定義するセキュリティフレームワーク。Googleによって開発され、OpenSSFに採用されています。レベル2はほとんどのオープンソースプロジェクトにとって現実的な目標です。すなわち、ホスト型ビルドプラットフォーム、バージョン管理されたビルドスクリプト、アーティファクトに添付された署名付きプロビナンスです。

来歴 (Provenance): ビルド成果物の出所を暗号的に署名した記録です。どのソースコミットから、どのワークフローで、どのプラットフォームで生成されたかを示します。これにより、ユーザーやパッケージマネージャーは、バイナリが宣言されたソースから宣言されたプロセスで生成されたことを検証できます。

cargo deny: 3 つの観点で依存関係ポリシーを適用する Cargo プラグイン。すなわち、(RustSec データベースに基づく) セキュリティアドバイザリ、(定義された許可リストに照らした) ソフトウェアライセンス、(承認された場所からのみ依存関係を取得することを保証する) ソースレジストリの 3 つです。cargo audit よりも設定が柔軟で、大規模なポリシー管理により適しています。

release-plz: デフォルトブランチへのプッシュ時に「Release PR」を作成し、バージョンを更新して conventional commit の履歴から changelog を生成する Rust エコシステム向けのリリース自動化ツール。ワークスペースに対応しており、どの crate が変更され、どの crate に新しいバージョンが必要かを把握します。

再利用可能なワークフロー: 別のワークフローからパラメーター付きでジョブとして呼び出せる GitHub Actions ワークフロー。ビルド、テスト、セキュリティのロジックを一度定義すれば、PR パイプラインとリリースパイプラインの両方から呼び出せます。

Conventional commits: 自動的な変更履歴の生成とバージョン決定を可能にするコミットメッセージの規約(feat:fix:chore: など)。release-plz のようなツールが、リリースをパッチ、マイナー、メジャーのいずれのバージョンアップとするかを判断するために使用する入力です。

Strangler Fig(パイプラインの文脈における): ワークフローに適用される同様の移行戦略です。既存のパイプライン構造と並行して新しいパイプライン構造を構築し、ジョブを一つずつ移行し、新しい構造が完成して検証されたときにのみ古いファイルを廃止します。


付録B: 参考文献

  • SLSA Framework: サプライチェーンセキュリティレベルに関する完全な仕様と実装ガイド。

  • cargo deny ドキュメント:すべての advisory、license、source オプションを含む deny.toml ポリシーファイルの設定リファレンス。

  • release-plz ドキュメント: ワークスペースの設定、変更履歴フォーマットのカスタマイズ、GitHub Actions 連携ガイド。

  • GitHub Actions のセキュリティ強化: SHA ピン留め、トークン権限、Actions ワークフローにおけるサプライチェーンリスクに関する公式ガイダンス。

  • Conventional Commits 仕様: コミットメッセージ形式とセマンティックバージョニングとの関係に関する完全な仕様。

  • OpenSSF Scorecard: 依存関係のピン留め、ブランチ保護、コードレビュー要件などを含むセキュリティプラクティスについてオープンソースプロジェクトを評価する自動化ツールです。ベースラインの評価や継続的な健全性メトリックとして役立ちます。