🐫

Rails アプリケーションを Github Codespaces で動かすには

に公開

この記事では、 Rails アプリケーションを Github Codespaces で動作させることをトライしている内容です。
現時点では少しつまずくポイントがありますが、これをアウトプットすることでこれから試す方がつまずくポイントが減れば良いなと思っています。

結論を先に

  • Rails アプリケーションを Github Codespaces で動かすには --devcontainer オプションをつけた rails new の構成でおおよそ動く。が、いくつか注意点がある。
  • codespace でなくても必要な一般的な注意点
    • localhost ではないドメイン名でのアクセスになるので、 config.hosts の調整を要する
  • codespace 独自の問題 (2025/04/30 時点)
    • POST リクエストが処理できない
      • HTTP Origin ヘッダによるサイトのオリジンチェックにひっかかるので config.action_controller.forgery_protection_origin_checkfalse に指定する必要がある
    • 初期状態で GitHub Codespaces のポートフォワーディングが効かない
      • 以下いずれかのワークアラウンドを要する
        1. ポートの公開範囲を、初期値の Private から ⇒ Public ⇒ Private と動かすとなぜか Private アクセス (自分だけアクセス可) が機能する
        2. docker image を Rails 提供のものから microsoft 提供のものに差し替える
        3. .devcontainer/devcontainer.json の設定、 forwardPorts を無効化する

GitHub Codespaces とは

github が提供しているクラウドベースの開発環境です。
https://github.com/features/codespaces

ブラウザひとつで開発を始められる環境です。
ブラウザ版の VS Code (github.dev) は基本的にエディタだけであり、実際にアプリケーションを動作させる環境が揃うわけではありません。一方 Github Codespaces はエディタだけではなくアプリケーションを動作させる環境がちゃんと動きます。

GitHub のユーザに対し、 15 GB/月 ストレージ および 120コア時間の無料枠が用意されています。また、 GitHub Pro ユーザの場合は 20 GB/月 ストレージ および 180コア時間です。
また、codespace は30分放置でアイドルに、30日放置で削除されます。この時間は設定 https://github.com/settings/codespaces で変更可能です。

https://docs.github.com/ja/billing/managing-billing-for-your-products/managing-billing-for-github-codespaces

Rails アプリケーションを Github Codespaces で動かす

--devcontainer オプション

Rails アプリケーションを新しく用意するためのコマンド rails new には --devcontainer というオプションがあります。
このオプションをつけて実行すると、 .devcontainer/ ディレクトリのもとにいくつかのファイルが作成されています。

$ rails new sample --devcontainer
$ cd sample/
$ ls .devcontainer/
compose.yaml  devcontainer.json  Dockerfile

これらが作成されることにより、 VS Code の dev container で開発環境を開くことができるようになります。
ここでは詳細は省略しますが、 GitHub Codespaces での起動でもこの構成を使います。

GitHub Codespaces で開いてみる

一度 rails new を実施した状態から first commit を入れて GitHub のリポジトリに push します。
その後、 https://codespaces.new/OWNER/REPO の URL にアクセスすることで Github Codespaces での起動を確認する画面が表示されます

また、余談ですが README.md に以下のような記述を入れることでリンクを配置することもできます。

[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/OWNER/REPO)

以下のような表示になります。

Open in GitHub Codespaces

codespace 上での操作

起動してしばらく放置していると、 bin/setup --skip-server がコンソール上で実行されます。
これは、 --devcontainer オプションをつけて rails new したことで作成されるファイルを見るとこの設定の記述が確認できると思います。

.devcontainer/devcontainer.json
  // Use 'postCreateCommand' to run commands after the container is created.
  "postCreateCommand": "bin/setup --skip-server"

setup を終えたら、コンソールから rails s で起動できます。

rails s

。。。 が、実はまだアクセスできません。もう少し続きます。

codespace でのポート転送の設定についての課題

GitHub Codespaces には、 codespace の中で動かす Rails サーバの http://localhost:3000 に対して外部からアクセス可能にするポートフォワーディングの仕組みがあります。

https://docs.github.com/ja/codespaces/developing-in-a-codespace/forwarding-ports-in-your-codespace

既定で、ポートの表示範囲に Private が設定されているのですがこの場合は自身のアカウントで GitHub にログイン状態であれば直接ブラウザからアクセスできます。
。。。が、現時点では初期状態からなんらかの変更をしないとなぜかアクセスできません。
既定が Private なので Private を維持した状態でアクセスするためには Private -> Public -> Private と変更する必要があります。

手動トグル以外の回避策

上記のとおり、ポートの公開範囲を Private -> Public -> Private といった変更をすることで本来の公開範囲 Private として動きます。
この操作で困らない場合はそれで良いですが、起動したらすぐに公開範囲が正しく作用して欲しい場合は以下の方法があります。

(しかし、これらは rails new してすぐに codespace で開発を始められるかという観点からあまり好ましくはないなと考えているので今後改善されれば良いなと思います。)

Docker イメージを差し替え

rails new --devcontainer オプションを実行することで生成される devcontainer に関する設定ですが、そこで以下の記述があります。

.devcontainer/Dockerfile
# Make sure RUBY_VERSION matches the Ruby version in .ruby-version
ARG RUBY_VERSION=3.4.2
FROM ghcr.io/rails/devcontainer/images/ruby:$RUBY_VERSION

Docker イメージとして https://github.com/rails/devcontainerghcr.io/rails/devcontainer/images/ruby が使われていますが、これを microsoft が提供している ruby の Docker イメージ mcr.microsoft.com/devcontainers/ruby と差し替えます。

.devcontainer/Dockerfile
 # Make sure RUBY_VERSION matches the Ruby version in .ruby-version
 ARG RUBY_VERSION=3.4.2
+FROM ghcr.io/rails/devcontainer/images/ruby:$RUBY_VERSION
-FROM mcr.microsoft.com/devcontainers/ruby:3.4

すると、本件のポート転送に関する問題は回避でき、はじめから Private のポート転送の設定が効いた状態で始められます。

forwardPorts を削除

--devcontainer オプションを付けて rails new した .devcontainer/devcontainer.json に、以下の記述が存在しています。
コンテナ内で開いているポートをホストマシン側に自動で転送するための設定項目なのですがこれを無効し、実際に 3000番ポートが使われる際の自動検出に頼るようにすれば Private のポート転送の設定が効いた状態で始められます。

.devcontainer/devcontainer.json
   // Use 'forwardPorts' to make a list of ports inside the container available locally.
-  "forwardPorts": [3000],
+  // "forwardPorts": [3000],

Blocked hosts エラーを解決する

この状態でブラウザからアクセスすると、エラーが生じます。

Blocked hosts: symmetrical-spoon-6vxqqgxx4cr6qq-3000.app.github.dev
To allow requests to these hosts, make sure they are valid hostnames (containing only numbers, letters, dashes and dots), then add the following to your environment configuration:
    config.hosts << "symmetrical-spoon-6vxqqgxx4cr6qq-3000.app.github.dev"

For more details view: the Host Authorization guide

これを解決するには、表示されているように config.hosts にホスト名を加える必要があります。
GitHub Codespaces で実行する際に設定されている環境変数を使います。

config/environments/development.rb

Rails.application.configure do
  if ENV["CODESPACES"] == "true"
    # 以下は codespace のドメインを hosts に加えています。
    # 別解として、面倒であれば config.hosts.clear してしまうという手もあります。 
    #
    codespaces_port_forwarding_domain = ENV["GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN"]
    codespace_name = ENV["CODESPACE_NAME"]
    host = "#{codespace_name}-3000.#{codespaces_port_forwarding_domain}"

    config.hosts << host
  end

  # 省略...
end

config/environments/development.rb を修正後、起動させている rails s を一度止めて再度動かし、
改めてブラウザでアクセスすると無事アクセスできます。

POST リクエストが処理できない

上記までの手順で、 GitHub Codespaces で Rails アプリケーションが動作します。
が、実はまだ GET リクエストだけしか処理できず、 POST リクエストはエラーになります。フォームを使った機能などを作るとこの問題にあたることになります。

ActionController::InvalidAuthenticityToken (HTTP Origin header (http://localhost:3000) didn't match request.base_url (https://symmetrical-spoon-6vxqqgxx4cr6qq-3000.app.github.dev)):

これは実際のリクエストの URL に対し、 Http Origin Header の内容がアンマッチゆえに生じているものです。

(ruby) request.base_url
"https://symmetrical-spoon-6vxqqgxx4cr6qq-3000.app.github.dev"
(ruby) request.origin
"http://localhost:3000"
(rdbg)

しかしながら、ブラウザからはたしかに Origin Header は現在アクセス中のドメインに基づいた値が送出されています。

これは、どうやら GitHub Codespaces がポートを外部解放するための間に存在するネットワークの中で Origin ヘッダが書き換えられていると思われます。
本件については、レポートしてみているので今後状況が変わるかもしれません。 (状況が変われば本記事もアップデートします)

https://github.com/orgs/community/discussions/156532

GitHub Codespaces での環境に限り POST リクエストが処理できるようにする

このチェックは、 Rails のアプリケーション設定値 config.action_controller.forgery_protection_origin_check により動作しています。

3.9.8 config.action_controller.forgery_protection_origin_check

CSRFの追加対策として、HTTPのOriginヘッダーがサイトのoriginと一致することをチェックすべきかどうかを設定します。

本来は既定値を可能な限り尊重したいのですが、 GitHub Codespaces で動作させる場合にこの設定値を無効化するように調整しました。

config/environments/development.rb

 Rails.application.configure do
   if ENV["CODESPACES"]
     codespaces_port_forwarding_domain = ENV["GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN"]
     codespace_name = ENV["CODESPACE_NAME"]
     host = "#{codespace_name}-3000.#{codespaces_port_forwarding_domain}"

     config.hosts << host
+
+    warn "Disabling the CSRF protection Origin header check in GitHub Codespaces"
+    config.action_controller.forgery_protection_origin_check = false
   end

これにより、 POST リクエストについても期待どおり動作するようにできました!

まとめ & 宣伝

ブラウザ上で開発環境を気軽に体験できるサンプルアプリケーションの環境として Github Codespaces は良い選択だと思います。当然そのまま実行もできます。
Rails アプリケーションを動かすためには前述のとおりいくつかの調整が必要でした。また、ポートフォワーディングの設定に気をつける点がありますがワークアラウンドもあります。

関連した宣伝ですが、 Rails で複数の ActiveRecord モデルを内包し、あたかもそれがひとつのモデルであるように振る舞うことことを提供する active_record_compose という gem を作成しています。
これを、実際に GitHub Codespaces 上で試すためのサンプルアプリケーション環境を用意しているのでよければさわってみてください。日本語 README も用意しています。

https://github.com/hamajyotan/active_record_compose
https://github.com/hamajyotan/active_record_compose-example

また、この gem そのものについては以下に記事をおいています。

https://zenn.dev/hamajyotan/articles/3e618ed8b6d22b

Discussion