--- title: Building and testing Ruby intro: You can create a continuous integration (CI) workflow to build and test your Ruby project. redirect_from: - /actions/guides/building-and-testing-ruby - /actions/automating-builds-and-tests/building-and-testing-ruby - /actions/use-cases-and-examples/building-and-testing/building-and-testing-ruby - /actions/how-tos/use-cases-and-examples/building-and-testing/building-and-testing-ruby - /actions/how-tos/writing-workflows/building-and-testing/building-and-testing-ruby - /actions/tutorials/build-and-test-code/building-and-testing-ruby versions: fpt: '*' ghes: '*' ghec: '*' topics: - CI - Ruby shortTitle: Ruby --- {% data reusables.actions.enterprise-github-hosted-runners %} ## Introduction This guide shows you how to create a continuous integration (CI) workflow that builds and tests a Ruby application. If your CI tests pass, you may want to deploy your code or publish a gem. ## Prerequisites We recommend that you have a basic understanding of Ruby, YAML, workflow configuration options, and how to create a workflow file. For more information, see: * [Learn {% data variables.product.prodname_actions %}](/actions/learn-github-actions) * [Ruby in 20 minutes](https://www.ruby-lang.org/en/documentation/quickstart/) ## Using a Ruby workflow template {% data reusables.actions.workflow-templates-get-started %} {% data variables.product.prodname_dotcom %} provides a workflow template for Ruby that should work for most Ruby projects. The subsequent sections of this guide give examples of how you can customize this workflow template. {% data reusables.repositories.navigate-to-repo %} {% data reusables.repositories.actions-tab %} {% data reusables.actions.new-starter-workflow %} 1. The "Choose a workflow" page shows a selection of recommended workflow templates. Search for "ruby". 1. Filter the selection of workflows by clicking **Continuous integration**. 1. On the "Ruby" workflow, click **Configure**. {%- ifversion ghes %} If you don't find the "Ruby" workflow template, copy the following workflow code to a new file called `ruby.yml` in the `.github/workflows` directory of your repository. ```yaml copy {% data reusables.actions.actions-not-certified-by-github-comment %} name: Ruby on: push: branches: [ "main" ] pull_request: branches: [ "main" ] permissions: contents: read jobs: test: runs-on: ubuntu-latest strategy: matrix: ruby-version: ['2.6', '2.7', '3.0'] steps: - uses: {% data reusables.actions.action-checkout %} - name: Set up Ruby # To automatically get bug fixes and new Ruby versions for ruby/setup-ruby, # change this to (see https://github.com/ruby/setup-ruby#versioning): # uses: ruby/setup-ruby@v1 uses: ruby/setup-ruby@55283cc23133118229fd3f97f9336ee23a179fcf # v1.146.0 with: ruby-version: {% raw %}${{ matrix.ruby-version }}{% endraw %} bundler-cache: true # runs 'bundle install' and caches installed gems automatically - name: Run tests run: bundle exec rake ``` {%- endif %} 1. Edit the workflow as required. For example, change the Ruby versions you want to use. {% indented_data_reference reusables.actions.third-party-actions spaces=3 %} 1. Click **Commit changes**. {% ifversion fpt or ghec %} The `ruby.yml` workflow file is added to the `.github/workflows` directory of your repository. {% endif %} ## Specifying the Ruby version The easiest way to specify a Ruby version is by using the `ruby/setup-ruby` action provided by the Ruby organization on GitHub. The action adds any supported Ruby version to `PATH` for each job run in a workflow. For more information and available Ruby versions, see [`ruby/setup-ruby`](https://github.com/ruby/setup-ruby). Using Ruby's `ruby/setup-ruby` action is the recommended way of using Ruby with GitHub Actions because it ensures consistent behavior across different runners and different versions of Ruby. The `setup-ruby` action takes a Ruby version as an input and configures that version on the runner. ```yaml {% data reusables.actions.actions-not-certified-by-github-comment %} steps: - uses: {% data reusables.actions.action-checkout %} - uses: ruby/setup-ruby@ec02537da5712d66d4d50a0f33b7eb52773b5ed1 with: ruby-version: '3.1' # Not needed with a .ruby-version file - run: bundle install - run: bundle exec rake ``` Alternatively, you can check a `.ruby-version` file into the root of your repository and `setup-ruby` will use the version defined in that file. ## Testing with multiple versions of Ruby You can add a matrix strategy to run your workflow with more than one version of Ruby. For example, you can test your code against the latest patch releases of versions 3.1, 3.0, and 2.7. {% raw %} ```yaml strategy: matrix: ruby-version: ['3.1', '3.0', '2.7'] ``` {% endraw %} Each version of Ruby specified in the `ruby-version` array creates a job that runs the same steps. The {% raw %}`${{ matrix.ruby-version }}`{% endraw %} context is used to access the current job's version. For more information about matrix strategies and contexts, see [AUTOTITLE](/actions/using-workflows/workflow-syntax-for-github-actions) and [AUTOTITLE](/actions/learn-github-actions/contexts). The full updated workflow with a matrix strategy could look like this: ```yaml {% data reusables.actions.actions-not-certified-by-github-comment %} {% data reusables.actions.actions-use-sha-pinning-comment %} name: Ruby CI on: push: branches: [ main ] pull_request: branches: [ main ] jobs: test: runs-on: ubuntu-latest strategy: matrix: ruby-version: ['3.1', '3.0', '2.7'] steps: - uses: {% data reusables.actions.action-checkout %} - name: {% raw %}Set up Ruby ${{ matrix.ruby-version }}{% endraw %} uses: ruby/setup-ruby@ec02537da5712d66d4d50a0f33b7eb52773b5ed1 with: ruby-version: {% raw %}${{ matrix.ruby-version }}{% endraw %} - name: Install dependencies run: bundle install - name: Run tests run: bundle exec rake ``` ## Installing dependencies with Bundler The `setup-ruby` action will automatically install bundler for you. The version is determined by your `gemfile.lock` file. If no version is present in your lockfile, then the latest compatible version will be installed. ```yaml {% data reusables.actions.actions-not-certified-by-github-comment %} steps: - uses: {% data reusables.actions.action-checkout %} - uses: ruby/setup-ruby@ec02537da5712d66d4d50a0f33b7eb52773b5ed1 with: ruby-version: '3.1' - run: bundle install ``` ### Caching dependencies The `setup-ruby` actions provides a method to automatically handle the caching of your gems between runs. To enable caching, set the following. {% raw %} ```yaml {% data reusables.actions.actions-not-certified-by-github-comment %} steps: - uses: ruby/setup-ruby@ec02537da5712d66d4d50a0f33b7eb52773b5ed1 with: bundler-cache: true ``` {% endraw %} This will configure bundler to install your gems to `vendor/cache`. For each successful run of your workflow, this folder will be cached by {% data variables.product.prodname_actions %} and re-downloaded for subsequent workflow runs. A hash of your `gemfile.lock` and the Ruby version are used as the cache key. If you install any new gems, or change a version, the cache will be invalidated and bundler will do a fresh install. **Caching without setup-ruby** For greater control over caching, you can use the `actions/cache` action directly. For more information, see [AUTOTITLE](/actions/using-workflows/caching-dependencies-to-speed-up-workflows). ```yaml steps: - uses: {% data reusables.actions.action-cache %} with: path: vendor/bundle key: {% raw %}${{ runner.os }}-gems-${{ hashFiles('**/Gemfile.lock') }}{% endraw %} restore-keys: | {% raw %}${{ runner.os }}-gems-{% endraw %} - name: Bundle install run: | bundle config path vendor/bundle bundle install --jobs 4 --retry 3 ``` If you're using a matrix build, you will want to include the matrix variables in your cache key. For example, if you have a matrix strategy for different ruby versions (`matrix.ruby-version`) and different operating systems (`matrix.os`), your workflow steps might look like this: ```yaml steps: - uses: {% data reusables.actions.action-cache %} with: path: vendor/bundle key: {% raw %}bundle-use-ruby-${{ matrix.os }}-${{ matrix.ruby-version }}-${{ hashFiles('**/Gemfile.lock') }}{% endraw %} restore-keys: | {% raw %}bundle-use-ruby-${{ matrix.os }}-${{ matrix.ruby-version }}-{% endraw %} - name: Bundle install run: | bundle config path vendor/bundle bundle install --jobs 4 --retry 3 ``` ## Matrix testing your code The following example matrix tests all stable releases and head versions of MRI, JRuby and TruffleRuby on Ubuntu and macOS. ```yaml {% data reusables.actions.actions-not-certified-by-github-comment %} {% data reusables.actions.actions-use-sha-pinning-comment %} name: Matrix Testing on: push: branches: [ main ] pull_request: branches: [ main ] jobs: test: runs-on: {% raw %}${{ matrix.os }}-latest{% endraw %} strategy: fail-fast: false matrix: os: [ubuntu, macos] ruby: [2.5, 2.6, 2.7, head, debug, jruby, jruby-head, truffleruby, truffleruby-head] continue-on-error: {% raw %}${{ endsWith(matrix.ruby, 'head') || matrix.ruby == 'debug' }}{% endraw %} steps: - uses: {% data reusables.actions.action-checkout %} - uses: ruby/setup-ruby@ec02537da5712d66d4d50a0f33b7eb52773b5ed1 with: ruby-version: {% raw %}${{ matrix.ruby }}{% endraw %} - run: bundle install - run: bundle exec rake ``` ## Linting your code The following example installs `rubocop` and uses it to lint all files. For more information, see [RuboCop](https://github.com/rubocop-hq/rubocop). You can [configure Rubocop](https://docs.rubocop.org/rubocop/configuration.html) to decide on the specific linting rules. ```yaml {% data reusables.actions.actions-not-certified-by-github-comment %} {% data reusables.actions.actions-use-sha-pinning-comment %} name: Linting on: [push] jobs: test: runs-on: ubuntu-latest steps: - uses: {% data reusables.actions.action-checkout %} - uses: ruby/setup-ruby@ec02537da5712d66d4d50a0f33b7eb52773b5ed1 with: ruby-version: '2.6' - run: bundle install - name: Rubocop run: rubocop -f github ``` Specifying `-f github` means that the RuboCop output will be in {% data variables.product.prodname_dotcom %}'s annotation format. Any linting errors will show inline in the **Files changed** tab of the pull request that introduces them. ## Publishing Gems You can configure your workflow to publish your Ruby package to any package registry you'd like when your CI tests pass. You can store any access tokens or credentials needed to publish your package using repository secrets. The following example creates and publishes a package to `GitHub Package Registry` and `RubyGems`. ```yaml {% data reusables.actions.actions-not-certified-by-github-comment %} {% data reusables.actions.actions-use-sha-pinning-comment %} name: Ruby Gem on: # Manually publish workflow_dispatch: # Alternatively, publish whenever changes are merged to the `main` branch. push: branches: [ main ] pull_request: branches: [ main ] jobs: build: name: Build + Publish runs-on: ubuntu-latest permissions: packages: write contents: read steps: - uses: {% data reusables.actions.action-checkout %} - name: Set up Ruby 2.6 uses: ruby/setup-ruby@ec02537da5712d66d4d50a0f33b7eb52773b5ed1 with: ruby-version: '2.6' - run: bundle install - name: Publish to GPR run: |{% raw %} mkdir -p $HOME/.gem touch $HOME/.gem/credentials chmod 0600 $HOME/.gem/credentials printf -- "---\n:github: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials gem build *.gemspec gem push --KEY github --host https://rubygems.pkg.github.com/${OWNER} *.gem env: GEM_HOST_API_KEY: "Bearer ${{secrets.GITHUB_TOKEN}}" OWNER: ${{ github.repository_owner }} - name: Publish to RubyGems run: | mkdir -p $HOME/.gem touch $HOME/.gem/credentials chmod 0600 $HOME/.gem/credentials printf -- "---\n:rubygems_api_key: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials gem build *.gemspec gem push *.gem env: GEM_HOST_API_KEY: "${{secrets.RUBYGEMS_AUTH_TOKEN}}"{% endraw %} ```