Setup πŸ›  Rails 8 App – Part 15: Set Up CI/CD βš™οΈ with GitHub Actions for Rails 8

Prerequisites

Our System Setup

  • Ruby version: 3.4.1
  • Rails version: 8.0.2
  • JavaScript tooling: using rails default tubo-stream, NO nodeJS or extra js

We would love to see:

  • RuboCop linting Checks
  • SimpleCov test coverage report
  • Brakeman security scan

Here’s how to set up CI that runs on every push, including pull requests:

1. Create GitHub Actions Workflow

Create this file: .github/workflows/ci.yml

name: Rails CI

# Trigger on pushes to main or any feature branch, and on PRs targeting main
on:
  push:
    branches:
      - main
      - 'feature/**'
  pull_request:
    branches:
      - main

jobs:
  # 1) Lint job with RuboCop
  lint:
    name: RuboCop Lint
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v3

      - name: Set up Ruby
        uses: ruby/setup-ruby@v1
        with:
          ruby-version: 3.4.1
          bundler-cache: true

      - name: Install dependencies
        run: |
          sudo apt-get update -y
          sudo apt-get install -y libpq-dev
          bundle install --jobs 4 --retry 3

      - name: Run RuboCop
        run: bundle exec rubocop --fail-level E

  # 2) Test job with Minitest
  test:
    name: Minitest Suite
    runs-on: ubuntu-latest
    needs: lint

    services:
      postgres:
        image: postgres:15
        ports:
          - 5432:5432
        env:
          POSTGRES_PASSWORD: password
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5

    env:
      RAILS_ENV: test
      DATABASE_URL: postgres://postgres:password@localhost:5432/test_db

    steps:
      - name: Checkout code
        uses: actions/checkout@v3

      - name: Set up Ruby
        uses: ruby/setup-ruby@v1
        with:
          ruby-version: 3.4.1
          bundler-cache: true

      - name: Install dependencies
        run: |
          sudo apt-get update -y
          sudo apt-get install -y libpq-dev
          bundle install --jobs 4 --retry 3

      - name: Set up database
        run: |
          bin/rails db:create
          bin/rails db:schema:load

      - name: Run Minitest
        run: bin/rails test
  # 3) Security job with Brakeman
  security:
    name: Brakeman Scan
    runs-on: ubuntu-latest
    needs: [lint, test]

    steps:
      - uses: actions/checkout@v3
      - name: Set up Ruby
        uses: ruby/setup-ruby@v1
        with:
          ruby-version: 3.4.1
          bundler-cache: true

      - name: Install Brakeman
        run: bundle install --jobs 4 --retry 3

      - name: Run Brakeman
        run: bundle exec brakeman --exit-on-warnings

How this works:

  1. on.push & on.pull_request:
    • Runs on any push to main or feature/**, and on PRs targeting main.
  2. lint job:
    • Checks out code, sets up Ruby 3.4.1, installs gems (with bundler-cache), then runs bundle exec rubocop --fail-level E to fail on any error-level offenses.
  3. test job:
    • Depends on the lint job (needs: lint), so lint must pass first.
    • Spins up a PostgreSQL 15 service, sets DATABASE_URL for Rails, creates & loads the test database, then runs your Minitest suite with bin/rails test.

πŸ›  What Does .github/dependabot.yml Do?

This YAML file tells Dependabot:
♦️ Which dependencies to monitor
♦️ Where (which directories) to look for manifest files
♦️ How often to check for updates
♦️ What package ecosystems (e.g., RubyGems, npm, Docker) are used
♦️ Optional rules like versioning, reviewer assignment, and update limits

Dependabot then opens automated pull requests (PRs) in your repository when:

  • There are new versions of dependencies
  • A security advisory affects one of your dependencies

This helps you keep your app up to date and secure without manual tracking.

πŸ— Example: Typical .github/dependabot.yml

Here’s a sample for a Ruby on Rails app:

version: 2
updates:
  - package-ecosystem: bundler
    directory: "/"
    schedule:
      interval: weekly
    open-pull-requests-limit: 5
    rebase-strategy: auto
    ignore:
      - dependency-name: rails
        versions: ["7.x"]
  - package-ecosystem: github-actions
    directory: "/"
    schedule:
      interval: weekly

♦️ Place the .github/dependabot.yml file in the .github directory of your repo root.
♦️ Tailor the schedule and limits to your team’s capacity.
♦️ Use the ignore block carefully if you deliberately skip certain updates (e.g., major version jumps).
♦️ Combine it with branch protection rules so Dependabot PRs must pass tests before merging.

πŸš€ Steps to Push and Test Your CI

βœ… You can push both files (ci.yml and dependabot.yml) together in one commit

Here’s a step-by-step guide for testing that your CI works right after the push.

1️⃣ Stage and commit your files

git add .github/workflows/ci.yml .github/dependabot.yml
git commit -m 'feat: Add github actions CI workflow Close #23'

2️⃣ Push to a feature branch
(for example, if you’re working on feature/github-ci):

git push origin feature/github-ci

3️⃣ Open a Pull Request

  • Go to GitHub β†’ your repository β†’ create a pull request from feature/github-ci to main.

4️⃣ Watch GitHub Actions run

  • Go to the Pull Request page.
  • You should see a yellow dot / pending check under “Checks”.
  • Click the “Details” link next to the check (or go to the Actions tab) to see live logs.

βœ… How to Know It’s Working

βœ”οΈ If all your jobs (e.g., RuboCop Lint, Minitest Suite) finish with green checkmarks, your CI setup is working!

❌ If something fails, you’ll get a red X and the logs will show exactly what failed.

So what’s the problem. Check details.

Check brakeman help for further information about the option.

➜  design_studio git:(feature/github-ci) brakeman --help | grep warn
    -z, --[no-]exit-on-warn          Exit code is non-zero if warnings found (Default)
        --ensure-ignore-notes        Fail when an ignored warnings does not include a note

Modify the option and run again:

run: bundle exec brakeman --exit-on-warn

Push the code and check all checks are passing. βœ…

πŸ›  How to Test Further

If you want to trigger CI without a PR, you can push directly to main:

git checkout main
git merge feature/setup-ci
git push origin main

Note: Make sure your .github/workflows/ci.yml includes:

on:
  push:
    branches: [main, 'feature/**']
  pull_request:
    branches: [main]

This ensures CI runs on both pushes and pull requests.

πŸ§ͺ Pro Tip: Break It Intentionally

If you want to see CI fail, you can:

  • Add a fake RuboCop error (like an unaligned indent).
  • Add a failing test (assert false).
  • Push and watch the red X appear.

This is a good way to verify your CI is catching problems!


Happy Rails CI setup! πŸš€

Unknown's avatar

Author: Abhilash

Hi, I’m Abhilash! A seasoned web developer with 15 years of experience specializing in Ruby and Ruby on Rails. Since 2010, I’ve built scalable, robust web applications and worked with frameworks like Angular, Sinatra, Laravel, Node.js, Vue and React. Passionate about clean, maintainable code and continuous learning, I share insights, tutorials, and experiences here. Let’s explore the ever-evolving world of web development together!

2 thoughts on “Setup πŸ›  Rails 8 App – Part 15: Set Up CI/CD βš™οΈ with GitHub Actions for Rails 8”

Leave a comment