How Bundler and Rake build Ruby and Rails applications and gems, which files they need, how to lint, scan and test, and a complete GitHub Actions pipeline.
Posted
DevOps pipeline loop motif with the title Ruby with Bundler and Rake
Ruby builds revolve around two tools: Bundler resolves, locks and installs gems from the Gemfile, and Rake is the task runner that drives building, testing and packaging. For Rails applications the “build” also includes precompiling assets; for libraries it means building and publishing a .gem.
Brakeman (Rails), Semgrep, CodeQL, SonarQube Community Build
SAST (commercial)
Checkmarx One, Veracode, Snyk Code, SonarQube Server
SCA
bundler-audit, OSV-Scanner, Trivy, Snyk Open Source, Mend
Frameworks
Ruby on Rails, Sinatra, Hanami
Test and coverage
RSpec, Minitest, Capybara; SimpleCov
Build output
.gem package, Rails app with precompiled assets, container image
CI cache
vendor/bundle (handled by ruby/setup-ruby with bundler-cache: true)
How the build works
In CI, each stage of the generic build pipeline maps to a Bundler / Rake command, plugin or scanner. Press play, or step through it:
Source + Gemfile.lock: A commit triggers CI. Gemfile and Gemfile.lock pin every gem, and .ruby-version pins Ruby.
Install: With bundle config set deployment true, bundle install installs exactly Gemfile.lock and fails if it would change.
Lint and format: bundle exec rubocop checks style and common bugs (with rubocop-rails and rubocop-rspec); Standard is a zero-config alternative.
Build assets: For Rails apps, bin/rails assets:precompile builds the production assets; plain gems skip this stage.
Test and coverage: bundle exec rspec runs the specs, with SimpleCov loaded in spec_helper to measure coverage.
Security scans in parallel: Brakeman checks Rails code for vulnerabilities, bundle-audit check --update checks Gemfile.lock against known advisories, and Gitleaks scans for secrets.
Quality gate: RuboCop, the specs and both scanners are required checks; one failure stops the pipeline.
Fail the build (fail): A failed check stops the pipeline at the gate, so nothing is packaged, signed or published.
SBOM and signing (pass): Syft generates the SBOM, and Sigstore cosign signs the gem.
Publish (pass): gem push uploads the .gem to RubyGems.org or a private gem server.
bundle exec runs a command with exactly the gem versions in Gemfile.lock; always use it (or binstubs in bin/) in CI.
Platforms matter: Gemfile.lock lists the platforms it was resolved for. Add the Linux platform (bundle lock --add-platform x86_64-linux) when developers use macOS but CI and production run Linux.
Deployment mode (bundle config set deployment true) refuses to change the lockfile and installs into vendor/bundle.
New Rails applications ship with Brakeman, RuboCop and a GitHub Actions workflow already set up (bin/brakeman, bin/rubocop).
# Rakefile for a gem: adds build, install and release tasksrequire"bundler/gem_tasks"require"rspec/core/rake_task"RSpec::Core::RakeTask.new(:spec)taskdefault: :spec
Frameworks
Framework
Start a project
Ruby on Rails
gem install rails && rails new app
Sinatra
bundle add sinatra puma and a config.ru with run Sinatra::Application