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.


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.