はじめに
前回の記事では、devcontainer 環境に Codex CLI を導入する際にnpm の権限問題でハマった話をまとめました。
その流れで、devcontainer の初期化処理を postCreateCommand に書いていたところ、
今度はpostCreateCommand 自体が思った通りに動かないという問題に遭遇しました。
原因は、postCreateCommand を 「配列形式」 で書いていたことが原因でした。
配列形式で書いた postCreateCommand が失敗した
問題が起きていたときの postCreateCommand は、次のような書き方でした。配列形式にすれば複数コマンドを順に実行できるのでは、と考えていたためです。
{
"postCreateCommand": [
"mkdir -p ~/.npm-global",
"npm config set prefix \"$HOME/.npm-global\"",
"echo 'export PATH=\"$HOME/.npm-global/bin:$PATH\"' >> \"$HOME/.bashrc\"",
"npm install -g @openai/codex@latest"
]
}
devcontainer のビルド時、次のようなエラーが表示されました。
OCI runtime exec failed: exec failed: unable to start container process: exec: "mkdir -p ~/.npm-global": stat mkdir -p ~/.npm-global: no such file or directory
-
mkdirが存在しないはずはない - bash で実行されるものだと思っていた
この時点では、なぜ失敗するのか分からず少し混乱しました。
配列形式の postCreateCommand は execでの実行として扱われる
ここで、postCreateCommand の 書き方の違いが影響していることに気づきました。
postCreateCommand を 配列形式で書いた場合、devcontainer はそれを exec(シェルを経由せずコマンドをそのまま実行)として扱います。
そのため、
["mkdir -p ~/.npm-global"]
は、
-
mkdirに-pを渡す
のではなく、 -
mkdir -p ~/.npm-globalという 名前の実行ファイルを探しに行く
という解釈になります。
結果として、「そのようなコマンドは存在しない」というエラーになります。
正しい配列形式の書き方
配列形式を使う場合は、引数を分けて書く必要があります。
{
"postCreateCommand": ["mkdir", "-p", "/home/node/.npm-global"]
}
この形であれば、exec 形式でも正しく動作します。したがって、複数コマンドを実行させるうえで配列形式は向いていないということになります。
string形式との違い
一方で、次のように string形式で書いた場合は挙動が異なります。
{
"postCreateCommand": "mkdir -p ~/.npm-global"
}
string形式の場合、多くの環境ではシェル経由で実行されるため、
-
~の展開 -
&&による複数コマンド - 環境変数の展開
といった シェルの機能が利用できます。
ただし、string形式は以下のような理由で長い処理を書くには不向きです。
- 環境や実行タイミングによって挙動が分かりづらくなる(どのシェルで実行されるかが明示されないため)
- 1行に長いコマンドを書く必要があり可読性の低下
- クォートが多い処理の場合さらに可読性が下がる
結論:postCreateCommand はスクリプト呼び出しにする
最終的に、最も安定した方法は
postCreateCommand には処理を書かず、スクリプトを呼び出すだけにすることでした。
これにより、書き方の違いによる挙動差を意識せずに済みます。
{
"postCreateCommand": "bash ./.devcontainer/postCreate.sh"
}
処理内容はすべて postCreate.sh にまとめました。
この構成にすると以下のようなメリットがあります。
- 処理が増えても可読性を保てる
- 手動実行でデバッグしやすい
- 再構築時のトラブルが減る
まとめ
- postCreateCommand の 配列形式は execでの実行として扱われる
-
["mkdir -p ..."]のような書き方は失敗する - 配列形式を使うなら、引数を分けて書く必要がある
- string形式は便利だが、複雑な処理には向かない
- 迷ったらスクリプトに切り出すのが一番安全