CI/CD

inspectors removing defective products off a conveyer belt
Image by Annie Ruygt

You’ve been a model developer. You’ve placed your source code under version control and posted it to GitHub. You’ve got a suite of tests, and they run green. You’ve deployed your software to production.

That’s a lot of work. You deserve a break. Let’s automate these tasks. The goal is to only deploy changes that pass the tests that you have defined. The good news is that GitHub actions makes this easy.

To get started, place the following in .github/workflows/ci-cd.yml:

name: CI_CD
on: [push, pull_request]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3
    - uses: ruby/setup-ruby@v1
      with:
        bundler-cache: true
    - run: rake test:all
  deploy:
    runs-on: ubuntu-latest
    needs: test
    if: github.ref == 'refs/heads/main'
    steps:
    - uses: actions/checkout@v3
    - uses: superfly/flyctl-actions/setup-flyctl@master
    - run: flyctl deploy --remote-only
      env:
        FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}

In a few short lines, what the above says is:

On every put and every pull request:

  • check out your source, setup ruby, and run all your tests

  • if the tests pass, and the branch is main:

    • check out your source, setup flyctl, and deploy

Note: the above assumes that your main branch is named main. Some older GitHub repositories use master. Adjust the github.ref check as needed.

The bad news is that if you try this with pretty much the simplest Rails application, it will fail:

rails new todolist
cd todolist
bin/rails generate scaffold Todo item
bin/rails db:migrate

There are two reasons for the failures. The first is an obscure bug in Ruby. At the moment, Rails 7.1’s fix is to remove the debug gem. This will eventually work itself out.

For the moment, if you are on Ruby 3.2 or don’t actively use the debug gem, the workaround is to run the following command to do the same with your project:

bundle remove debug

The second problem is that Rails system tests launch a browser, which by default need a display, and the GitHub servers don’t have one. So the solution is to change :chrome to :headless_chrome in one file:

 require "test_helper"

 class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
-  driven_by :selenium, using: :chrome, screen_size: [1400, 1400]
+  driven_by :selenium, using: :headless_chrome, screen_size: [1400, 1400]
 end

The next step is to provide your flyctl access token to Github as a secret.

Start by running the following command:

flyctl auth token

Now you have a token you need to make it available to GitHub Actions that run against your repository. For that, there’s secrets in the repository’s settings. GitHub provides four combinations: Environment and Repository, and Secrets and Variables. Click on the green “New repository secret” button in the top left, pop our secret under the FLY_API_TOKEN name, and we are ready.

All that is left is for you to commit your changes and…

git push

You can play with this right now.

It’ll take less than 10 minutes to get your Rails application running globally.

Try Fly for free

This is just a taste of what GitHub actions can do for your application. The one thing we haven’t covered is testing your Dockerfile itself. While this can be done using docker/setup-buildx-action@v2, health checks already do this and more.