Understanding CI.rb in Rails 8: Local CI Execution

When you generate a new Rails application after Rails 8.0, you’ll notice some new additions to the project structure: bin/ci, config/ci.rb, and a .github/ folder containing workflows for GitHub Actions. These files represent a significant shift in how Rails approaches continuous integration.

The Rails Philosophy: Reducing Dependencies

rails is omakase

In recent years, DHH and the Rails team have been systematically reducing external dependencies. Rails has moved toward using a single database for caching, WebSocket synchronization, and SolidQueue. The addition of Kamal for Docker-based deployments enables easy deployment to your own machines or VPS, reducing reliance on expensive platforms like Vercel or Heroku.

Now, the team is addressing another costly dependency: CI providers.

The CI Cost Problem

ci cost

Companies are paying significant amounts to CI providers like GitHub Actions, Jenkins, Semaphore, and CircleCI just to run test suites. Yet most development teams already have powerful hardware at their disposal—either company-provided MacBooks and workstations or developers’ personal machines.

This raises an interesting question: Why run builds in the cloud when developers can run them locally?

The Trust Question

trust

Some might argue, “Can we trust developers to run tests honestly?” The Rails team’s perspective is pragmatic: companies already trust developers with far more critical responsibilities—access to production systems, customer data, and the entire codebase. Running tests locally doesn’t introduce new trust issues; in fact, it can strengthen team accountability and ownership.

Signing Off on Local Builds

sign

The remaining challenge is verification: how do you mark a commit or PR as passing when tests run locally?

The Basecamp team developed a GitHub CLI extension called basecamp/gh-signoff to solve this. After running your local CI successfully, you simply install the extension and sign off on the commit, which creates a GitHub commit status.

1
2
3
4
5
# Install the extension
gh extension install basecamp/gh-signoff

# Sign off on a passing build
gh signoff

Why Use config/ci.rb?

The config/ci.rb file provides several benefits:

  1. Consistency: All developers run the same CI steps in the same order
  2. Speed: Local execution is often faster than waiting for cloud CI
  3. Cost savings: Eliminates or reduces CI provider bills
  4. Immediate feedback: No waiting for CI queues
  5. Offline capability: Run tests without internet connectivity

How config/ci.rb Works

The newly generated ci.rb file defines a structured test pipeline:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# Run using bin/ci

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"

  # Optional: Run system tests
  # step "Tests: System", "bin/rails test:system"

  # Optional: set a green GitHub commit status to unblock PR merge.
  # Requires the `gh` CLI and `gh extension install basecamp/gh-signoff`.
  # if success?
  #   step "Signoff: All systems go. Ready for merge and deploy.", "gh signoff"
  # else
  #   failure "Signoff: CI failed. Do not merge or deploy.", "Fix the issues and try again."
  # end
end

Each step defines a label and command. The runner executes them sequentially, stopping if any step fails. You can customize this file to match your project’s specific needs—add database migrations, additional linters, or custom test suites.

To run the CI pipeline locally:

1
bin/ci

Conclusion

Rails 8’s CI.rb represents a pragmatic approach to continuous integration: leverage the powerful hardware developers already have, reduce cloud computing costs, and maintain fast feedback loops. While cloud CI still has its place (especially for cross-platform testing or deployment pipelines), local CI execution offers a compelling alternative for day-to-day development.

By combining config/ci.rb with tools like gh-signoff, Rails provides teams with the flexibility to choose the CI approach that best fits their workflow and budget.