ローカル開発環境でrbenv等を使っていると.ruby-versionで自動的にバージョンを判断してくれて便利ですが、VS Code Devcontainerで独自のDockerfileを採用している際に.ruby-versionを元にDockerfileのFROMで指定するイメージをコントロールする方法にちょっと迷って、色々試して一旦落ち着いたのでメモ📝
基本方針
Dockerfileのbuild時のargsとして.ruby-versionのcat結果を元にしたRubyのバージョンを指定してFROMに指定したRubyのバージョンをを切り替える方針とした。
.devcontainer/Dockerfile
+ ARG RUBY_VERSION=3
FROM ruby:$RUBY_VERSION AS base
rails/devcontainerのruby fearuresを使うとrbenvをinstallするのでよしなにバージョンを切り替えてくれそうだが、RC版等のRubyのバージョンも使いたいので今回はbuild argsに引き渡す方針とした。
VS Code Dev Containerのビルド時に.ruby-versionの値をargsに指定する方法
まずDocker composeで開発環境を管理しているので、compose.ymlからビルド時に渡すargsにRUBY_VERSIONを追加します。
app: &app
build:
context: .
+ args:
+ RUBY_VERSION:
※直接devcontainer.jsonでbuild argsを定義できれば良かったのですが、これはサポートされてない😢
以下の通りbuild-argsは、値なしの場合にローカル環境の環境変数の値が設定されるのでビルド時に環境変数RUBY_VERSIONにcat .ruby-versionの値をセットできれば実現できます。
値のない --build-arg フラグも使うこともでき、その場合、ローカル環境の値が、構築時の Docker コンテナ内に継承されます。 docker build — Docker-docs-ja 24.0 ドキュメント
devcontainerでのビルド時にはローカルの環境変数を参照することはできないようですが、.envファイルを使えばローカルの環境変数を共有することができます。
Option 2: Use an env file If you have a large number of environment variables that you need to set, you can use a .env file instead. Container environment variables
docker composeはデフォルトではルートの.envを読み込んでくれます。
v1.28 以上は、 .env ファイルはプロジェクトがあるディレクトリのベースにあります。 プロジェクト ディレクトリは、 --file オプションや COMPOSE_FILE 環境変数の値で明示できます。 Compose 内の環境変数 — Docker-docs-ja 24.0 ドキュメント
以下のようなスクリプトを用意して、.env内のRUBY_VERSIONを.ruby-versionの値を差し替えます。
※.envファイルでは動的な環境変数の指定ができなさそうだった。
bin/update_dotenv_language_version.sh
#!/bin/bash # .envファイルのパス ENV_FILE=".env" # ruby-versionファイルから値を取得 RUBY_VERSION=$(cat .ruby-version) # 一時ファイルを作成 TEMP_FILE=$(mktemp) # .envファイルを読み込み、値を置換して一時ファイルに書き込む while IFS= read -r line; do case "$line" in export\ RUBY_VERSION=*) echo "export RUBY_VERSION=\"$RUBY_VERSION\"" ;; *) echo "$line" ;; esac done < "$ENV_FILE" > "$TEMP_FILE" # 一時ファイルを元の.envファイルに置き換える mv "$TEMP_FILE" "$ENV_FILE" echo "=== Updated language versions in .env file ===" echo "Ruby: $RUBY_VERSION"
.ruby-versionを変更した際には.envの更新が必要という一手間発生してしまいますが、上記スクリプトを実行すると.envの値が.ruby-versionを元に書き換わり、それをcompose.yamlが読み取ってビルドしてくれるので、.ruby-version元にVS Code Dev Containerで使用するDocker ImageのRubyバージョンも管理することができるようになりました🎉
compose.ymlの配置場所を.devcontainer/compose.yml等にしている場合には、VS Code Dev Containerではbuild時に--env-fileが指定できずデフォルトのプロジェクトパスからしか渡せないのでCOMPSE_FILES等の指定パスに.envファイルをコピーしたりする必要があるので注意です。