implement-prepare-environment-script について
ビルドを一時作業フォルダーにステージングし、適合性テスト前に依存関係を分離して準備するスクリプト生成スキルです。言語や Bash、PowerShell に応じた共通パターンを扱います。
主な機能
- 01ビルドを一時作業フォルダーにステージング
- 02依存関係とビルド成果物を分離して準備
- 03適合性テストスクリプトとの連携
- 04Bash と PowerShell のパターンに対応
活用シーン
- 01適合性テストの実行ごとの依存関係導入を削減
- 02プロジェクトを適合性テスト向けに準備
- 03ソースフォルダーを変更しない準備スクリプトの生成
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ plainlang/ plain-forge --skill implement-prepare-environment-scriptインストール後、Claude Code で/implement-prepare-environment-scriptを入力して読み込まれたか確認します。
詳細
環境準備スクリプトの実装
このスキルでは、一貫した言語に依存しないパターンに従って、適合性テストの実行前に行うビルドごとに一度だけのセットアップを実施する、単一の実行可能なスクリプトを作成します。
参照実装は assets/prepare_environment_java.sh と assets/prepare_environment_python.sh です。まずこれらを読み、作成するスクリプトはすべて、そのパターンを対象言語のツールとユーザーのシェル環境に合わせて忠実に移植してください。
環境準備スクリプトの目的
prepare_environment_<lang> は run_conformance_tests_<lang> と対になる任意のスクリプトです。適合性テストスクリプトの直前に実行され、次の目的で使用します。
- ビルドを作業フォルダー(適合性テストスクリプトが使用するフォルダーと同じフォルダー)にステージングします。
- 依存関係キャッシュ/ビルド成果物を事前に準備し、適合性テストスクリプトが何もない状態から開始しても、再ダウンロードや再コンパイルが発生しないようにします。
このスクリプトがプロジェクトに存在する場合、対応する適合性テストスクリプトの依存関係インストールフェーズは「有効化のみ」に簡略化されます(implement-conformance-testing-script/SKILL.md →「prepare_environment が存在する場合のセットアップの省略」を参照)。存在しない場合は、適合性テストスクリプト内でセットアップを実行します。
このスクリプトが存在する構造上の理由
適合性テストランナーは、レンダラーによってレンダリングごとに一度ではなく、機能仕様ごとに一度呼び出されます。モジュール内の各機能仕様には、それぞれ専用の plain_modules/<module>/tests/<spec>/ フォルダーがあります。レンダラーは新しい仕様のコード生成を完了すると、回帰を検出するため、同じモジュール内のすべての以前の仕様の適合性テストを実行します。機能仕様が N 個あるモジュールでは、レンダリングごとに適合性テストスクリプトがおよそ N 回呼び出されます。
準備スクリプトがないと、N 回の呼び出しすべてで、依存関係を一から完全にインストールします(Python 仮想環境と pip install、Maven の依存関係ツリー全体、npm ci、cargo build など)。レンダリングごとに N 回支払うこのコストが、規模の大きいプロジェクトのレンダリング時間の大半を占めます。
prepare_environment_<lang> は、レンダリングごとのインストールを一度に集約するために存在します。
- 準備スクリプトは一度だけ実行され、すべてをインストールし、作業フォルダー
<temp>/<lang>_<basename>/内のプロジェクト固有の隔離場所(./.venv、./node_modules、./.m2、./.gocache、./.cargo、./.pub-cache)にデータを格納します。このスキルでの<temp>/<lang>_<basename>は、システムの一時ディレクトリ(Bash では/tmp、PowerShell では[System.IO.Path]::GetTempPath())、短い言語識別子、ビルドフォルダー引数のベース名($(basename "$1")/Split-Path -Leaf)を意味します。現在のレンダラーからは絶対パスが渡されるため、引数そのものは使用しないでください。 - その後、適合性テストスクリプトは 有効化のみのバリアントで N 回実行され、準備済みの作業フォルダーに接続し、インストール手順を完全に省略します。
- 結果として、インストールコストは
N × install-costから1 × install-cost + N × cheap-attach-costに削減されます。
これが、準備スクリプトと適合性テストスクリプトを分ける唯一の理由です。機能仕様が少なく、インストールのオーバーヘッドが無視できる程度のプロジェクトでは、準備スクリプトを生成しても労力の無駄です。その場合は、適合性テストスクリプトのインラインインストールバリアントで問題ありません。一方、仕様が多数ある(または依存関係が重い、GPU ビルドやブラウザーバイナリーなどを使う)プロジェクトでは、実質的に準備スクリプトが必須です。プロジェクトごとにユーザーが判断します。このスキルは、その判断を実行に移すためのツールです。
prepare_environment は適合性テスト専用です — ユニットテストには使用しません
よくある高くつく誤り:
prepare_environment_<lang>を、ユニットテストランナーも依存する「すべてのテストスクリプト向けに環境を準備する」汎用的なステップだと考えてしまうことです。そうではありません。
prepare_environment_<lang> は、run_conformance_tests_<lang> が後から接続する作業フォルダーをセットアップするためだけに存在します(適合性テストスクリプトの有効化のみのバリアントを介します)。run_unittests_<lang> とは関係ありません。
- ユニットテストランナーは完全に自己完結しています。独自の
<temp>/<lang>_<basename>/作業フォルダーにステージングを行い、実行のたびに依存関係も独自にインラインでインストールします(pip install -r requirements.txt、npm ci、mvn、cargo fetchなど)。終了時にはそのフォルダーを削除します。 - ユニットテストランナーは、
prepare_environmentが準備する作業フォルダーを読み取ることはありません。2つのスクリプトは、たまたま<temp>/<lang>_<basename>/という命名規則を共有している場合でも、別々の作業フォルダーを使用します。各スクリプトはそれぞれのコピーを削除して、再構築します。 - ユニットテストランナーは、事前に
prepare_environmentが実行されていることを必要としません。ユーザーや CI システムは、適合性テストを一度も実行せずに、スモークチェックとしてユニットテストを日常的に実行しており、今後もそれができなければなりません。 - ユニットテストランナーには有効化のみのバリアントはありません。
implement-unit-testing-scriptは毎回自己完結型のスクリプトを生成します。2つのバリアントを使い分けるパターンは、適合性テストランナー専用です。
prepare_environment_<lang> を作成する際は、適合性テストスクリプトが必要とするものだけに範囲を厳密に限定してください。適合性テストには不要でもユニットテストランナーが必要とする依存関係を組み込まないでください。ユニットテストランナーが読み取るファイルをステージングしないでください。生成したものを使用するのはユニットテストランナーだと想定しないでください。そうしたことをしようとしていると気づいたら、いったん止めてください。正しい対応は prepare_environment を変更せず、ユニットテストランナーに依存関係を独自にインラインで処理させることです。
環境準備スクリプトとその他のスクリプトとの違い
このスクリプトの構成の大部分は、2つの姉妹スクリプト(run_unittests_<lang> と run_conformance_tests_<lang>)と共通ですが、次の違いがあります。
- 位置引数は1つ:
<build_folder>。 適合性テストのフォルダーは指定しません。それはこのスクリプトではなく、適合性テストスクリプトへの入力です。 - テスト実行の手順はありません。 このスクリプトはステージングとインストール/ビルドのみを行います。ユニットテストも適合性テストも実行しません。
- 「テストが見つかりませんでした」というガードはありません。 同じ理由で、このスクリプトではテストを実行しません。
- 副作用は適合性テストスクリプトから確認できなければなりません。 このスクリプトが行う処理(作業フォルダー名、依存関係の分離場所、インストールしたアーティファクト)はすべて、適合性テストスクリプトが見つけることを期待するものと完全に一致している必要があります。調整に関する契約を参照してください。
それ以外(ツールチェーンの確認、ビルドのステージング、依存関係の分離、終了コード)はすべて同じです。
まずシェルを選択する
何かを書く前に、スクリプトの対象とするシェルの種類を決めてください。これは言語ではなく、このスキルを実行するホストマシンによって決まります。ホスト OS を事前に検出し、Bash をデフォルトにしないでください。
| ホスト OS | 出力 | 検出方法 |
|---|---|---|
| macOS | .sh | uname -s が Darwin を返す |
| Linux(WSL、CI ランナーを含む) | .sh | uname -s が Linux を返す |
| ネイティブ Windows(PowerShell、cmd) | .ps1 | $OS 環境変数に Windows_NT が含まれるか、uname -s が MINGW*/MSYS*/CYGWIN* を返す(Windows 上の Git Bash/MSYS2/Cygwin シェル) |
不明な場合は uname -s 2>/dev/null || echo "$OS" を実行してください。確認する前にユーザーに尋ねないでください。
プロジェクトが、異なるチームメンバーや CI ランナーによって macOS/Linux と Windows の両方 で使用される場合は、このスクリプトの .sh 版と .ps1 版を両方生成してください。これらは相互に機械的に変換でき、終了コードと分離パスを共有します。実行時にはオーケストレーター(またはユーザーの CI)が適切な方を選択します。対応する run_conformance_tests_<lang> スクリプトも、同様に両方生成する必要があります。
どちらのシェルでも同じパターンが適用されます。変わるのは構文だけです。
パターン
すべての prepare-environment スクリプトは、次の手順をこの順序で実装する必要があります。
- ツールチェーンの確認。 必要な言語ランタイム/ビルドツール(および必要な場合は指定バージョン)がインストールされていることを確認します。インストールされていない場合は、エラーを出力してコード
69で終了します。 - 引数の検証。 位置引数
<build_folder>を1つ受け取る必要があります。指定されていない場合は、使用方法を出力してコード69で終了します。レンダラーはこの引数を絶対パスとして渡します(古いバージョンのレンダラーは相対パスを渡していました)。スクリプトはどちらでも動作する必要があります。そのため、手順3の作業フォルダー名は引数そのものではなく、引数の ベース名 から作成します。 - 作業ディレクトリの設定。
<temp>/<lang>_<basename>に作業フォルダーを定義します。これは適合性スクリプトが使用するパスと同一です(Bash では/tmp/<lang>_$(basename "$1")、PowerShell ではJoin-Path ([System.IO.Path]::GetTempPath()) "<lang>_$(Split-Path $BuildFolder -Leaf)")。フォルダーを削除し(rm -rf/Remove-Item -Recurse -Force)、再作成します。以降のすべての書き込みは、このフォルダー内にのみ行います。クリーンアップ用の trap やfinallyによる削除を登録しないでください。2つの兄弟スクリプトとは異なり、prepare は終了時に作業フォルダーを意図的に残します。このフォルダーが、適合性スクリプトによって添付される成果物です。 - ビルドのコピー。
<build_folder>($1)の内容をすべて再帰的に作業フォルダーへコピーします。この手順以降、ソースフォルダー($1)はスクリプトの残りの処理では読み取り専用として扱います。 - 作業ディレクトリに移動。
<temp>/<lang>_<basename>にcd/Set-Locationで移動します。失敗した場合はコード69で終了します。残りの手順はすべて作業フォルダー内で実行し、$1へ書き戻してはいけません。 - 依存関係/ビルド前成果物を
<temp>/<lang>_<basename>内の隔離環境にインストールします。 作業フォルダーごとの依存関係の格納場所(./.venvの Python venv、ローカルの./node_modules、プロジェクト専用の Maven リポジトリ./.m2など)を設定し、すべての依存関係をそこにインストール/解決します。テストの実行前にビルドが必要な言語(Java、Rust、Go)では、ビルド成果物も生成し、適合性スクリプトが見つけられる場所、つまり$1内やユーザーのホームディレクトリではなく、作業フォルダー内に配置します。ソースのビルドフォルダー($1)、ユーザーのグローバルキャッシュ(~/.m2、システム全体のpip、~/.cargo、~/.npmなど)、または<temp>/<lang>_<basename>の外には、決してインストールしないでください。サブ手順が失敗した場合はコード69で終了します(Maven/pip/npm の終了コードをそのまま伝播させないでください。不完全な準備環境そのものが回復不能なエラーです)。言語ごとの詳細については、依存関係の隔離を参照してください。
以上です。手順7はありません。依存関係がインストールされ、該当する場合はビルド成果物がローカルリポジトリに配置されたら、このスクリプトの役割は終了です。内容がそろった作業フォルダーは、適合性ランナーのために意図的に残します。
ビルドフォルダーは読み取り専用です — 厳守してください
$1 として渡されるソースビルドフォルダーは入力専用です。Prepare はステップ 4 で一度だけこのフォルダーから読み取り、作業フォルダーに内容をコピーします。それ以降、スクリプトで次の操作を行ってはいけません。
- 依存関係をインストールする(
$1内でのpip install、$1内でのnpm install、$1に書き込むmvn install、$1内に生成される Cargo ビルド成果物は禁止です)。 $1内に仮想環境、node_modules、.m2、.gocache、.cargo、.pub-cacheディレクトリを書き込む。$1内で事前ビルドする(コンパイル出力はすべて、target/、build/、dist/、ネイティブバイナリ、生成ソースを含め、<temp>/<lang>_<basename>内に置きます)。$1内にログ、キャッシュ、一時ファイルを作成する。
ビルドフォルダーは、レンダラー(デフォルトでは plain_modules/<module>/code)および適合性スクリプトと共有されています。適合性スクリプトは if [ ! -d "/tmp/<lang>_$(basename "$1")" ] を使って作業フォルダーのステージング状態を確認し、$1 自体がレンダリング直後と同じ状態であることを前提としています。$1 に書き込むと、「何が生成されたか」というレンダラーの認識が損なわれ、プロジェクトが $1 をコミットしている場合は git のステータスに不要な変更が生じます。また、適合性スクリプトが独自のセットアップ中に rm -rf $1 を実行した場合、Prepare が行った作業がひそかに破壊されます。
<temp>/<lang>_<basename> にステージングするのは、ソースビルドフォルダーをレンダリングから作成されたクリーンで再現可能な成果物として保ち、ビルドの不要な生成物をユーザーのプロジェクト内に残さないためです。依存関係、コンパイル済みクラス、バイナリ、キャッシュはすべて作業フォルダー内に置く必要があります。適合性スクリプトのアクティブ化のみを行うバリアントが使用するのは、このフォルダーだからです。
cwd が $1 であるコマンド、または対象パスが $1/ で始まるコマンドを実行しようとしている場合は、いったん止めてください。処理を <temp>/<lang>_<basename> 内に移すか、スクリプトで禁止されている操作を行おうとしています。
連携の契約
これはこのスキルで最も重要な点です。Prepare 環境スクリプトと、それに対応する適合性スクリプトが、処理の配置場所について一致していない場合、Prepare スクリプトがない場合よりも悪い結果になります。時間がかかるうえ、適切な環境が実際には準備されていないのに、環境が温まったように見えてしまいます。
2 つのスクリプトは、次の点で一致している必要があります。
| 内容 | 重要な理由 |
|---|---|
作業フォルダーのパス(<temp>/<lang>_<basename>) | 適合性スクリプトはこのフォルダーに cd します。Prepare が別の名前を使うと、適合性スクリプトには空のフォルダー、または新たにステージングされたフォルダーが見え、すべての作業をやり直すことになります。両方のスクリプトで同じ方法を使ってパスを導き出す必要があります。システムの一時ディレクトリに <lang>_ と $1 のベース名を組み合わせます。 |
| 作業フォルダーのライフサイクル | Prepare は開始時にフォルダーを削除して再作成し、終了時には内容を保持します。アクティブ化のみを行う適合性スクリプトはこのフォルダーを使用し、成功時も失敗時も削除しません。次回の実行時にフォルダーを再作成するのは Prepare だけです。どちらかのスクリプトにクリーンアップ用のトラップを設けると、この 2 つのスクリプトを組み合わせる目的である作業の償却効果が損なわれます。 |
依存関係の分離先(./.venv、./.m2、./node_modules、./.gocache、./.cargo、…)— 作業フォルダーからの相対パス | Prepare が ~/.m2 に内容を配置し、適合性スクリプトが ./.m2 から読み取る場合、温めたキャッシュを参照できません。両方のスクリプトで、作業フォルダー内のプロジェクトローカルなパスを必ず使います。 |
| ビルド成果物の場所(Java/Rust/Go) | 適合性テストのプロジェクトはビルド成果物を必要とします。適合性スクリプトが期待する正確な場所(例:Java の場合は同じプロジェクトローカルの ./.m2)に見つかる必要があります。 |
| ツールチェーンのバージョン | Prepare が Java 21 で実行され、適合性スクリプトが Java 17 で実行されると、クラスファイルの非互換性が Prepare の実行時ではなく、テスト時に発生します。両方のスクリプトで同じツールチェーンの確認を行う必要があります。 |
判断に迷ったら、まず適合性スクリプトを読み、その前提条件をそのまま反映してください。
規約
2 種類のシェル形式に共通です。
- 終了コード:
69— 回復不能:引数がない、ツールチェーンがない、作業フォルダーに移動できない、依存関係のインストール/ビルドに失敗した場合です。ここではすべての失敗を回復不能として扱ってください。prepare に「軽度の」失敗モードはありません。0— 成功。
- 作業フォルダーの命名:
<temp>/<lang>_<basename>— システムの一時ディレクトリ(Bash では/tmp、PowerShell では[System.IO.Path]::GetTempPath())に、言語の短い識別子(java、python、node、go、rustなど)と、最初の(かつ唯一の)引数のベース名($(basename "$1")/Split-Path -Leaf)を組み合わせます。生の引数をパスに埋め込んではいけません。引数が絶対パスの場合があり、連結するとパスが重複して壊れるためです。依存関係のインストール、ビルド出力、キャッシュ、ビルド済みアーティファクトはすべてこのフォルダー内に置きます。手順 4 の後は、スクリプトが$1に触れてはいけません。 - 終了時にクリーンアップしないでください。 prepare は作業フォルダーにファイルを残します。このフォルダーが成果物です。
trap … EXIT/finallyによる削除を追加しないでください。このフォルダーは、次回 prepare が削除して再作成するときに回収されます(また、OS が最終的に一時ディレクトリを回収します)。 - ログ記録 — 可能な限り詳細にしてください。 レンダリング後から適合性の結果が緑/赤で表示されるまで、オペレーターが目にするのはスクリプトだけです。prepare が失敗した場合、誰もが調査に使える唯一の証拠は stdout/stderr です。静かなヘルパーではなく、本番環境のランブックのようにスクリプトを扱ってください。具体的には、次のとおりです。
- 各手順の実行前に、その内容をすべて告知してください。 関係する各変数の値を正確に示します。解決済みのソースフォルダー、作業フォルダー、言語、検出されたツールチェーンのバージョン、分離ルート、依存関係マニフェストのパス、実行予定のインストール/ビルド前コマンドなどです。「依存関係をインストールしています」だけでは役に立ちません。「
./requirements.txtから依存関係を venv./.venvにインストールしています(/usr/local/bin/python3の Python 3.11.6)」のようにすれば、トリアージに必要な情報がそろいます。 - 重要なコマンドはすべて、実行前に表示してください。 Bash では、スクリプト本体に
set -xを使うか、各呼び出しの直前にecho "+ <cmd>"を使います。PowerShell では、$VerbosePreference = 'Continue'を設定し、Write-Verbose/Write-Hostを使って引数を含む各コマンド行を表示します。 - 明確なセクション見出しを表示してください。 各パターン手順に
===== [4/6] Copy build into working folder =====のような見出しを付けると、長いログを目で追いやすくなります。 - 相対パスだけでなく、解決済みの絶対パスを表示してください。 別のマシンでログを読むオペレーターが、venv /
.m2/.gocache/.cargo/.pub-cacheが実際にどこに配置されたかを把握できる必要があります。 - 手順 1 では、成功時も含め、ツールチェーン自身の
--version出力と解決元のパスを表示してください。 prepare がローカルでは成功して CI では失敗する場合に、これは調査に最も役立つ情報です。 - インストール/ビルド前の出力は、そのまま表示してください。
/dev/nullにパイプしたり、作業フォルダー内のログファイルにリダイレクトしたりしてはいけません。依存関係解決の各行、コンパイル警告、アーティファクトのインストール行をすべて、スクリプト自身の出力ストリームでオペレーターが確認できるようにしてください。Java のリファレンスにあるVERBOSE条件付きのラップは削除してください。prepare は最も時間のかかるフェーズであり、調査用データが最も重要な場所です。 - 失敗時は、
69で終了する前に実行予定だった内容を表示してください。 正確なコマンド、作業ディレクトリ、関連する環境変数(PYTHONPATH、NODE_PATH、GOMODCACHE、CARGO_HOME、MAVEN_OPTS、PUB_CACHE、PATH)を示します。 - 言語、作業フォルダー、分離ルート、実時間、終了コード
0を明記した最終サマリー行を表示してください。 これによりオペレーターは、何がどこに準備され、適合性の確認がどこを参照するのかを正確に把握できます。 ここで詳細なログは無駄ではありません。出力自体が処理内容を記録するスクリプトなら、トリアージにかかる時間は 30 秒で済みますが、簡潔さを優先すると 30 分かかることもあります。詳細さを簡潔さと引き換えにしてはいけません。
- 各手順の実行前に、その内容をすべて告知してください。 関係する各変数の値を正確に示します。解決済みのソースフォルダー、作業フォルダー、言語、検出されたツールチェーンのバージョン、分離ルート、依存関係マニフェストのパス、実行予定のインストール/ビルド前コマンドなどです。「依存関係をインストールしています」だけでは役に立ちません。「
date +%s.%N(Bash)/Get-Date(PowerShell)を使って依存関係のセットアップにかかった時間を計測し、最後に所要時間を表示してください。ここが最も時間のかかるフェーズであり、このスクリプトが存在する理由そのものです。所要時間を確認すれば、実際に時間を節約できているかがわかります。この所要時間の行は、上記の詳細なログ記録ルールで求められる最終サマリーの一部です。
依存関係の隔離
依存関係の環境は $WORKING_FOLDER 内に配置する必要があります。準拠スクリプトが環境を見つけられるようにし、他の言語やプロジェクトの並行実行が干渉しないようにするためです。その言語で最も標準的な隔離方式を選び、準拠スクリプトが参照する場所と一致させてください。
| 言語 | 隔離方式 | 準備コマンド($WORKING_FOLDER 内で実行) |
|---|---|---|
| Python | ./.venv に venv を作成 | python3 -m venv .venv && source .venv/bin/activate && pip install -r requirements.txt |
| Node.js | ローカルの ./node_modules(デフォルト) | npm ci(推奨)または npm install |
| Java | プロジェクト専用の Maven リポジトリ ./.m2 | mvn -Dmaven.repo.local="$(pwd)/.m2" install -DskipTests(プロジェクトの独自 jar をビルドしてリポジトリにインストールし、依存するテストプロジェクトから解決できるようにします) |
| Go | ./.gocache にモジュールキャッシュ | GOMODCACHE="$PWD/.gocache" go mod download && GOMODCACHE="$PWD/.gocache" go build ./... |
| Rust | ./.cargo に cargo のホームディレクトリ | CARGO_HOME="$PWD/.cargo" cargo build --tests(依存関係とテストをまとめてコンパイルします) |
| Flutter | ./.pub-cache に pub キャッシュ | PUB_CACHE="$PWD/.pub-cache" flutter pub get && flutter precache |
注意事項:
- インストール/ビルド前コマンド内のすべてのパスは、
<temp>/<lang>_<basename>からの相対パスです。 そのため、手順 5 でスクリプトは作業フォルダーにcdします。以降、./.venv、./node_modules、./.m2、./.gocache、./.cargo、./.pub-cache、およびすべてのコンパイル出力(target/、build/、dist/、ネイティブバイナリ)は<temp>/<lang>_<basename>内に解決され、$1内にもユーザーのホームディレクトリ内にも配置されません。 - Java/Rust/Go は、ダウンロードだけでなくコンパイルする必要があります。 メタデータの解決だけでは、準拠スクリプトはタイムアウトするか、最初から再コンパイルします。
mvn install、cargo build --tests、go build ./...を使ってください(dependency:resolve/cargo fetch/go mod downloadだけでは不十分です)。 - Python と Node.js はインストールだけで十分です。 これらはテスト実行時にインタープリターで実行、または JIT コンパイルされるため、
pip install/npm ciで十分です。 - 必ず隔離用のフラグ/環境変数を指定してください。
-Dmaven.repo.localを指定しないmvn、CARGO_HOMEを指定しないcargoなどは、$WORKING_FOLDERではなくユーザーのホームディレクトリに書き込みます。準拠スクリプトは誤った場所を参照し、事前準備も無駄になります。また、ユーザーのホームディレクトリが汚染されます。 - インストールの失敗は
exit 69としてください。 テストコマンドの終了コードをそのまま返す準拠スクリプトとは異なり、準備処理では「ユーザーのテストが正当に失敗した」という扱いはありません。ここでの失敗はすべて、環境が使用できないことを意味します。
Bash 固有の注意事項
- シェバン:
#!/bin/bash。 - ファイル名:
prepare_environment_<lang>.shとし、assets/に配置します。 - 引数:
$1。 - 実行可能にする:
chmod +x assets/prepare_environment_<lang>.sh。 cdの失敗確認: 参照例にあるcd ... 2>/dev/nullと[ $? -ne 0 ]のパターンを使います。成功ログは失敗確認の 後に 出力してください。確認より前に出力すると、失敗時に誤ったログになります。
PowerShell 固有の注意事項
- シェバンは不要です。 代わりに、先頭に
param([Parameter(Mandatory=$true)][string]$BuildFolder)ブロックを使います。 - ファイル名:
prepare_environment_<lang>.ps1とし、assets/に配置します。 - 終了コード:
exit 69などを使います(PowerShell でも Bash と同様に機能します)。 - ツールチェーンの確認:
Get-Command <tool> -ErrorAction SilentlyContinueを優先し、特定のバージョンが必要な場合は、ツールの--version出力を解析します。 - ファイルシステム:
Test-Path、Remove-Item -Recurse -Force、New-Item -ItemType Directory、Copy-Item -Recurse、Set-Locationを使います。パスはスペースに対応できるよう引用符で囲みます。 chmodの手順は不要です。 実行ポリシーによってスクリプトがブロックされる可能性がある場合は、スクリプトに組み込まず、ユーザーにSet-ExecutionPolicy -Scope CurrentUser RemoteSignedを案内してください。
ワークフロー
- ホスト OS を検出して、使用するスクリプトの種類を選びます。
uname -s 2>/dev/null || echo "$OS"を実行し、Pick the Shell First のルールを適用します。Darwin/Linux→.sh、Windows_NT/MINGW*/MSYS*/CYGWIN*→.ps1です。プロジェクトが macOS/Linux と Windows の両方を対象としている場合(複数 OS のチームまたは CI)、.shと.ps1の両方を作成する計画を立て、各スクリプトについて手順 3~8 を繰り返します。 - 対象の言語と依存関係マニフェスト(
pom.xml、requirements.txt/pyproject.toml、package.json、go.mod、Cargo.tomlなど)を確認します。さらに重要な点として、対応するrun_conformance_tests_<lang>スクリプトがすでに存在する場合は、まずそれを読んで、合わせるべき分離パスとツールチェーンのバージョンを把握します。不明な点があれば確認してください。 - 正確な構成を再確認するため、assets/prepare_environment_java.sh を読みます。注:この参照スクリプトには、上記の契約と異なる点がまだあります(「アンチパターン」を参照)。不具合ではなく、契約に従ってください。
- 6 つのパターン手順をそれぞれ、対象言語およびシェルに対応するコマンドに置き換えます。ツールチェーンの確認と依存関係のインストール/ビルドは言語固有の部分であり、残りは Bash と PowerShell の構文間で機械的に置き換えます。
- 依存関係の分離 の表から、依存関係の分離方法を選びます。対応する
run_conformance_tests_<lang>スクリプトが使用するパスと一致していることを確認してください。 - 新しいスクリプトを適切な
test_scripts/の場所に保存します(例:test_scripts/prepare_environment_<lang>.sh/.ps1)。Bash の場合はchmod +xを実行します。 - 新しいスクリプトを参照するよう
config.yamlを更新します。prepare-environment-script:キーを追加または更新し、新しく作成したスクリプトへのパスを指定します(例:prepare-environment-script: test_scripts/prepare_environment_<lang>.sh)。これは必須です。レンダラーは、適合性テストを実行する前に準備スクリプトを呼び出すため、この参照を必要とします。 - スクリプトを、リンクされたリソースとしてベースの
.plainファイルにリンクします。 スクリプトをディスク上に作成したら、プロジェクトのベース.plainモジュール(通常は適合性テスト戦略の***test reqs***を含むもの)から参照する必要があります。これにより、レンダラーは準備に関する契約の根拠としてスクリプトの内容を確認できます。add-resourceスキルを使って、仕様の該当セクション内に標準的な Markdown リンク(例:[prepare environment](test_scripts/prepare_environment_<lang>.sh))を追加します。パスは.plainファイルを基準に解決してください。スクリプトは単一のテキストファイルなので、リンクされたリソースの契約をそのまま満たします。test_scripts/ディレクトリへのリンク、スクリプトの内容を仕様内で言い換えること、コマンドを仕様内に重複して記載することは避けてください。複数のベース.plainモジュールが準備に関する契約を参照している場合(例:ホストモジュールと、それを継承する統合モジュール)、必要とする各モジュールからスクリプトにリンクします。リンクされたリソースは、テスト要件を持つ仕様の隣に配置します。デフォルトでチェーンの最上位に置くわけではありません。リンクを追加した後、仕様をもう一度読み、リンク先のパスが今作成したスクリプトに解決されることを確認してください。 - 適合性テストスクリプトを調整します(次のセクションを参照)。 プロジェクトに対応する
run_conformance_tests_<lang>がすでに存在する場合は、必ず実施してください。 - 両方のスクリプトを配置し、
config.yamlを更新した後、準備スクリプトと適合性テストスクリプトを並べて読み直し、作業フォルダー名、分離パス、ツールチェーンのバージョンが一致していることを確認します。
既存の適合性テストスクリプトの調整
prepare_environment_<lang> スクリプトを追加すると、対応する run_conformance_tests_<lang> スクリプトの契約が変わります。準備スクリプトが処理するようになった作業はすべて、適合性テストスクリプトから削除する必要があります。そうしないと、再ステージングによって準備済みの作業が失われたり、再インストールによって重複したりします。このスキルを使用するたびに、必ず調整してください。
手順
- 既存のコンフォーマンススクリプトを探します。プロジェクト内の一般的な場所(
test_scripts/run_conformance_tests_<lang>.sh/.ps1、またはプロジェクトのconfig.yamlのconformance-tests-script:キーで指定されている場所)を確認します。 - 存在しない場合 → そこで終了します。調整するものはありません。 コンフォーマンススクリプトは後で
implement-conformance-testing-scriptが生成します。このスキルは、activate-only 版にも対応しています。 - 存在する場合 → 修正します。 prepare が現在担当している手順を特定し、削除します。
| コンフォーマンスの手順 | prepare が存在する場合の対応 |
|---|---|
ステージングブロック(作業フォルダーの rm -rf + mkdir -p + cp -R $1/*) | すべて削除します。 次のガードに置き換えます:WORKING_FOLDER="/tmp/<lang>_$(basename "$1")"; if [ ! -d "$WORKING_FOLDER" ]; then echo "Error: build folder missing — run prepare_environment_<lang>.sh first."; exit 69; fi |
依存関係のインストール/ビルド前処理(pip install、mvn install -DskipTests、npm ci、cargo build --tests など) | すべて削除します。 分離先が存在するか確認するガード(Python なら .venv/bin/activate、Java なら .m2/、Node なら node_modules/ など)に置き換え、存在しない場合は 69 で終了します。 |
アクティベーション手順(Python の source .venv/bin/activate、Java の -Dmaven.repo.local=$(pwd)/.m2) | 残します。 これがないと、テストコマンドから準備済みの依存関係を参照できません。 |
終了時の作業フォルダーのクリーンアップ(trap 'rm -rf "$WORKING_FOLDER"' EXIT /finally 内の Remove-Item) | すべて削除します。 activate-only 版では作業フォルダーを削除しません。ライフサイクルの管理は prepare が担当します。trap を残すと、最初のコンフォーマンス実行後にウォームアップ済みの環境が削除されるため、その後の実行はすべて「準備済み環境が見つからない」ガードに引っかかって失敗します。 |
| テスト実行+「テストが見つからない」ガード+終了コードの伝播 | 変更せず残します。 |
- コンフォーマンススクリプトの終了コードが引き続き
implement-conformance-testing-scriptの仕様に従っていることを確認します。新しい「準備済み環境が見つからない」ガードは69(回復不能な呼び出しエラー)で終了し、「テストが見つからない」ガードは引き続き1で終了し、テストコマンドの終了コードは引き続き伝播する必要があります。 - スモークチェックを実行します:
prepare_environment_<lang>.sh <build_folder> && run_conformance_tests_<lang>.sh <build_folder> <conformance_tests_folder>が最後まで正常に実行されることを確認します。prepare の実行直後にコンフォーマンスが「準備済み環境が見つからない」というエラーで失敗する場合は、2 つのスクリプトで作業フォルダーのパスまたは分離先の場所が一致していません。調整の契約に戻って確認してください。
この調整をスキップする場合
- コンフォーマンススクリプトがまだ存在しない場合。 調整するものはありません。
- コンフォーマンススクリプトにインラインのステージングやインストールの形跡がすでにない場合。 以前に activate-only 版として生成されたものです。そのままにしてください。
- ユーザーが prepare とコンフォーマンスを独立させるよう明示的に依頼した場合(例:prepare なしでコンフォーマンスを単独実行できるようにする場合)。両方のスクリプトの先頭にコメントでその旨を明記し、調整をスキップしてください。なお、これにより prepare が提供するはずだった速度向上はすべて失われます。
アンチパターン
- (重大な誤り)このスクリプトから単体テストランナーを事前準備しないでください。
prepare_environment_<lang>は適合性スクリプト専用です。単体テストランナー(implement-unit-testing-script)は常に完全に自己完結しており、呼び出しのたびに、準備スクリプトの有無にかかわらず、ステージング、インストール、実行を一度に行います。時間を節約するために、prepare_environmentに単体テストの依存関係をインストールする手順を追加しないでください。単体テストランナーは作成したものを読み取らず、連携させると、prepare と conformance の間で activate のみを行うという契約が破られます。上記のprepare_environmentは適合性専用 — 単体テスト用ではありません を参照してください。 - (重大な誤り)ソースのビルドフォルダー(
$1)にインストール、ビルド、その他の書き込みをしないでください。$1として渡されるビルドフォルダーは、手順 4 の後は読み取り専用の入力です。すべてのインストール、キャッシュ、ビルド成果物(target/、build/、dist/、ネイティブバイナリ、生成ソース)、ログ、一時ファイルは、<temp>/<lang>_<basename>に配置する必要があります。これには、$1をcwdまたはターゲットとしてpip install、npm install、mvn install、cargo build、go buildを実行しないこと、仮想環境/node_modules/.m2/.gocache/.cargo/.pub-cacheディレクトリを$1内に作らないこと、そして$1/以下のどのパスにもビルド済み成果物を生成しないことが含まれます。システムの一時ディレクトリにステージングするのは、ビルドフォルダーをレンダリングのクリーンな成果物として保つためです。そこに書き込むとレンダラーの認識が壊れ、git status が変動し、conformance スクリプトが独自に$1を再ステージングした場合は、気づかないうちに破棄されることがあります。 - (重大な誤り)ビルドフォルダーの引数をそのまま連結してパスを作らないでください。 現在のレンダラーから渡される
$1は絶対パスです。WORKING_FOLDER=.tmp/$1やWORKING_FOLDER=<lang>_$1のようなパターンでは、パスが二重になり(/project//abs/path/...)、典型的にはプロジェクトルートが重複したパスを示す「フォルダーが存在しません」またはImportError: Start directory is not importableエラーが発生します。必ず$(basename "$1")/Split-Path -Leafを使用してください。 - (重大な誤り)終了時に作業フォルダーを削除するクリーンアップトラップを追加しないでください。 内容が配置された作業フォルダーはこのスクリプトの成果物です。activate のみを行う conformance ランナーは、1 回のレンダリングにつき N 回、このフォルダーを使用します。終了時に削除すると、conformance の実行が毎回「準備済み環境が見つかりません」というエラーになります。(単体テストスクリプトとインラインインストールを行う conformance スクリプトは自分で後片付けしますが、prepare と activate のみを行う conformance は後片付けをしません。)
- ユーザーのグローバルな依存関係キャッシュ(
~/.m2、システム全体のpip、~/.cargo、~/.npmなど)に書き込まないでください。conformance スクリプトが読み取るのはプロジェクトローカルのキャッシュです。グローバルへの書き込みは参照されず、ユーザーのホームディレクトリを汚染します。 - conformance スクリプトと異なる名前の作業フォルダーを使わないでください。 名前は完全に一致させる必要があります。一方を変更した場合は、もう一方も変更してください。
- テストを実行しないでください。 単体テスト、conformance テスト、スモークテストのいずれも実行しないでください。prepare の契約は「環境をセットアップすること」です。テストの実行は関連する別スクリプトの役割です。
mvn/pip/npmから69以外の終了コードを引き継がないでください。 インストールに失敗した場合、環境は使用できません。オーケストレーターが「prepare の失敗」と「テストの失敗」を区別できるよう、すべての失敗をexit 69として扱ってください。- 「全員がインストール済み」であっても、ツールチェーンの確認を省略しないでください。 呼び出し元システムは終了コード
69を使ってランタイムの不足を検出します。また、prepare は通常、最初に実行されるスクリプトなので、JDK/Python/Node の不足を知らせるには最も効率的な場所です。 cdの成功確認より前に「移動しました...」という行を出力しないでください。 参照スクリプトはこの順序になっていますが、失敗時には誤った情報を出します。ガード処理の後にログを出すか、「移動を試みています...」と出力してください。- ソースフォルダーをその場で再利用しないでください。 必ず最初に
<temp>/<lang>_<basename>にコピーしてください。conformance スクリプトはこの分離を前提としています。 - Bash 版と PowerShell 版で終了コードの契約を変更しないでください。 同じ言語の
.shと.ps1は、同じ失敗条件に対して同一の終了コードを使う必要があります。 - 複数のシェルを混在させたスクリプトを書かないでください(例:PowerShell を検出する
.sh、またはその逆)。シェルごとに、適切な拡張子を付けたスクリプトを 1 つずつ用意してください。 - インストールにかかった時間を記録し忘れないでください。 所要時間のログがなければ、prepare が実際に時間を節約しているのか、conformance スクリプトがインラインで行っていた処理をそのまま実行しているのかを判断できません。
- prepare の生成後に既存の
run_conformance_tests_<lang>スクリプトを変更せずに残さないでください。 conformance スクリプトが独自にステージングとインストールを行うままだと、毎回 prepare の処理が(rm -rfによって)消去されるか、(インストールの再実行によって)重複し、このスキルの目的が損なわれます。必ず既存の conformance スクリプトを調整する手順を実行してください。 - ホスト OS を確認せずに Bash をデフォルトにしないでください。 ネイティブ Windows(Git Bash/WSL の外)の
.shスクリプトは実行できず、macOS/Linux の.ps1スクリプトも同様に役に立ちません。拡張子を決める前に、必ず最初にシェルを選ぶに記載された検出を実行してください。プロジェクトが両方をサポートする場合は、両方のファイルを作成し、対応する conformance スクリプトも両方の形式で調整してください。 config.yamlの更新を忘れないでください。 prepare environment スクリプトを作成した後は、必ずconfig.yamlのprepare-environment-script:キーを追加または更新して、新しいスクリプトを参照させてください。この項目がないと、レンダラーは conformance テストの実行前に prepare スクリプトを呼び出せず、最適化全体が機能しません。- ベースの
.plainファイルでスクリプトをリソースとしてリンクし忘れないでください。config.yamlからのみ参照されているスクリプトは、仕様の契約からは見えません。レンダラーはprepare-environment-script:をビルドシステムへのポインターとして扱い、モデルが参照する test-req 契約の一部とはみなしません。add-resourceスキルを使って、conformance/prepare の test reqs を管理する.plainモジュールからスクリプトへの Markdown リンクを追加してください(ワークフローの手順 8 を参照)。リンクされたリソースを省略すると、モデル作成者やレビュー担当者は、準拠すべき実際の環境準備契約を確認せずに conformance テストコードを作成・レビューすることになり、conformance が有効化すべき分離パスを適切に判断できません。 - 簡潔すぎるスクリプトを書かないでください。 手順ごとの説明がないこと、インストール/事前ビルドの出力を
>/dev/nullにリダイレクトすること、--versionの出力がないこと、失敗時のバナーがないこと、最後の要約行がないことは、スクリプトのデバッグを準備対象のコードより難しくします。規約の ログ出力 — できるだけ詳細にする ルールに文字どおり従ってください。各手順を明示し、各コマンドを出力し、失敗時には実行しようとしていた内容と場所を出力します。また、最後の要約では言語、作業フォルダー、分離ルート、所要時間を明記します。