Ruby in GitHub Actions

The workflow file

The two big hosts describe a pipeline in the same way. This lesson writes the GitHub file; the next one writes the GitLab file.

GitHub reads workflows from .github/workflows/. One YAML file describes when the pipeline runs and what each job does. For the project from the previous lesson the whole file is ten lines of YAML plus the step list:

name: CI on: push: pull_request: jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: ruby/setup-ruby@v1 with: ruby-version: "3.4" bundler-cache: true - run: bundle exec rake test

Three things carry the work. actions/checkout copies the repository into the runner. ruby/setup-ruby installs the chosen Ruby and, with bundler-cache: true, runs bundle install and caches the result keyed on Gemfile.lock. The last step is the command from the previous lesson.

The trigger is the on block: every push and every pull request. A workflow that runs on a branch nobody merges is noise; a workflow that runs only on tags misses the bug before the release.

Check that it parses

A YAML file that does not parse fails before any job starts, and the error points at a line rather than at the mistake. Ruby can parse the file with the standard library, which is the quickest check before a push:

ruby -ryaml -e 'YAML.load_file(ARGV[0])' .github/workflows/ci.yml

No output means the file parsed. To see something useful, ask for one value:

ruby -ryaml -e 'p YAML.load_file(ARGV[0])["jobs"]["test"]["steps"].size' .github/workflows/ci.yml

3

Three steps, as written. One Ruby quirk shows up if you print the whole document: the YAML 1.1 parser reads the key on as the boolean true. GitHub parses the file with its own rules and reads it as a string, so the workflow works; the Ruby dump is the odd one out.

The same steps by hand

The workflow has no secret sauce: it installs and it tests. Run the same two commands locally and compare:

Install the gems

bundle install

Fetching gem metadata from https://rubygems.org/...
Resolving dependencies...
Fetching minitest 5.27.0
Installing minitest 5.27.0
Bundle complete! 2 Gemfile dependencies, 2 gems now installed.
Use `bundle info [gemname]` to see where a bundled gem is installed.

Run the tests

bundle exec rake test

Run options: --seed 8918

# Running:

...

Finished in 0.001315s, 2281.3081 runs/s, 3041.7441 assertions/s.

3 runs, 4 assertions, 0 failures, 0 errors, 0 skips

The output above is from running these two commands locally; the runner log is the same text with timestamps around it. When a job fails, this is the section of the log to read first: the failing command and the lines above it, not the top of the page.

What to watch for

bundler-cache: true only helps when Gemfile.lock is committed. Without the lock file the cache key changes on every run and the job reinstalls the same gems every time, which looks like a slow pipeline rather than a missing file.

Pin the Ruby version in the workflow rather than saying latest. A new Ruby release then arrives as an intentional change with a failing matrix entry, not as a pipeline that went red during the weekend.

Next: Ruby in GitLab CI

Article author: Arthur Isaev

Banner image