GitHub Codespaces の使い方を調べる

まずはドキュメント

コスト管理

codespace のランタイムとツールをカスタマイズするには、リポジトリ用に 1 つ以上の開発コンテナー構成を作成できます。
開発コンテナー構成が無い場合は規定のコンテナイメージが利用される。

リポジトリ用に特別に構成された開発環境で作業できます。 そのプロジェクトで作業するために必要なすべてのツール、言語、構成が揃っています。
いわゆる「おま環」問題が無くなる
VS Code の codespace で作業する場合、Live Share を使ってチームの他のユーザーと共同作業できます。 「codespace での共同作業」を参照してください。
LiveShare でペアプロもできる
codespace からポートを転送し、URL を共有して、自分がアプリケーションで行った変更を pull request で送信する前に、チームメイトがそれらの変更を試すことができるようにします。
マージ・デプロイ通さなくてもアプリケーションの動作をチームメンバーと共有できる

コードスペースの作成を高速化するために、リポジトリ管理者は GitHub Codespaces prebuilds を有効にすることができます。
プレビルドを活用すると Codespace の作成が高速化できる
実行中のコードスペースにのみCPUコストが発生します。 停止したコードスペースには、ストレージ・コストのみが発生します。
停止中でもストレージにコストがかかるのは注意

開発コンテナの構成をカスタマイズする
リポジトリ用に単一の開発コンテナー構成を定義したり、異なるブランチ用に異なる構成を定義したり、複数の構成を定義したりできます。複数の構成を利用できる場合、ユーザーは codespace を作成するときに好みの構成を選択できます。 ... (略) ... 構成の選択肢を作成することで、異なるチームが、実行する作業用に適切に設定された codespace で作業できるようになります。
求めてたものかもしれない
リンターのようなものは標準化して、すべてのユーザーにインストールするよう求めることが適切なので、devcontainer.json ファイルに含めることができます。 ユーザー インターフェイスのデコレーターやテーマなどは、devcontainer.json ファイルに含めるべきでない個人的な選択肢です。
メンバーで共有する設定を devcontainer.json に入れて、個人的な設定は入れない。
devcontainer.json ファイルは JSONC (コメント付きの JSON) 形式を使用して記述されます。 これにより、構成ファイル内にコメントを含めることができます。
JSONC めずらしい

Development Containers の仕様
devcontainer.json (Dev Container metadata) のリファレンス
Dev Container は開発向けの機能が含まれた開発環境としてのコンテナ
引用元: https://containers.dev/overview


たとえば、[GitHub Codespaces で開く] バッジへのリンクを追加できます。

devcontainer.json
を書いてみる
スキーマ定義
Reference

ローカルで Dev Container を試す
Codespaces を触る前に、まずローカル(VS Code)の Dev Container の動作を確認してみる

Remote-Containers: Reopen in Container
が何を指しているか...

Open Folder in Container..
を選択して、先に作成したフォルダを指定すると .devcontainer/devcontainer.json
を読み込んでコンテナが立ち上がった
ちょっとビルドに時間かかる

ビルド失敗
[2025-05-26T22:37:34.707Z] Dockerfile-with-features:19
--------------------
17 | RUN yarn install
18 |
19 | >>> COPY ../Gemfile ../Gemfile.lock ./
20 | RUN bundle install --path 'vendor/bundle'
21 |
--------------------
ERROR: failed to solve: failed to compute cache key: failed to calculate checksum of ref e6d490eb-a9e3-419c-9add-b6e519e01e33::k126hrc6sq7su66czzr2pv632: "/Gemfile.lock": not found
Gemfile.lock
が無い。あと yarn.lock
も
devcontainer の外でターミナル開いて以下を実行して Gemfile.lock
, yarn.lock
を作成する
bundle install
yarn
修正後に立ち上げ直すのは Reopen Container...
(スクショ取り忘れた)

数分後、devcontainer 環境が立ち上がった。

ssh 設定
1Password で SSH キーを管理している場合、設定が特殊かも

git コマンドで 1Password の SSH キーを使う
まず、macOS 側の環境変数 SSH_AUTH_SOCK
を設定するため ~/.bash_profile
を編集
export SSH_AUTH_SOCK=~/Library/Group\ Containers/2BUA8C4S2C.com.1password/t/agent.sock
OS再起動して、まずは以下のとおり環境変数が有効であることを確認
$ env | grep SSH
SSH_AUTH_SOCK=/Users/foo/Library/Group Containers/2BUA8C4S2C.com.1password/t/agent.sock
$ ssh -T git@github.com
Hi snaka! You've successfully authenticated, but GitHub does not provide shell access.
この状態で、devcontainer.json
を編集
{
"name": "Ruby",
"build": {
"dockerfile": "Dockerfile",
"context": ".."
},
"workspaceFolder": "/workspace",
"containerEnv": {
// 【追加】dev container で 1Password の ssh キーを利用する設定
"SSH_AUTH_SOCK": "/run/host-services/ssh-auth.sock"
},
"mounts": [
"source=${localWorkspaceFolder},target=/workspace,type=bind",
"source=${localWorkspaceFolderBasename}_node_modules,target=/workspace/node_modules,type=volume",
"source=${localWorkspaceFolderBasename}_bundle,target=/workspace/vendor/bundle,type=volume",
// 【追加】dev container で 1Password の ssh キーを利用する設定
"source=/run/host-services/ssh-auth.sock,target=/run/host-services/ssh-auth.sock,type=bind"
]
}
コンテナ内のターミナルで 以下を実行してアクセスできるのを確認
$ ssh -T git@github.com
Hi snaka! You've successfully authenticated, but GitHub does not provide shell access.
上記のように変更したあと、コマンドパレットから Dev Container: Rebuild Container
を実行してコンテナをビルドし直し (コンテナ入り直すだけでよかったかも?)

ruby-lsp のインストールは失敗した
root@63875b763180:/workspace# bundle add ruby-lsp --group "development" --require false
[!] There was an error parsing `injected gems`: You cannot specify the same gem twice with different version requirements.
You specified: ruby-lsp (~> 0.23.23) and ruby-lsp (>= 0). Gem already added. Bundler cannot continue.
# from injected gems:1
# -------------------------------------------
> gem "ruby-lsp", ">= 0", :group => :development, :require => false
# -------------------------------------------
よくわからないので一旦スルー

既存の Docker 開発環境を Codespace で稼働させてみる
普段、開発に利用している Dockerfile が Project Root に存在するとして、以下のように devcontainer.json を設定する。
- Dockerfile は project root から見て
docker/Dockerfile
という path に配置されている
{
"name": "This is devcontainer name",
"build": {
"dockerfile": "../docker/Dockerfile",
"context": "../docker"
}
}
この修正を適当な Topic ブランチ立てて、GitHub に push する。
push したら Pull Request を作って、そこから Codespace を立ち上げる。
初回はビルドに数分かかる

立ち上げ失敗してた
This codespace is currently running in recovery mode due to a container error.
1. Use Cmd/Ctrl + Shift + P -> "Codespaces: View Creation Log" to see full logs
2. Update your devcontainer configuration as needed
3. Use Cmd/Ctrl + Shift + P -> "Codespaces: Rebuild Container" to retry
4. For help, read more about custom configuration: https://aka.ms/ghcs-custom-configuration

devcontainer.json の内容が最新の状態になっていないことが判明したので Command Palette から Codespaces: Rebuild Container
を実行

勘違い... Codespace 立ち上げた元のブランチの変更に自動的に追随するわけじゃないのか....
明示的に git pull
する必要がある。そして初期の clone は shallow なので git pull
の段階で多くの履歴が落ちてくる。
git pull
が完了すると Rebuild を促される
無事に Rebuild 完了したら Codespace 環境が使えるようになった。

🟩プライベートな Gem のリポジトリへのアクセスに失敗
bundle install
で失敗。該当プロジェクトは private な gem のリポジトリにアクセスしているため、そこが失敗しているっぽい
Retrying `git clone --bare --no-hardlinks --quiet -- git@github.com:foo-org/bar ...
If this error persists you could try removing the cache directory '/workspaces/baz'
git@github.com: Permission denied (publickey).
fatal: Could not read from remote repository.
Please make sure you have the correct access rights
and the repository exists.

追加または変更したカスタム アクセス許可は、変更がリポジトリにコミットされた後に作成された新しい codespace にのみ適用されます。 codespace 内からアクセス許可を追加または変更した場合、その codespace を再構築しても、これらのアクセス許可は現在のコードスペースには適用されません。
codespace を新しく作る必要があるのか

codespace 作り直したが解消しなかった
codespace の GITHUB_TOKEN は、codespace を作成したリポジトリに対する読み取りおよび書き込みアクセス権を持つように構成されます。 既定では、このトークンは他のリポジトリにアクセスできません。 リポジトリを複製できない場合があります。または、複製したリポジトリにプッシュできません。
これが関係してるかも?

private リポジトリへの追加のアクセス権の要求を devcontainer.json に追記した
{
"name": "This is devcontainer name",
"build": {
"dockerfile": "../docker/Dockerfile",
"context": "../docker"
},
"customizations": {
"codespaces": {
"repositories": {
"my-organization/private-repo": {
"permissions": {
"contents": "read"
}
},
// 他の private リポジトリも同様に記述する
}
}
}
}
最初 typo してて設定が反映されてなかったが、 typo に気づいて修正したらアクセス許可を求めてくれるようになった。

設定したがダメだった... おそらく Gemfile に記述している private リポジトリへのアクセスが ssh ( git@github.com:my-organization/private-repo
形式 ) であるため?
関連:

あってた
環境変数 BUNDLE_GITHUB__COM
に以下のようにトークンを設定
export BUNDLE_GITHUB__COM="x-access-token:${GITHUB_TOKEN}"
さらに、 Gemfile に記述している private gem 用のコードの書き換えで、 bundle install できるようになった。

これまでの内容を参考に
devcontainer.json に環境変数の設定を追加した
"remoteEnv": {
"BUNDLE_GITHUB__COM": "x-access-token:${localEnv:GITHUB_TOKEN}"
},

とりあえず、ここまでの環境は立ち上がった。

既存の docker-compoer.yml を利用する
ローカル端末での開発では、 docker compose
コマンドで環境を用意していて、そのための docker-compose.yml を用意していた。 docker-compose.yml では MySQL や Redis , Sidekiq などのアプリを動作させるために必要なサービスを立ち上げるようになっている。
それをそのまま codespace で使えると楽そうと考えた。
Dockerfile で立ち上げに成功したものを docker-compose.yml で立ち上げるように変更
diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
index ed60007330..6872ff53b2 100644
--- a/.devcontainer/devcontainer.json
+++ b/.devcontainer/devcontainer.json
@@ -1,9 +1,6 @@
{
"name": "This is devcontainer name",
- "build": {
- "dockerfile": "../docker/Dockerfile.development",
- "context": "../docker"
- },
+ "dockerComposeFile": "../docker-compose.yml",
+ "service": "app",
"remoteEnv": {
"BUNDLE_GITHUB__COM": "x-access-token:${localEnv:GITHUB_TOKEN}"
},

codespace 起動に失敗した...
[31203 ms] Stop: Run: docker compose --project-name ...(snip)... up -d
Outcome: success User: root WorkspaceFolder: /workspaces/campfire-2
devcontainer process exited with exit code 0
Container creation failed.
Creating recovery container.
Creating container...
Container creation failed
としか出てこなくて原因がわからない...

rubygems.org のリポジトリを参考にしてみる

あれ?見逃してたか?
Error response from daemon: failed to mkdir /var/lib/docker/volumes/campfire-2_bundle-data-volume/_data/bin: mkdir /var/lib/docker/volumes/campfire-2_bundle-data-volume/_data/bin: file exists
と思って Codespace 作り直してみたが、このエラー自体は解消しても Container creation failed
は解消してなかった。

(一旦この作業は中断)

時間帯的に被ってるので Codespaces のメンテナンスが原因だった可能性はあり...

rubygems.org を Codespace で立ち上げてみる
参考のために、既存のリポジトリで Dev Container の設定がある、かつ、 Rails を利用している物を探す。
path:devcontainer.json dockerComposeFile rails
で検索してみたら rubygems.org がヒットした
このリポジトリで devcontainer.json の書き方を参考にしてみる

devcontainer.json
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
{
"name": "RubyGems.org",
"dockerComposeFile": [
"docker-compose.yml",
"../docker-compose.yml"
],
"service": "rails-app",
"runServices": [
"db",
"cache",
"search",
"toxiproxy",
"selenium"
],
"forwardPorts": [
3000, // Rails
11211, // Memcache
9200, // Opensearch
5432 // PostgreSQL
],
// .... 略
}
-
docker-compose.yml
と../docker-compose.yml
の2つのファイルを使ってサービスを立ち上げている -
runServices
とあるので、docker-compose.yml
に記述されている service は立ち上がっているんだろう -
forwardPorts
でそれらの service の port がローカル端末に forward されていそう


DB にクライアントからつないでみる
portForward されているのでローカル端末から
psql -h localhost -U postgres
見えている
しかし codespace のターミナルから見えないのはなぜだろう...
codespace で起動している service の管理ってどこからできるんだろう?
ローカルで docker compose している状況だと、ホスト側で docker ps
とか Docker Desktop などでコンテナの状態を確認できたけど...
Codespces に接続している VS Code はそのコンテナの1つに接続しているので、ホスト側へのアクセス手段が無い...?

と思ったら、ローカルでの docker compose の状態を思い出したらできた
psql -h db -U postgres
コンテナの中では docker-compose.yml で定義された service の名前がそれぞれ host 名として認識されている。よって上のように -h db
とすることで接続できた

データベースを rubygems_development
に切り替える
\c rubygems_development
テーブルの一覧を見る
\d

同じ疑問
なるほど docker-outside-docker
という feature があるのか、名前からすると docker コンテナ野中から外(ホスト側)の docker を利用する、みたいな感じか?
"features": {
"ghcr.io/devcontainers/features/docker-outside-of-docker": {}
}
features のリポジトリ

devcontainer.json に修正を加えて、Rebuild して codespace を起動し直し。
"features": {
"ghcr.io/devcontainers/features/github-cli:1": {},
// "ghcr.io/rails/devcontainer/features/activestorage": {},
"ghcr.io/rails/devcontainer/features/postgres-client": {},
"ghcr.io/devcontainers-extra/features/apt-packages:1": {
"packages": [
"pkg-config"
]
},
// ↓この行を追加
"ghcr.io/devcontainers/features/docker-outside-of-docker": {}
},
docker ps
コマンドが使えるようになった

ほか参考

なんとなく Codespace での開発スタイルが見えてきた

既存の docker-compose.yml ベースで環境を構築してみる
既存の docker-compoer.yml を利用する のつづき
- 既存の docker-compose.yml の内容を参考にしつつ、必要最小限の要素から始めて、徐々に service を追加していく方法で環境を整備してみる

(うまくいっていない、一旦中断)

docker-in-docker で構築してみる
作業用のコンテナは標準的な環境 + docker-in-docker の環境 ( つまり docker が利用できる標準的 Linux 環境 ) を用意して、ローカルで docker を利用するのと同等の環境を再現してみる。
Codespaces の標準イメージは以下

どうやら docker-in-docker は不要そう。デフォルトのイメージに入っていた。
👋 Welcome to Codespaces! You are on our default image.
- It includes runtimes and tools for Python, Node.js, Docker, and more. See the full list here: https://aka.ms/ghcs-default-image
- Want to use a custom image instead? Learn more here: https://aka.ms/configure-codespace
🔍 To explore VS Code to its fullest, search using the Command Palette (Cmd/Ctrl + Shift + P or F1).
📝 Edit away, run your app as usual, and we'll automatically make it available for you to access.

📝 docker compose
でコンテナに GITHUB_TOKEN 渡すために -e BUNDLE_GITHUB__COM
する必要があった

既存の Dockerfile, docker-compose.yml などを活かせるという点で、こちらの方法が楽ではあった。
意識する仮想環境の階層が深くなるのがあまり好きではないが....

理想はこれだったが、これだと codespace という Docker 環境向けの構成をローカル用とは別で管理することになりそう...(知らないだけでうまいやり方はありそう)

prebuild の活用方法について考える

立ち上げたコンテナ内でさらに別リポジトリ(Frontend)を clone したい
詳細は省略するが、およその手順は以下のとおり
- devcontainer.json の
customizations.codespaces.repositories
に対象リポジトリを追加する -
.local
などの git 管理対象外のディレクトリに移動
a..local
のようなディレクトリがなければ、それを.gitignore
に追加 & ディレクトリ作成 - 対象リポジトリを GitHub CLI で clone (認証情報とかGITHUB_TOKENでよしなにやってくれる) する
- 対象を立ち上げる

prebuild で待ち時間を短縮したい
Organization が所有するリポジトリの場合、GitHub Codespaces のリポジトリ レベルの設定は、GitHub Team および GitHub Enterprise プランの Organization で使用できます。 設定にアクセスするには、Organization またはその親 Enterprise が支払い方法を追加し、GitHub Codespaces の使用制限を設定する必要があります。
チームで使う場合は Organization 設定が必要そう
プレビルドの構成ワークフローが実行されると、GitHub により一時的な codespace が作成され、devcontainer.json ファイル内の任意の onCreateCommand および updateContentCommand コマンドまでの設定操作が実行されます。 プレビルドの作成時に postCreateCommand コマンドは実行されません。
onCreateCommand
, updateContentCommand
があるとよさそう

onCreateCommand を用意する
prebuidl する・しないにかかわらずあると良さそうなので用意しておく。