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
name: CI
This defines the name of the workflow as CI, which stands for Continuous Integration.
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.
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
- name: Checkout code
uses: actions/checkout@v4
This step checks out the project's code from the repository to the runner environment.
- 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.
- 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.
lint:
runs-on: ubuntu-latest
Runs this job on the Ubuntu runner.
Steps
- name: Checkout code
uses: actions/checkout@v4
Similar to the previous job, this step checks out the code to the runner.
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: .ruby-version
bundler-cache: true
Sets up Ruby and caches dependencies.
- 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.
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.
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
- 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.
- name: Checkout code
uses: actions/checkout@v4
Checks out the codebase.
- name: Set up Node.js
uses: actions/setup-node@v3
with:
node-version: '16'
Sets up Node.js version 16 for frontend-related tasks.
- name: Cache Node modules
uses: actions/cache@v3
with:
path: ~/.npm
Caches Node.js dependencies to reduce runtime.
- name: Install npm dependencies
run: npm install
Installs frontend dependencies defined in the package.json file.
- name: Set up Ruby
uses: ruby/setup-ruby@v1
Sets up Ruby as before.
- name: Cache gems
uses: actions/cache@v3
Caches Ruby gems to improve performance.
- 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.
- name: Debug esbuild installation
run: npx esbuild --version
Ensures that esbuild, a JavaScript bundler, is correctly installed.
- name: Run build
run: npm run build
Builds the frontend assets.
- 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.
- 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.