One job, several Ruby versions
The matrix
One job tests one Ruby version. A matrix turns that job into several jobs, one per version, without copying the steps. The workflow keeps the step list and gains a list of versions:
jobs:
test:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
ruby: ["3.3", "3.4", "4.0"]
steps:
- uses: actions/checkout@v4
- uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ matrix.ruby }}
bundler-cache: true
- run: bundle exec rake ci
fail-fast: false keeps the other jobs running when one version fails. The default stops the rest as soon as the first job fails, which is faster on a broken branch and hides whether the problem is one version or all of them.
Check that it parses
The version list is data, so the YAML check can print it:
ruby -ryaml -e 'p YAML.load_file(ARGV[0])["jobs"]["test"]["strategy"]["matrix"]["ruby"]' .github/workflows/ci.yml
["3.3", "3.4", "4.0"]
Three jobs, each with the same steps and one different value. The ${{ matrix.ruby }} is replaced before the job starts, so the runner only ever sees a plain string.
The same check locally
A matrix only helps if the code actually runs on every version in it. The interpreter can tell a script which version it is:
ruby -e 'puts RUBY_VERSION'
3.4.12
A guard for a feature that needs a newer Ruby compares versions properly instead of string by string:
ruby -e 'puts Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("3.0")'
true
String comparison would call "3.10" smaller than "3.9"; Gem::Version reads the dots as numbers. Put the minimum version in the gemspec with required_ruby_version and the matrix and the package then agree.
What to watch for
Every matrix entry costs a full job: install the gems, run the tests. Two
versions are usually enough for a course project; three is a statement
that the library supports the old line. Drop a version when it reaches
end of life, and add the new one in the same commit.
A matrix that is red on the oldest version only is the useful case: the
code uses something newer than the gemspec promises. Read the failing
job before removing the version from the list.
Next: Artifacts against caches
Article author: Arthur Isaev