โ† Back to Documentation Index โœจ PROFESSIONAL EDITION

๐Ÿงช Local CI Integration

Run Tests Locally with CI Configuration

Richard Piacentini

Project Lead: Richard Piacentini

Cheqs Global LTD

AI Research: Claude Sonnet 4.5

Suite Finalized: October 10, 2025

๐Ÿ“ Living Document - Updated until Rails 8.1 stable release

Rails 8.1 Feature

๐Ÿ“‹ Overview

Local CI Integration brings your CI/CD pipeline to your local development environment. Run the exact same tests and checks that run in your CI pipeline, right on your machine, before pushing code.

Key Benefits

  • โšก 50-60% Faster Feedback - Catch issues immediately without waiting for CI
  • ๐Ÿ’ฐ 15-25% CI Cost Reduction - Fewer failed CI runs means lower infrastructure costs
  • ๐Ÿ”„ Consistent Testing - Same configuration locally and in CI
  • ๐Ÿš€ Improved DX - Faster iteration cycles for developers

๐ŸŽฏ What Problem Does It Solve?

Before Rails 8.1

  • Push code โ†’ Wait for CI โ†’ Fix issues โ†’ Repeat
  • Inconsistent test environments between local and CI
  • Wasted CI minutes on trivial failures
  • Slow feedback loops (5-10 minutes per cycle)

With Rails 8.1 Local CI

  • Run full CI suite locally in 2-5 minutes
  • Catch issues before pushing
  • Identical environment to production CI
  • Pay only for successful CI runs

๐Ÿ”ง How It Works

Rails 8.1 introduces a built-in CI DSL that defines your test pipeline in config/ci.rb:

# config/ci.rb
ci do
  # Run test suites
  test :units
  test :integration
  test :system

  # Code quality checks
  lint

  # Security scanning
  security_scan

  # Performance checks
  performance_test
end

Running Locally

# Run the full CI suite locally
bin/ci

# Run specific test groups
bin/ci test:units
bin/ci lint

# Run with coverage
bin/ci --coverage

๐Ÿ’ก Use Cases

1. Pre-Commit Validation

Run CI checks before committing to catch issues early:

# In your git pre-commit hook
#!/bin/bash
bin/ci test:units lint

2. Feature Branch Testing

Validate entire feature branches before opening PRs:

# Full CI suite before PR
bin/ci --full

3. Debugging CI Failures

Reproduce CI failures locally for faster debugging:

# Run exact CI configuration
bin/ci --env=ci

๐Ÿš€ Getting Started

Step 1: Create CI Configuration

# config/ci.rb
ci do
  # Define your test pipeline
  test :models
  test :controllers
  test :integration

  # Add quality checks
  lint
  security_scan
end

Step 2: Run Locally

# Test the configuration
bin/ci

# See available commands
bin/ci --help

Step 3: Integrate with Git Hooks

# .git/hooks/pre-push
#!/bin/bash
bin/ci test:units lint || exit 1

๐Ÿ“Š Configuration Options

Test Groups

ci do
  # Parallel test execution
  test :units, parallel: 4

  # With specific options
  test :system, headless: true

  # Conditional execution
  test :integration, if: -> { ENV['FULL_CI'] }
end

Linting & Quality

ci do
  # RuboCop
  lint do
    rubocop '--parallel'
  end

  # Brakeman security scan
  security_scan do
    brakeman '--format json'
  end
end

Custom Commands

ci do
  # Custom checks
  command 'bundle exec rspec spec/models'
  command 'yarn test'

  # With descriptions
  command 'npm run lint', description: 'Frontend linting'
end

๐Ÿ† Best Practices

1. Keep It Fast

  • Run quick checks first (lint, unit tests)
  • Save slow tests for later (system, integration)
  • Use parallel execution where possible
ci do
  # Fast checks first
  lint
  test :units, parallel: 4

  # Slower tests later
  test :integration
  test :system
end

2. Mirror Production CI

  • Use identical configuration locally and in CI
  • Same Ruby version, dependencies, database
  • Same environment variables
# config/ci.rb
ci do
  # Set CI environment
  env 'CI' => 'true'
  env 'RAILS_ENV' => 'test'

  # Use same database as CI
  database :test
end

3. Fail Fast

  • Stop on first failure for rapid feedback
  • Use --fail-fast flag
# Stop on first error
bin/ci --fail-fast

๐Ÿ“ˆ Performance Optimization

Parallel Execution

ci do
  # Run tests in parallel
  test :units, parallel: :auto  # Uses CPU count
  test :integration, parallel: 4
end

Caching

ci do
  # Cache dependencies
  cache 'vendor/bundle'
  cache 'node_modules'

  # Cache test results
  cache '.test-cache'
end

Selective Testing

# Run only changed tests
bin/ci --changed

# Run based on git diff
bin/ci --since origin/main

๐Ÿ”— Integration Examples

GitHub Actions

# .github/workflows/ci.yml
name: CI
on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: ruby/setup-ruby@v1
      - run: bundle install
      - run: bin/ci  # Same command locally and in CI

GitLab CI

# .gitlab-ci.yml
test:
  script:
    - bundle install
    - bin/ci  # Identical to local runs

CircleCI

# .circleci/config.yml
jobs:
  test:
    steps:
      - checkout
      - run: bundle install
      - run: bin/ci

โš ๏ธ Common Pitfalls

1. Environment Differences

Problem: Different results locally vs CI

Solution:

ci do
  # Force CI environment
  env 'CI' => 'true'
  env 'RAILS_ENV' => 'test'
end

2. Database State

Problem: Tests fail due to dirty database

Solution:

ci do
  # Reset database before tests
  before_suite do
    system 'bin/rails db:test:prepare'
  end
end

3. Missing Dependencies

Problem: Local CI fails on missing gems/packages

Solution:

# Always bundle before CI
bundle install && bin/ci

๐Ÿ“š Related Features

  • Active Job Continuations - Resume interrupted test jobs
  • Structured Event Reporting - Better CI debugging with structured logs
  • Kamal Integration - Deploy with confidence after local CI validation

๐ŸŽ“ Learn More

Official Documentation

Community Resources