Building a Bulletproof CI/CD Pipeline for Ruby on Rails

· CI/CD & GitHub Actions · By Zeeshan Ahmad

Problem

Continuous Integration (CI) ensures that new code changes are regularly tested and validated. The provided YAML file configures a GitHub Actions CI pipeline for a Ruby on Rails project, including steps for security analysis, linting, and testing. This article dissects each line to clarify its purpose and functionality.

Solution

Below is a line-by-line breakdown of the YAML configuration:

1. Defining the Workflow Name and Triggers

bash
name: CI

This defines the name of the workflow as CI, which stands for Continuous Integration.

bash
on:
  pull_request:
  push:
    branches: [ main ]

This specifies the triggers for the workflow. It runs whenever a pull request is opened or when code is pushed to the main branch.

2. Job: scan_ruby

The scan_ruby job performs static analysis for common Rails security vulnerabilities using brakeman.

bash
jobs:
  scan_ruby:
    runs-on: ubuntu-latest

The job runs on the latest version of the Ubuntu operating system provided by GitHub-hosted runners.

Steps

bash
- name: Checkout code
  uses: actions/checkout@v4

This step checks out the project's code from the repository to the runner environment.

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

Sets up the Ruby environment as specified in the .ruby-version file. It also caches bundler dependencies to speed up subsequent runs.

bash
- name: Scan for common Rails security vulnerabilities using static analysis
  run: bin/brakeman --no-pager

Runs brakeman, a static analysis tool that scans Rails applications for security vulnerabilities.

3. Job: lint

The lint job ensures consistent code style across the repository using rubocop.

bash
lint:
    runs-on: ubuntu-latest

Runs this job on the Ubuntu runner.

Steps

bash
- name: Checkout code
  uses: actions/checkout@v4

Similar to the previous job, this step checks out the code to the runner.

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

Sets up Ruby and caches dependencies.

bash
- name: Lint code for consistent style
  run: bin/rubocop -f github

Runs rubocop with the -f github format for GitHub-optimized output.

4. Job: test

The test job runs the application’s automated tests to ensure functionality.

bash
test:
    runs-on: ubuntu-latest
    services:
      postgres:
        image: postgres:13

Runs the job on Ubuntu and sets up a PostgreSQL 13 service for database integration tests.

bash
ports:
          - 5432:5432
        options: --health-cmd "pg_isready -U postgres" --health-interval 10s --health-timeout 5s --health-retries 5
        env:
          POSTGRES_PASSWORD: postgres
          POSTGRES_USER: postgres
          POSTGRES_DB: charter_test

Configures the PostgreSQL service with health checks and environment variables for credentials.

Steps

bash
- name: Install packages
  run: sudo apt-get update && sudo apt-get install --no-install-recommends -y google-chrome-stable curl libjemalloc2 libvips sqlite3

Installs system dependencies such as Google Chrome, used for system tests.

bash
- name: Checkout code
  uses: actions/checkout@v4

Checks out the codebase.

bash
- name: Set up Node.js
  uses: actions/setup-node@v3
  with:
    node-version: '16'

Sets up Node.js version 16 for frontend-related tasks.

bash
- name: Cache Node modules
  uses: actions/cache@v3
  with:
    path: ~/.npm

Caches Node.js dependencies to reduce runtime.

bash
- name: Install npm dependencies
  run: npm install

Installs frontend dependencies defined in the package.json file.

bash
- name: Set up Ruby
  uses: ruby/setup-ruby@v1

Sets up Ruby as before.

bash
- name: Cache gems
  uses: actions/cache@v3

Caches Ruby gems to improve performance.

bash
- name: Install dependencies
  run: |
    gem install bundler
    bundle config path vendor/bundle
    bundle install --jobs 4 --retry 3

Installs the Ruby gems required for the project.

bash
- name: Debug esbuild installation
  run: npx esbuild --version

Ensures that esbuild, a JavaScript bundler, is correctly installed.

bash
- name: Run build
  run: npm run build

Builds the frontend assets.

bash
- name: Run tests
  env:
    RAILS_ENV: test
  run: bin/rails db:test:prepare test test:system

Runs database preparation and system tests for the Rails application.

bash
- name: Keep screenshots from failed system tests
  uses: actions/upload-artifact@v4
  if: failure()
  with:
    name: screenshots
    path: ${{ github.workspace }}/tmp/screenshots
    if-no-files-found: ignore

Uploads screenshots from failed tests for debugging purposes.

Conclusion

This YAML file outlines a comprehensive CI pipeline for a Ruby on Rails application. By using GitHub Actions, the workflow performs security scans, code linting, and extensive testing to ensure the application maintains high standards for quality and security.

Related articles

Home · All Tools · Blog