GitHub Actions in Action

December 03, 2019

Over the Thanksgiving holiday, I took some time to familiarize myself with GitHub Actions and set up a couple of workflows. Here’s a quick review of what it takes to run rspec tests using GitHub Actions for a Rails project and a few of my observations.

But first, what are GitHub Actions?

GitHub Actions are small, composable tasks that can be combined to create a workflow. These workflows allow you to plug your team or project’s software development lifecycle (SDLC) right into your codebase. You can build, test, package, release, publish, and deploy with these workflows and you can automate the other parts of your process like assigning reviewers or sending notifications to Slack or linting files or checking code coverage.

My First Stab

My first stab at a workflow for running rspec tests went like this:

  1. From the Actions tab in GitHub, click “Set up this workflow” for the suggested Ruby workflow.
  2. Configured to run on pull requests to the master branch (or any pushes to that branch that has a pull request created)
  3. Updated to use my specific version of ubuntu-18.04 and ruby 2.5.7
  4. Changed bundle exec rake to be bundle exec rspec spec
  5. Commit and create a pull request to master for this branch
  6. Watch the action triggered!
  7. And fail. WOMP WOMP. 😞

Failed Test Run

The first failure was that Ruby 2.5.7 is not available on the virtual machine.

##[error]Version 2.5.7 not found

After a little digging, I found that (at this precise moment) the ubuntu-18.04 virtual machine only supports Ruby 2.5.5. This project depends on the latest security patched version 2.5.7. The standard actions/setup-ruby that GitHub suggested won’t work. Instead, I found a different one from the marketplace (where this person also encountered this same issue) to install the specific and correct version of Ruby. With this action, I was able to use the right version of Ruby as well as cache it with actions/cache. It’s still a bit pokey, but it worked.

The next roadblock was not being able to find the right bundler version that my Gemfile requires:

Could not find 'bundler' (1.17.3) required by your Gemfile.lock

Whoops. Adding -v 1.17.3 to the bundler install fixed that one.

And then it failed while trying to install the pg gem:

An error occurred while installing pg (1.0.0), and Bundler cannot continue.
Make sure that `gem install pg -v '1.0.0' --source 'https://rubygems.org/'`
succeeds before bundling.

To solve this problem, we can pull a container with postgresql into our workflow using the services block, which can be used to host other services like databases or caching in another container.

In our workflow file, it looks like this:

services:
      postgres:
        image: postgres:10.11
        ports: ["5432:5432"]
        options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5

Then we have to install the postgresql client:

- name: Install PostgreSQL 10 Client
      run: |
        sudo apt-get -yqq install libpq-dev

Note: My original build error with not being able to install the pg gem was because this libpq-dev package wasn’t available.

And set up the database:

- name: Setup Database
      env:
        PGHOST: localhost
        PGUSER: postgres
        RAILS_ENV: test
      run: |
        gem install bundler -v 1.17.3
        bundle install --jobs 4 --retry 3
        bin/rails db:setup

Depending on your needs here, you might need bin/rails db:create db:schema:load instead.

Adding these last three blocks (service, installing the postgresql client, and setting up the database), our tests now run!

Commit your .github/workflows/ruby.yml changes. If you’re using the GitHub Actions editor, you’ll automagically create a pull request with this change and your action will be triggered. If you making these changes locally, push them up and create a pull request and you’ll see the action in action.

Successful Test Run

The Final Version

Not patient enough to follow along or want to just see the final version? Here ya go! This file is located in: .github/workflows/ruby.yml

name: Run Rspec Tests

on:
  pull_request:
    branches:
    - master

jobs:
  build:

    runs-on: ubuntu-18.04

    services:
      postgres:
        image: postgres:10.11
        ports: ["5432:5432"]
        options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5

    steps:
    - uses: actions/checkout@v1

    - name: Cache Ruby
      uses: actions/cache@preview
      id: cache
      with:
        path: ~/local/rubies
        key: ruby-2.5.7

    - name: Set up Ruby 2.5.7
      uses: clupprich/ruby-build-action@master
      id: ruby
      with:
        ruby-version: 2.5.7
        cache-available: ${{ steps.cache.outputs.cache-hit == 'true' }}

    - name: Print ruby version
      run: ${{ steps.ruby.outputs.ruby-path }} --version

    - name: Install PostgreSQL 10
      run: |
        sudo apt-get -yqq install libpq-dev

    - name: Setup Database
      env:
        PGHOST: localhost
        PGUSER: postgres
        RAILS_ENV: test
      run: |
        gem install bundler -v 1.17.3
        bundle install --jobs 4 --retry 3
        bin/rails db:create db:schema:load

    - name: Build and test with rspec
      env:
        PGHOST: localhost
        PGUSER: postgres
        RAILS_ENV: test
      run: |
        gem install bundler -v 1.17.3
        bundle install --jobs 4 --retry 3
        bundle exec rspec spec

Some Observations

The editor is… wonky and annoying but until I have a good grasp of what I want to do, I’ll stick with it. It kept getting in my way as I typed and copy/pasted steps. Also, all the documentation is right there in the sidebar along with the full docs here.

I like that with GitHub Actions I don’t have to jump to another tool or service to run tests and view results. I like that the workflows are right there alongside and versioned with my code. It feels much less clunky for my brain and for the actual setup without that extra jump to another tool like Circle CI.

There is a marketplace for actions others have already written. But as you can see by searching for slack, there’s a boatload of actions to choose from, so who knows about that quality! There are some requirements to getting into the Marketplace and getting verified, but the first couple I tried out were hard to flaky.

Whether or not you choose to use GitHub Actions in your project probably also depends on what other tools you have in your toolbox. If you’re already using Heroku Pipelines with CI, then this approach might not make any more sense.

What’s next?

If you’re confident enough in how your codebase is tested, the next step might be to do automatic deploys to your app when a pull request is merged to master. Or maybe you’ll send a Slack notification when a build starts, succeeds, or fails!