コードを書いてプッシュ、GitHub Actions の CI が回るのをコーヒーを飲みながら待つ。数分後に赤くなって、「あ、RuboCop 引っかかってた」と直して再プッシュ……。この「CI 待ち → 赤 → 出直し」のループ、地味に時間を溶かしていませんか?
Rails 8.1 で追加された Local CI(bin/ci)は、まさにこの悩みへの答えです。テスト・Lint・セキュリティチェックといった「CI で回している一式」を、プッシュ前に手元でまとめて実行できます。この記事では bin/ci と config/ci.rb の使い方を、完成イメージ → 設定の中身 → 既存プロジェクトへの導入 → GitHub Actions との併用、の順で見ていきます。
まず結論:bin/ci 一発で「CI と同じチェック」が手元で回る
細かい説明は後回しにして、先に完成形を見てもらいます。Rails 8.1 のアプリで bin/ci を実行すると、こんな出力が出ます。
$ bin/ci
✔ Setup
✔ Style: Ruby
✔ Security: Gem audit
✔ Security: Brakeman code analysis
✔ Tests: Rails
All checks passed 🎉この「何をチェックするか」を決めているのが config/ci.rb というファイルです。つまり、config/ci.rb が「CI が通る=何が OK か」の定義そのもので、それをローカルでもクラウドでも同じように走らせられる、というのが Local CI の考え方です。

GitHub Actions の結果を待たずに、プッシュ前に同じチェックを手元で潰せるので、「CI が赤くなって出直し」の往復が減ります。レビュー依頼の前に自分でゲートをくぐれる、と言い換えてもいいでしょう。
Local CI とは(bin/ci と config/ci.rb)
Rails 8.1 の Local CI は、次の2つがセットになった仕組みです。
config/ci.rb:実行するチェック(ステップ)を並べる定義ファイル。CI の「正」はここ1か所に集約されるbin/ci:config/ci.rbを読んで各ステップを順に実行するランナー
bin/ci は実行時に ENV["CI"] を true にセットして走ります。これにより、eager loading(アプリ起動時に全クラスをまとめて読み込むこと。読み込み漏れのエラーを早期に炙り出せる)が有効になるなど「CI 相当の環境」でチェックが動きます。手元の development とは少し違う条件で回るので、CI サーバーでだけ落ちる、といった食い違いも見つけやすくなります。
背景には「開発マシンが速くなり、小〜中規模のアプリならローカルでテスト一式を数分で回しきれる」という状況の変化があります。Rails 8.1 は、この現実に合わせて Local CI を標準機能として用意した、というわけです。
config/ci.rb の中身を読む
Rails 8.1 で新規アプリを作ると、config/ci.rb はだいたい次のような内容で生成されます。読みやすいように少し省略していますが、雰囲気はこのままです。
# config/ci.rb
CI.run do
step "Setup", "bin/setup --skip-server"
step "Style: Ruby", "bin/rubocop"
step "Security: Gem audit", "bin/bundler-audit"
step "Security: Importmap vulnerability audit", "bin/importmap audit"
step "Security: Brakeman code analysis", "bin/brakeman --quiet --no-pager --exit-on-warn --exit-on-error"
step "Tests: Rails", "bin/rails test"
step "Tests: Seeds", "env RAILS_ENV=test bin/rails db:seed:replant"
endやっていることは、見た目のとおりシンプルです。CI.run do ... end の中に step を並べているだけ。各ステップが何をチェックしているかを表にすると、こうなります。
| ステップ | 実行コマンド | 役割 |
|---|---|---|
| Setup | bin/setup --skip-server | 依存関係のインストール・DB準備(サーバーは起動しない) |
| Style: Ruby | bin/rubocop | コードスタイル(Lint)のチェック |
| Security: Gem audit | bin/bundler-audit | gem に既知の脆弱性がないか |
| Security: Importmap | bin/importmap audit | importmap 経由の JS に脆弱性がないか |
| Security: Brakeman | bin/brakeman ... | Rails 特有のセキュリティ問題を静的解析 |
| Tests: Rails | bin/rails test | テストスイート |
| Tests: Seeds | bin/rails db:seed:replant | seeds が壊れていないか(投入し直して確認) |
ポイントは、ステップの実体はただのシェルコマンドだということ。RuboCop も Brakeman も、普段ターミナルで打つコマンドをそのまま並べているだけです。だから「うちのプロジェクト独自のチェックを足したい」というのも、step を1行追加するだけで済みます。
ここで出てくる書き方(DSL)は、覚えることが少ないのも魅力です。よく使うものは次の5つだけ。
| 書き方 | 役割 |
|---|---|
CI.run do ... end | CI 全体の定義ブロック。この中にステップを並べる |
step "名前", "コマンド" | 1つのチェックを追加する(実体はシェルコマンド) |
group "名前", parallel: N do | 中のステップを最大 N 個まで並列実行する |
success? | ここまでの全ステップが通ったかを判定する |
failure "見出し", "説明" | 失敗時にメッセージを出す |
これだけ押さえれば、あとは step を足したり並列にしたりの組み合わせです。順に見ていきましょう。
step の書き方
step の引数は「表示名」+「コマンドと引数」です。第1引数がログに出るステップ名、残りが実行するコマンドになります。
# 1つの文字列でまとめて渡す
step "Tests: Rails", "bin/rails test"
# コマンドと引数を分けて渡す(シェルエスケープされる)
step "Tests: All", "bin/rails", "test", "test:system"引数を分けて渡すと、スペースを含む値なども安全にエスケープしてくれます。動くコマンドでありさえすれば何でも登録できるので、たとえば step "Types", "bin/rails runner ..." のように、自作のチェックスクリプトを差し込むこともできます。
並列実行で速くする(group)
ステップは上から順に実行されますが、お互いに独立したチェックは並列で走らせて時短できます。group に parallel: を渡すと、その中のステップを同時に実行します。
CI.run do
step "Setup", "bin/setup --skip-server"
group "Checks", parallel: 2 do
step "Style: Ruby", "bin/rubocop"
step "Security: Brakeman", "bin/brakeman --quiet"
step "Tests: Rails", "bin/rails test"
end
endRuboCop・Brakeman・テストは互いに影響しないので、まとめて並列にすると全体時間が縮みます。出力は宣言した順に整理して表示されるので、並列にしてもログがぐちゃぐちゃに混ざることはありません。Rails 8.1 の新規アプリのデフォルトでも、チェック系はこの group でまとめられています。
成功・失敗で処理を分ける(success?)
全ステップが通ったかどうかは success? で判定できます。これを使うと、「全部緑なら承認(signoff)まで済ませる」「1つでも落ちたら分かりやすくメッセージを出す」といった分岐が書けます。
CI.run do
# ... 各ステップ ...
if success?
step "Signoff", "gh signoff"
else
failure "CI failed", "直してから再実行してください"
end
endデフォルトの config/ci.rb にも、この success? を使って「全チェック通過なら gh signoff でコミットにOKを付ける」例がコメントで入っています(gh CLI の拡張が必要なので、最初はコメントアウトされています)。まずはここまで踏み込まなくても、テストと Lint を並べるだけで十分に役立ちます。
既存プロジェクトに bin/ci を入れる
「うちは Rails 8.0 以前から動かしてる」「8.1 に上げたけど bin/ci が無い」という場合は、アップデートタスクで取り込めます。
$ bin/rails app:update差分を確認する対話が出るので、config/ci.rb と bin/ci を受け入れる(Y を選ぶ)と一式が入ります。既存の設定ファイルまで上書きしてしまわないよう、差分は1つずつ中身を見てから受け入れるのが安全です。
取り込んだあとは、生成された config/ci.rb のうち自分のプロジェクトに無いステップ(bin/rubocop を入れていないなら Style ステップ、など)を削るだけ。逆に bin/ にコマンドが無い場合は、bundle exec rubocop のように置き換えれば動きます。まずはテスト1本から始めて、慣れてきたら Lint・セキュリティチェックを足していく形でも十分です。
ちなみに、Rails 8 系で Solid Queue などを使って複数 DB 構成にしているなら、bin/setup や db:seed:replant が正しく動くよう database.yml の書き方もあわせて確認しておくと、CI のステップでつまずきにくくなります。
GitHub Actions は捨てなくていい(使い分けの考え方)
ここまで読むと「じゃあ GitHub Actions は要らない?」と思うかもしれませんが、答えは「捨てずに使い分ける」です。Local CI とクラウド CI は、役割が少し違います。
- ローカルの
bin/ci:プッシュ前に手元で回して、明らかな失敗を先に潰す。フィードバックが速い - GitHub Actions などのクラウド CI:クリーンな環境・複数の Ruby/DB バージョン・チーム全員のプッシュに対する最終ゲート
大事なのは、クラウド CI 側も config/ci.rb を「正」として同じチェックを走らせること。そうすれば「手元では通ったのに CI で落ちた(設定がズレていた)」が起きにくくなります。

GitHub Actions の設定は、極端にはこれだけまで縮められます。
# .github/workflows/ci.yml
name: CI
on: [push, pull_request]
jobs:
ci:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ruby/setup-ruby@v1
with:
bundler-cache: true
- run: bin/ciワークフロー側に個別のステップをズラズラ書くのではなく、bin/ci を呼ぶだけ。チェックの内容を変えたいときは config/ci.rb を1か所直せば、ローカルとクラウドの両方に効きます。「CI の定義が YAML とローカルで二重管理」から解放されるのが、地味ながら大きなメリットです。
ハマりポイント
bin/ciが無い:8.1 に上げただけでは入らないことがある。bin/rails app:updateでconfig/ci.rbとbin/ciを受け入れる- ステップのコマンドが実在しない:
config/ci.rbはbin/rubocopのような bin スタブ前提。無ければbundle exec ...に書き換えるか、そのステップを消す - ローカルだけ・CI だけで落ちる:
bin/ciはCI=trueで走るので eager loading などが有効。development で気づけなかった読み込みエラーがここで出ることがある(=それを早く見つけられる、とも言える)
まとめ
Rails 8.1 の Local CI(bin/ci / config/ci.rb)は、「CI で回している一式を、プッシュ前に手元で一発実行する」ためのシンプルな仕組みです。ポイントを整理します。
config/ci.rbにstepを並べ、bin/ciで実行する。CI の「正」が1か所に集約される- ステップの実体はただのシェルコマンド。テスト・Lint・セキュリティチェックを自由に足せる
- 独立したチェックは
group ..., parallel: 2で並列にして時短できる - GitHub Actions は捨てずに、同じ
config/ci.rbを走らせる最終ゲートとして併用する
まずはテスト1本から config/ci.rb に書いて、bin/ci を叩いてみてください。「プッシュ前に自分でゲートをくぐる」習慣がつくと、レビューも CI も驚くほどスムーズになります。
ローカルで CI が緑になったら、次はデプロイです。Rails 8 × Kamal 2 で AWS にデプロイする手順で、テストを通したコードを本番へ届けるところまでつなげてみてください。CI のステップが赤くなって原因が分からないときは、エンジニアの「調べ方」の記事も役に立つはずです。
スポンサーリンク
