コンテンツにスキップ

リリース検証

英語版の参考訳です(10/11/2026 更新)。内容が異なる場合は英語版が優先されます。識別子、コマンド、コードブロックは翻訳せずにそのまま掲載しています。

公開済みの DCP パッケージを公式レジストリから一時的なコンシューマーにインストールし、その同一性と取得元を確認したうえで、既存の外部適合性キット(tools/conformance)をインストールされたものに対して実行します。カタログ、オラクル、適合性モデルは一切追加しません。読者向けのガイドは docs/conformance/RELEASE_VALIDATION.md です。このファイルは、ツールを実行・保守するためのリファレンスです。

PASS が述べるのは次のことだけです。これらのアーティファクトが、これらのハッシュで、これらのレジストリから取得され、このリビジョンのキットにより、この環境で、これらのカタログに合格した、ということです。認証ではなく、実行されなかったものは何も対象になりません。

node tools/release-validation/run.mjs [--list] [--lane <id>]... [--catalog <id>]...
                                      [--resolution locked|fresh] [--work <dir>] [--clean]
                                      [--python <exe>] [--go <exe>] [--cargo <exe>] [--chrome <exe>]
node --test tools/release-validation/release-validation.test.mjs     # no external network
終了コード 意味
0 PASS:すべてのレーン、すべてのカタログセル(60)、すべてのブロック(3)、すべてのコントロール
1 不一致:アーティファクトは応答したが、コミットされた期待値と一致しない
2 使用方法の誤り
3 エラー:インフラ、同一性、非対応、不完全、または実装
4 PARTIAL:失敗のない一部選択。診断であり、検証ではない

エラーが拒否として数えられることはなく、エラーは不一致より優先されます。パッケージをインストールして取得元を確認することは適合性ではありません。適合性を示すのはセルとブロックだけです。

テストは外部ネットワークを使用しません。そのうちの 1 つは、ループバックの HTTP サーバーからパッケージインデックスを配信します。

要件

POSIX ホスト上の Node 20 以降。node と同じ場所にある npm、/bin/ps、/usr/bin/tar、および Go レーン用の /usr/bin/unzip が必要です。Node の組み込みモジュールだけを使用します。

Python、Go、Rust、Chrome はホストのツールです。--python、--go、--cargo、--chrome で指定したパス、またはツール独自の PATH(node のディレクトリ、/usr/bin、/bin)で最初に見つかる python3、go、cargo、およびそのプラットフォームの通常の Chrome を使用します。何もインストール、更新、切り替えはしません。GOTOOLCHAIN=local を設定し、rustup のプロキシは拒否します。そのため、ツールチェーン自体の cargo(rustup which cargo)を指定してください。その隣に rustc が必要です。Python は 3.11 以降が必要です(fresh モードの sdist レーンが tomllib で pyproject.toml を読むため)。コミットされているロックは CPython 3.12 用です。

レーン

レーン 公開済みアーティファクト 証拠
npm-sdk npm @dcp-ai/sdk 2.4.0 8 カタログ
npm-wasm-node npm @dcp-ai/wasm 2.2.0(Node 上) 6 カタログ
npm-wasm-browser 同じパッケージ。実際の Chrome 上の公開 API 6 カタログとブロック direct-sections(103 ケース)
npm-cli npm @dcp-ai/cli 2.2.0 カタログなし:ブロック cli-suites(218 テスト)と cli-negative-control
pypi-wheel PyPI dcp-ai 2.11.0、wheel 8 カタログ
pypi-sdist PyPI dcp-ai 2.11.0、sdist(ここでビルド) 8 カタログ
go Go モジュール sdks/go/v2 v2.14.0 8 カタログ
crates-default crates.io dcp-ai 2.14.0、デフォルトの feature 8 カタログ
crates-otlp 同じクレート、otlp feature 付き 8 カタログ

公開済みバージョンは 6 つ、レーンは 9 つです。6 つのレーンが 8 カタログを、WebAssembly の 2 つのレーンが 6 カタログを実行するため、カタログセルは 60、これにブロックが 3 つ加わります。manifest.json には、期待される同一性(監査ドシエのもの)、カタログ、レーンが記載されています。expected-blocks.json には、各ブロックが報告すべき識別子が記載されています。

1 回の実行で行うこと

  1. 作業ディレクトリ。 新しい mkdtemp ディレクトリ、または --work <dir>。後者は存在していてはならず、チェックアウトの外にある必要があります。再利用されるものはありません。
  2. 環境。 すべてのサブプロセスは、ツールが構築した環境を受け取ります。HOME、TMPDIR、キャッシュ、各パッケージマネージャーの設定は作業ディレクトリ内にあります。PATH はそうではありません。ツール独自の、ホストのディレクトリの短い一覧です(node のディレクトリ、/usr/bin、/bin、およびオプションで指定したツールチェーンのディレクトリ)。継承されるものはありません。認証情報も、プロキシも、ユーザーやプロジェクトの設定も継承されません。
  3. インストール、同一性、取得元(エコシステムごと。後述)。各パッケージについて、3 つの値が一致しなければなりません。マニフェストの値、ダウンロードしたバイト列からツールが計算したハッシュ、パッケージマネージャーまたはレジストリが記録する値です。ダウンロードしたアーカイブは、インストールまたはコンパイルされるものと、ファイル単位で双方向に一致しなければなりません。ただし、インストーラー自身が書き込む少数のファイルは除きます。それらは後述します。
  4. セル。 レーンとカタログごとに、キットのランナーを 1 回実行します。キット自身のラッパーがインストール済みパッケージを指します。レポートは、この実行のものである場合にだけ有効です。すなわち、事前に存在しなかったパス、観測された時間区間内の実行で、まだ見たことのない識別子を持ち、要求した argv、kit.catalog_sha256、フィクスチャのハッシュ、スイート、ケース識別子と一致し、ステータスがランナーの終了コードと整合していることです。各ケースは、そのクラスで許されるステータスを持たなければならず、各スイートとレポートの件数(executed を含む)は、ケースから数えたものと一致しなければなりません。
  5. ブロック。 カタログセルではない証拠(CLI のスイート、そのネガティブコントロール、ブラウザーの直接セクション)は、そのレーンのブロックです。キャンペーンは、セルと同様に各ブロックをちょうど 1 回要求します。レーンのインストールやステータスが、ブロックの代わりになることはありません。
  6. コントロール(同じ実行内)。コントロールを持つ各レーン(npm-sdk、npm-cli、npm-wasm-browser)は、選択されるたびに自身の固定セットを満たす必要があります。各識別子について ok === true の結果がちょうど 1 つあり、それ以外の結果がないこと。そうでなければキャンペーンはエラーです。一部は、合格しなければならない肯定的なコントロールです。残りは、実際の出力を改変したコピー(結果の欠落・重複・未知の結果、古いレポート、整合しない件数、改変された同一性、ローカルの取得元)と、実際の実行(失敗しなければならない反転した期待値、ラッパーとハーネスの障害、タイムアウト、取り残されたプロセス、外部から停止されたブラウザー実行)で、それぞれが、求められているとおりの形で拒否されなければなりません。
  7. レポート。 作業ディレクトリの report.json。ランナーのレポート、ブロックの出力、ログ、アーカイブ、ロックも保存されます。すべて保持されます。--clean は、この実行でインストールされたコンシューマーとキャッシュだけを削除します。

プロセスは、そのすべての観測結果が正常である場合にだけ、実行されたものとして扱われます(シグナルなし、期限超過なし、出力上限なし、取り残しなし)。終了コード 0 だけでは不十分です。各フェーズには期限があり、スーパーバイザーは、プロセスグループがどうであれ、プロセスツリー全体を停止します。

npm(npm-sdk、npm-wasm-node、npm-wasm-browser、npm-cli)

3 つのパッケージを正確なバージョンで指定し、ロックを持つ 1 つのコンシューマー(consumers/npm)を使用します。コンシューマーとロックの静的チェック(正確なバージョン、すべてのエントリーが registry.npmjs.org 由来で sha512 の integrity を持つこと、リンクがないこと、各 DCP パッケージが 1 回だけであること)、空のキャッシュへの npm pack とツールによるハッシュ計算、npm ci --ignore-scripts(locked)または npm install --ignore-scripts(fresh)、その後アーカイブとインストール済みツリーの比較を行います。

@dcp-ai/wasm 2.2.0 には、コンパイル元の Rust クレートの名前とバージョン(dcp-ai 2.14.0)を記した pkg/package.json が含まれます。レポートは両方の同一性を記録し、両者が等しいことは決して期待しません。

  • npm-cli。 cli/__tests__ の 4 つのスイートとキットの negative-control.mjs を、インストール済みの CLI(DCP_CLI_ROOT)に対して実行します。対象は、その CLI のバイナリ、lib/、npm がインストールしたとおりの依存パッケージです。ツールは事前に、インストール済みの CLI が、検証済みの唯一の @dcp-ai/sdk を読み込むことを確認します。ネガティブコントロール自身の判定をそのまま信用することはありません。その出力は、反転した 4 つのケースだけがそれぞれアサーションで失敗し、残りの 214 件が合格していることを示さなければなりません。
  • npm-wasm-browser。 キットの wasm-browser.mjs が、一時プロファイルを使った実際の Chrome 上で公開 API DcpWasm を操作します。配信されるファイルは、ツールがインストール済みツリーから書き出す SHA256SUMS に固定されます。各セルと直接セクションには、ラッパー自身がその実行について書き出すブラウザー記録が必要です。その記録は、新しく、その実行の識別子を持ち、観測された時間区間内にあり、インストール済みファイルを配信しており、ブラウザーが応答して終了し、プロファイルが削除されたことを示していなければなりません。

Python(pypi-wheel、pypi-sdist)

各レーンは独自の仮想環境を持ちます。Python は -I -B で実行し、pip は --isolated、キャッシュなし、設定ファイルなしで、https://pypi.org/simple/ を唯一のインデックスとして実行します。

  • Wheel。 pip download を行い、何かをインストールする前に SHA-256 を確認します。次に pip install --only-binary :all: を実行します。pip 自身のレポートが、インストールしたハッシュを示し、すべてのファイルが files.pythonhosted.org から取得されたことを示します。wheel とインストール済みディストリビューションは双方向に比較します。すべてのモジュールファイルが一致しなければならず、ほかに何もインストールされていてはなりません。比較から除外するのは、pip 自身が *.dist-info に書き込むファイルだけです。RECORD(書き換えられる)、INSTALLER、REQUESTED です。ここで direct_url.json があれば問題として扱います。ツリーを比較する時点でバイトコードは存在しません(--no-compile、-B)。import dcp_ai は、その site-packages から読み込まれなければならず、user site が無効で、作業ディレクトリとインタープリターの外にあるパスが含まれていてはなりません。
  • Sdist。 ファイルはまず fetch-pypi.mjs によってバイト列としてダウンロードされます。このスクリプトは何も展開せず、何も実行しません。その SHA-256 をマニフェストと比較します。インデックスがそのファイルについて宣言する値は別に記録し、信用しません。その後に初めて展開し、ビルド依存関係をインストールし、ローカルファイルから --no-index --no-build-isolation で wheel をビルドします。ビルドした wheel は、レポート内で別のアーティファクトとして扱います。そのモジュールファイルは、双方向で sdist のものと正確に一致しなければなりません。ビルドした wheel はクリーンな環境にインストールし、wheel レーンと同じように比較します。ただし、ここでは direct_url.json があることが期待され(ローカルの wheel をインストールしたため)、それは作業ディレクトリ内を指していなければなりません。
  • ロック。 consumers/python/<platform>/ には、wheel.txt(wheel とそのランタイム依存関係)、sdist.txt、build.txt(ビルド依存関係)があり、すべてハッシュで固定されています。platform.json もあります。プラットフォームごとに 1 つのディレクトリがあり、実行中のインタープリターによって選択されます。cpython-3.12-darwin-arm64 と cpython-3.12-linux-x86_64 です。それ以外のインタープリターやプラットフォームでは、locked は試行されず、非対応として拒否されます。

Go(go)

キットの main.go をバイト単位でそのまま使い、コミットされた go.mod と go.sum(consumers/go、replace なし)とともに、-mod=readonly、GOPROXY=https://proxy.golang.org、GOSUMDB=sum.golang.org、GOENV=off、GOWORK=off でビルドします。モジュールはまず、どのモジュールにも属さない場所でダウンロードするため、go はチェックサムデータベースと照合します。ツールは、ダウンロードした zip のファイルから h1 を自ら計算し、zip と go がコンパイルに使うディレクトリを比較し、バイナリのビルド情報を読み取ります。モジュール、そのバージョン、h1 が一致し、何も置き換えられていないことを確認します。レポートには、go ツールがビルド対象とするアーキテクチャーを記録します。これはホストのアーキテクチャーと同じとは限りません。

Rust(crates-default、crates-otlp)

キットの src/main.rs をバイト単位でそのまま使い、それぞれ独自の Cargo.toml と Cargo.lock(consumers/rust-default、consumers/rust-otlp)、CARGO_HOME、target ディレクトリを持つ 2 つのコンシューマーでビルドします。コンシューマーの Cargo.toml は、ツールが期待する唯一のマニフェストと行単位で一致しなければなりません。cargo fetch --locked の後、何かをコンパイルする前に .crate のハッシュを計算し、マニフェストおよびロックと比較します。cargo metadata は、dcp-ai が crates.io 由来であり、作業ディレクトリ内のディレクトリにあり、そのレーンの feature を持つことを示さなければなりません。.crate はそのディレクトリと双方向に比較します。ただし、cargo 自身のマーカーである .cargo-ok は除きます。その後 cargo build --locked を実行します。

locked と fresh

  • locked(デフォルト):サードパーティーの依存関係は、コミットされたロックから取得します。選択は再現可能です。ホストのツール(npm、pip、Go、cargo、rustc、C コンパイラー、Chrome)は固定されず、記録されるだけです。
  • fresh:新しいコンシューマーが取得するのと同じように、その時点で解決します。結果のロックは作業ディレクトリに保存されます。package-lock.fresh.json はそのルートに、それ以外は locks/ の下に置かれます。この場合、sdist レーンのビルド依存関係は記録されますが、固定はされません。

6 つの DCP の同一性は、どちらのモードでも要求されます。

CI

.github/workflows/release-validation.yml は、ubuntu-24.04 上でツールを実行します。手動(workflow_dispatch)、またはこのツールや適合性キットの入力を変更する PR で実行します。PR では完全なキャンペーン(all、locked)を実行します。リポジトリへの読み取りアクセスだけを持ち、シークレットも公開ステップもありません。入力は、レーン(all または 1 つのレーン)と解決モードです。結果は ci.mjs が決定します。PASS は、all で終了コード 0 の場合だけです。診断として報告される PARTIAL は、1 つのレーンで終了コード 4 の場合だけです。それ以外の組み合わせはすべてジョブを失敗させます。作業ディレクトリは、どの場合もアーティファクトとしてアップロードされます。

別のリリースに合わせた更新

このツールは何も公開しません。リリースが公開され、その識別子が監査ドシエに記録された後に、次の手順を行います。

  1. 新しいバージョンと識別子を、manifest.json と、それらを固定しているテストに記入します。パッケージのファイル名(file)と、@dcp-ai/wasm の内部クレートのバージョンも、それに合わせて変わります。
  2. コンシューマーを更新します。consumers/npm/package.json、consumers/go/go.mod、2 つの Cargo.toml のバージョンと、Python のロックの DCP の行です。
  3. ロックを持つ各プラットフォームで、--resolution fresh を使ってロックを再生成し、作業ディレクトリからコピーします。ルートにある package-lock.fresh.json と、locks/python/<platform>/、locks/go/、locks/rust-default/、locks/rust-otlp/ です。Python のロックは、そのプラットフォーム上で実行した場合にだけ生成されます。
  4. キットの CLI スイートまたは直接セクションが変わった場合は、expected-blocks.json を更新します。
  5. テストを実行し、その後 locked モードで完全なキャンペーンを実行します。

制限事項

  • 1 回のキャンペーンは 1 つの環境です。実行された内容はガイドに記載されています。それ以外のことは主張しません。
  • ラッパーとランナーはチェックアウトのもので、テスト対象のパッケージはレジストリのものです。モジュール解決は計測していません。取得元の確認は、ロック、インストール済みツリー、ラッパーが報告するパスによります。
  • Python のロックは 2 つのプラットフォームを対象としています。ロックに含まれるハッシュは、ほかのプラットフォームについて何も示しません。
  • ブラウザーのバージョンと user agent は、ブラウザーが報告したとおりに記録します。user agent は情報が削減されたもので、どのアーキテクチャーで実行されたかは示しません。
  • プロセスのクリーンアップは、プロセステーブルを読み取って行います。1 回のポーリング間隔(250 ms)以内に終了する親によって切り離されたプロセスは見えません。ツール自体が SIGKILL で終了させられた場合は、何もクリーンアップされません。ps が失敗した場合は、シグナルを一切送らず、クリーンアップが確認できなかったことをレポートに記録します。外部から停止されたブラウザー実行は、その一時プロファイルを作業ディレクトリに残します。
  • プロキシなし:サブプロセス用に構築する環境には、プロキシの変数がありません。
  • レポートには、ホストの絶対パスが含まれます。環境の値は、ツールが構築したもの以外は含まれません。