This is a comprehensive and robust GitHub Actions YAML snippet guide for this Ruby on Rails project.
This snippet will cover:
main
and pull requests targeting main
.Here’s the .github/workflows/ci.yml
snippet:
# .github/workflows/ci.yml
name: Ruby on Rails CI
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
test:
name: Test (Ruby $)
runs-on: ubuntu-latest
# Services to spin up before the job starts
# The service name (e.g., 'postgres', 'redis') will be the hostname
# for accessing the service from within the runner.
services:
postgres:
image: postgres:14-alpine # Or your preferred PostgreSQL version
env:
POSTGRES_USER: rails_user # User for CI
POSTGRES_PASSWORD: rails_password # Password for CI
POSTGRES_DB: cover_letter_app_test_ci # Distinct DB name for CI
ports:
- 5432:5432 # Map container port 5432 to host port 5432
# Health check to ensure postgres is ready before tests run
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
redis:
image: redis:7-alpine # Or your preferred Redis version
ports:
- 6379:6379 # Map container port 6379 to host port 6379
options: >-
--health-cmd "redis-cli ping"
--health-interval 10s
--health-timeout 5s
--health-retries 5
strategy:
fail-fast: false # Don't cancel other jobs in the matrix if one fails
matrix:
ruby-version: ['3.1', '3.2', '3.3'] # Specify Ruby versions to test against
# Environment variables available to all steps
env:
RAILS_ENV: test
DATABASE_URL: "postgres://rails_user:rails_password@localhost:5432/cover_letter_app_test_ci"
REDIS_URL: "redis://localhost:6379/1" # Use a different Redis DB number for tests if needed
# If your tests make real calls to Google AI API (discouraged for most tests - mock them!)
# You would need to set this secret in your GitHub repository settings
# GOOGLE_API_KEY: $
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Ruby $
uses: ruby/setup-ruby@v1
with:
ruby-version: $
bundler-cache: true # Automatically runs bundle install and caches gems
working-directory: CoverLetterApp
# Optional: If you're using Webpacker/Shakapacker or any JS managed by Node.js
- name: Set up Node.js (if using Webpacker/Shakapacker)
uses: actions/setup-node@v4
with:
node-version: '18' # Or your project's Node.js version
cache: 'yarn' # Or 'npm' if you use npm
- name: Install JavaScript dependencies (if using Webpacker/Shakapacker)
# Only run if you have a yarn.lock or package-lock.json
if: |
(
(steps.setup-node.outputs.cache-hit != 'true' || steps.setup-node.outputs.cache-hit == '') &&
(hashFiles('yarn.lock') != '' || hashFiles('package-lock.json') != '')
)
run: |
if [ -f yarn.lock ]; then
yarn install --frozen-lockfile
elif [ -f package-lock.json ]; then
npm ci
fi
# Configure database (if your config/database.yml doesn't automatically use DATABASE_URL)
# A common pattern is to have a config/database.yml.ci file and copy it
# Or ensure your default config/database.yml can read DATABASE_URL
# - name: Configure database
# run: cp config/database.yml.ci config/database.yml
- name: Create and migrate database
run: |
bundle exec rails db:prepare
# For some setups, `db:schema:load` might be preferred/faster if db:prepare doesn't work as expected
# bundle exec rails db:create
# bundle exec rails db:schema:load
- name: Run RuboCop
run: bundle exec rubocop
- name: Run RSpec tests
run: bundle exec rspec
Explanation and Key Points:
name: Ruby on Rails CI
: The name of your workflow.on:
: Defines triggers. This runs on pushes to main
and pull requests targeting main
.jobs:
: Defines one or more jobs. We have one job named test
.runs-on: ubuntu-latest
: Specifies the runner environment.services:
:
postgres
: Sets up a PostgreSQL container.
image
: Specifies the Docker image.env
: Sets environment variables inside the PostgreSQL container (like user, password, db name).ports
: Maps the container’s port 5432 to the runner’s port 5432. This means your Rails app in the runner can connect to localhost:5432
.options --health-cmd
: A crucial check to ensure PostgreSQL is actually ready to accept connections before proceeding.redis
: Sets up a Redis container, similar to PostgreSQL.strategy: matrix:
:
ruby-version
s.fail-fast: false
: Ensures that if one Ruby version fails, the tests for other Ruby versions still run.env:
: Sets environment variables for the job steps.
RAILS_ENV: test
: Standard for running tests.DATABASE_URL
: Your Rails app will use this to connect to the PostgreSQL service. The hostname is localhost
because the service container’s port is mapped to the runner’s localhost.REDIS_URL
: For Sidekiq to connect to the Redis service.GOOGLE_API_KEY
: Important: For most of your tests (unit, integration), you should mock calls to external APIs like Google Generative AI. This makes tests faster, more reliable, and avoids API costs during testing. If you have specific end-to-end tests that must hit the real API in CI (use sparingly), you’d store the API key as a secret in your GitHub repository (Settings > Secrets and variables > Actions > New repository secret
) and reference it here.steps:
: The actual commands to run.
actions/checkout@v4
: Checks out your repository code.ruby/setup-ruby@v1
: Sets up the specified Ruby version and uses bundler-cache: true
to automatically install gems from your Gemfile.lock
and cache them for faster subsequent runs.webpacker
gem), you’ll need Node.js and to install JS dependencies. This step is conditional.DATABASE_URL
environment variable should ideally be picked up by your config/database.yml
. If your config/database.yml
is set up to read ENV['DATABASE_URL']
for the test environment, you might not need to copy a CI-specific database.yml
file.bundle exec rails db:prepare
: This command is often sufficient. It creates the database if it doesn’t exist, and loads the schema. If it gives you trouble, you can fall back to the more explicit db:create
and db:schema:load
. Using db:schema:load
is generally faster in CI than db:migrate
as it loads the structure directly from db/schema.rb
.Run RuboCop
: Runs your linter.Run RSpec tests
: Executes your test suite.Before using this:
GOOGLE_API_KEY
for any CI tests, add it as a secret in your GitHub repository settings.config/database.yml
: Ensure your config/database.yml
for the test
environment can use the DATABASE_URL
environment variable. A typical setup looks like this:
# config/database.yml
default: &default
adapter: postgresql
encoding: unicode
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
development:
<<: *default
database: CoverLetterApp_development
# ... your local dev settings
test:
<<: *default
# This will be overridden by DATABASE_URL if present
database: CoverLetterApp_test
# For CI, DATABASE_URL will be:
# url: <%= ENV['DATABASE_URL'] %>
Rails 6+ usually handles DATABASE_URL
automatically if it’s present, so your existing test
block might just need:
test:
<<: *default
database: CoverLetterApp_test_ci # This will be overridden by DATABASE_URL
The key is that ENV['DATABASE_URL']
takes precedence.
webpacker
gem, I included it.