GitHub Actions: Rails + MySQL

by Carl Furrow — on  ,  , 

I recently wanted to setup GitHub Actions on one of my personal Rails 6.x projects. I realized I’d been paying for 3,000 minutes / month, and not using them on my personal accounts! Yet once I started setting up actions, I ran into a few issues getting Rails + MySQL working correctly. If you’ve had this issue, you may find this bit of info useful. I provide a light break-down the changes I had to make to each file in my github repo.

config/database.yml

Not that I use ENV.fetch('MYSQL_HOST', 'db') because locally, when testing, I have a docker service setup at hostname db that is used, but within GitHub Actions, that is not available, so I first check the MYSQL_HOST environment var, which I setup insode my ci.yml file (see below).

...snip
test:
  <<: *default
  host: <%= ENV.fetch('MYSQL_HOST', 'db') %>
  port: <%= ENV.fetch('MYSQL_PORT', 3306) %>
  database: 'my_app_test'
...snip

.github/workflows/ci.yml

This is a fairly standare workflow YAML file, but the one caveat is that you’ll see my MYSQL_* ENV vars declared twice. Once in the top-level env:, which should be shared across all jobs/services, but then again underneath the db: service. Before I’d duplicated the top-most env: vars into the db: service, MySQL would fail to start, and those healthchecks failed every single time. I do not know why this is.

Additionally, I use the local, loopback IP (127.0.0.1) for the MYSQL_HOST, as this seemed to work over using the service name db. Not sure why db is not working, but it’s not, have to use the IP.

name: Continuous integration

env:
  RUBY_VERSION: 2.7.4
  BUNDLER_VERSION: 2.2.0
  RAILS_ENV: test
  MYSQL_USERNAME: root
  MYSQL_PASSWORD: password
  MYSQL_ROOT_PASSWORD: password
  MYSQL_HOST: 127.0.0.1
  MYSQL_PORT: 3307
  MYSQL_DATABASE: my_app_test

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]
jobs:
  test:
    name: "RSpec Tests"
    runs-on: ubuntu-latest
    services:
      db:
        image: mysql:5.7
        env:
          MYSQL_USERNAME: root
          MYSQL_PASSWORD: password
          MYSQL_ROOT_PASSWORD: password
          MYSQL_HOST: 127.0.0.1
          MYSQL_PORT: 3307
          MYSQL_DATABASE: my_app_test
        ports:
          - 3307:3306
        options: >-
          --health-cmd="mysqladmin ping"
          --health-interval=10s
          --health-timeout=5s
          --health-retries=5
    steps:
      - uses: actions/checkout@v2
        with:
          fetch-depth: 1
      - name: Setup Ruby
        uses: actions/setup-ruby@v1
        with:
          ruby-version: ${{ env.RUBY_VERSION }}
      - name: Install dependencies
        run: |
          sudo apt-get -yqq install default-libmysqlclient-dev default-mysql-client
      - name: Restore bundle cache
        uses: actions/cache@v2
        with:
          path: vendor/bundle
          key: ${{ runner.os }}-gems-${{ hashFiles('**/Gemfile.lock') }}
          restore-keys: |
            ${{ runner.os }}-gems-
      - name: Bundle
        env:
          RAILS_ENV: test
        run: |
          gem install bundler:${{ env.BUNDLER_VERSION }}
          bundle config path vendor/bundle
          bundle install --jobs 4 --retry 3 --path vendor/bundle
      - name: Setup Database
        run: |
          bundle exec rake db:prepare
      - name: Run tests          
        run: bundle exec rspec

config/environment/test.rb

I also found that since I was using an older version of MySQL (5.7 versus the latest 8.x), I had to tell Rails to NOT dump the schema after running db:migrate, or else it would fail with this error:

mysqldump: Couldn't execute 

SELECT COLUMN_NAME,
       JSON_EXTRACT(HISTOGRAM, '$."number-of-buckets-specified"')
FROM information_schema.COLUMN_STATISTICS
WHERE SCHEMA_NAME = 'my_app_test' 
  AND TABLE_NAME = 'active_storage_attachments';


: Unknown table 'COLUMN_STATISTICS' in information_schema (1109)

rake aborted!
failed to execute: `mysqldump`

Please check the output above for any errors and make sure that `mysqldump` 
is installed in your PATH and has proper permissions.

You can read this Serverfault post to understand it a bit more, but my workaround of disabling the mysqldump fixes this, but the short answer was:

This is due to a new flag that is enabled by default in mysqldump 8. You can disable it by adding --column-statistics=0.

Since I cannot pass that parameter to mysqldump during rails db:prepare, simply disabling mysqldump entirely while RAILS_ENV == test will work for my purposes.

#...snip
Rails.application.configure do
  #...snip
  config.active_record.dump_schema_after_migration = false
  #...snip
end

It works!

Once I had all of the above set, my github actions began to run! Every commit to a PR would cause the “ci.yml” action to run, and it would run my rspec tests, and output failures when they happened. Lovely.


 Want to get updates in your inbox? Sign up to receive the newsletter!
Carl Furrow's photo Author

Carl Furrow

hello@carlfurrow.com

Addicted to learning and looking to master the art of solving problems through writing code, while occasionally yelling at computer screens.