ドキュメント
LayerFS を使ったローカル開発
Native Shell のリポジトリには、再利用可能なビルドワークスペースをテストするためのローカル LayerFS ワークフローが含まれています。このワークフローは、コミット済みのソースツリーをインポートし、Linux Sandbox 内で Cargo を実行し、生成されたファイルを保存し、その保存状態から作成したフォークでテストを再度実行します。ソースの編集と Git コミットは、既存の NSH チェックアウトで行います。
#スナップショットが保存するもの
LayerFS はファイルシステムツリーを保存します。対象は、ソースファイル、Git メタデータ、無視されたファイル、そしてワークスペース内で生成されたビルド成果物です。フォークは保存されたツリーから開始され、独自の変更を記録します。これにより、複数のタスクが同じビルド済みの状態から開始しつつ、それぞれの変更を分離しておけます。
NSH のローカルトライアルでは、最初のブランチが nsh-package のテストを実行し、target/ と無視されるプローブファイルを保存します。2 番目のブランチはそのコミットから開始し、それらのファイルを確認して、オフラインでテストを繰り返します。3 番目のブランチは元のソース状態から開始し、分離が保たれていることを検証します。
| フェーズ | 内容 |
|---|---|
| Init | チェックアウトの HEAD コミットのエクスポートと、新しい .git ディレクトリをインポートします |
| Build | ワークスペースをフォークし、cargo +1.98.1 test --locked -p nsh-package を実行します |
| Commit | 生成された target/ とプローブファイルを LayerFS の履歴に保存します |
| Reuse | 保存されたブランチをフォークしてマウントし、--offline でテストを繰り返します |
| Verify | コミット履歴を読み取り、元のソースブランチがクリーンなままであることを確認します |
呼び出しのたびに新しい Store が作成され、この一連のシーケンスがすべて実行されます。スナップショットの再利用は、その呼び出し内のブランチ間で行われます。このトライアルで測定すべき効果は、マウント、コミット、アンマウントの時間を含め、テスト済みで利用可能な別のワークスペースに到達するまでのコストです。
#開発環境を準備する
次の条件を満たす NSH リポジトリのチェックアウトを使用します。
- Store ボリュームに ext4、XFS、Btrfs のいずれかを使用する、ローカルの Linux Docker エンジン。Linux 上、または OrbStack などの macOS 環境経由で実行します。
- API 1.54 以降を提供する Docker Engine。検証済みのローカル環境では Engine 29.7.2 を使用しています。
/dev/fuse。Docker がそのデバイスとCAP_SYS_ADMINを Sandbox に公開できるようにしておく必要があります。- Git と Python 3.12 以降。Linux では、rustup と C/C++ コンパイラも必要です。
- 固定された LayerFS ソース、Cargo の依存関係、ランタイムイメージを取得するためのネットワークアクセス。
このトライアルは、ソースコミット 2aaf2552ce21154928b9f9c83582e41a637f5910 に固定された LayerFS 1.0.0 をビルドします。ランタイムイメージは、Linux プロセス用に Rust 1.98.1、Node、clang を提供します。アップストリームの SDK ガイドでは、Store と Sandbox のライフサイクルを説明しています。
NSH チェックアウトのルートで次のコマンドを実行します。
./scripts/layerfs prepare
./scripts/layerfs smoke
./scripts/layerfs status
この実行ファイルは、macOS 上の Homebrew のインストールを含め、Python 3.12 以降を選択し、アクティブな Docker コンテキストからソケットを検出します。ローカルソケットを明示的に選択するには --docker-socket PATH を使用します。
prepare は Docker API を確認し、Rust 1.85.1 をインストールして、固定されたデーモン、Node アドオン、ランタイムイメージをビルドします。macOS では、これらを一時的な Linux ビルドコンテナ内で準備します。通常の Cargo レジストリと target/ を再利用し、既存の CARGO_TARGET_DIR 設定も引き継ぎます。正確なランタイムイメージ ID とデーモンのパスは .nsh/layerfs/deployment.json に記録されます。status は、保存されたビルド成果物とイメージを確認し、最新の検証結果を表示します。
smoke は、チェックアウトのコミット済みの HEAD から .nsh/layerfs/nsh-<id>/input を作成し、その入力を新しい Store にインポートします。ソースの編集内容を含めるには、実行前にトピックブランチへコミットしてください。生成される入力は新しい Git リポジトリから始まり、ビルド成果物は Sandbox 内で取得されます。
制御プロセスは、名前付きのローカル Docker ボリューム内で Store を開きます。Sandbox デーモンは同じ Store を開き、ブランチのファイルシステムビューをマウントします。Cargo はそのマウントされたワークスペース内で実行されます。通常のチェックアウトは、引き続きソースの変更を編集してコミットする場所です。
#結果を読み取る
コマンドは、レシートディレクトリと Store ボリューム名を出力します。生成された result.json を開いて、"ok": true と各フェーズの経過時間を確認してください。同じディレクトリには、ソースコミットとイメージを識別する settings.json と、SDK 呼び出しを記録する trace.jsonl があります。
ローカル検証では、ビルドフェーズと再利用フェーズのそれぞれで 22 件のパッケージテストを実行しました。最初のビルドとテストのフェーズには依存関係のダウンロードが含まれていました。オフラインフェーズでは、保存された target/ と同じ Sandbox の Cargo レジストリを使用しました。その価値を評価する際は、ご自身のワークロードで、サイクル全体を通常の Cargo ビルドと比較してください。
ビルド済みの状態を再利用する場合は、オペレーティングシステム、アーキテクチャ、ツールチェーン、依存関係を一致させてください。このガイドのワークロードは Linux の nsh-package テストスイートです。プラットフォーム UI のチェックには、それぞれのプラットフォーム環境を使用します(Testing)。
#有用な成果を残し、Store を管理する
ソースの変更は Git で管理し、プルリクエストで提出してください。このトライアルでは、再構築可能なワークスペース履歴のために、LayerFS の Disposable Store プロファイルを使用します。測定結果を記録する場合は、有用なレシートを別途保管してください。
生成される入力では、LayerFS がインポートする間オブジェクトデータベースを安定させるため、最初のコミット時に Git の自動メンテナンスを無効にしています。トライアル内の Git ステータスチェックでは --no-optional-locks を使用し、ステータスの読み取りで元のインデックスが保持されるようにしています。カスタムスクリプトでは、ツリーをインポートまたはコミットする前に、バックグラウンドの書き込み処理を完了させてください。
アンマウントに成功した Sandbox は、LayerFS のアイドルタイムアウト後に停止します。各トライアルは Store ボリュームを保持します。そのボリュームとコンテナは、レシートにある識別子を使って個別に管理してください。失敗した操作は、診断のために記録された状態を保持します。スクリプト、レシート、リソースのクリーンアップについては、メンテナ向けリファレンスを参照してください。