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