I recently had to configure Github Actions to execute tests in a Rails project that uses MySQL. At first glance, it seemed like a reasonably easy task, but I ran across some issues when connecting to the database with errors that looked like:

Mysql2::Error::ConnectionError: Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' (2)

or:

Mysql2::Error::ConnectionError: Access denied for user 'root'@'localhost' (using password: NO)

Here’s how I solved it. Github provides a mysql service. The way to configure it is the following:

   services:
      mysql:
        image: mysql:5.7
        env:
          MYSQL_ALLOW_EMPTY_PASSWORD: yes
          MYSQL_DATABASE: your-db_dev
        ports:
          - 3306
        options: >-
          --health-cmd="mysqladmin ping"
          --health-interval=10s
          --health-timeout=5s
          --health-retries=3

We will use MySQL 5.7. We can see some interesting bits. MYSQL_ALLOW_EMPTY_PASSWORD: yes tells MySQL that we don’t need to specify a password. You can use a password here, although I don’t know what the default is in this case (maybe password?). For testing, I think it’s usually fine to use this option. MYSQL_DATABASE specifies the name of your testing database. This needs to match the one declared in your database.yml file.

After that, we will specify the steps we want to run. In my case, they look like this:

    steps:
    - uses: actions/checkout@v2
    - name: Set up Ruby 2.6
      uses: actions/setup-ruby@v1
      with:
        ruby-version: 2.6
    - name: Install dependencies
      run: |
        gem install bundler
        bundle install --jobs 4 --retry 3
    - name: Run Tests
      run: |
        sudo service mysql start
        bundle exec rails db:prepare
        bundle exec rails test
      env:
        DB_PORT: 3306 
        RAILS_ENV: test

Nothing surprising here. We first fetch the code, then we set up Ruby, and we install the dependencies. Then, we start the MySQL service with sudo service mysql start. This command is very important. Otherwise, you will get one of the errors mentioned above.

This is how my file looks like:

name: CI

on:
  push:
    branches: [ master ]
  pull_request:
    branches: [ master ]
  workflow_dispatch:

jobs:
  test:
    runs-on: ubuntu-latest

    services:
      mysql:
        image: mysql:5.7
        env:
          MYSQL_ALLOW_EMPTY_PASSWORD: yes
          MYSQL_DATABASE: your-db_dev
        ports:
          - 3306
        options: >-
          --health-cmd="mysqladmin ping"
          --health-interval=10s
          --health-timeout=5s
          --health-retries=3
    steps:
    - uses: actions/checkout@v2
    - name: Set up Ruby 2.6
      uses: actions/setup-ruby@v1
      with:
        ruby-version: 2.6
    - name: Install dependencies
      run: |
        gem install bundler
        bundle install --jobs 4 --retry 3
    - name: Run Tests
      run: |
        sudo service mysql start
        bundle exec rails db:prepare
        bundle exec rails test
      env:
        DB_PORT: 3306
        RAILS_ENV: test

Last but not least, this is how my database.yml file looks like:

default: &default
  adapter: mysql2
  collation: utf8mb4_unicode_ci
  encoding: utf8mb4

development: &development
  <<: *default
  username: root
  database: your-db_dev

test:
  <<: *development
  database: your-db_test
  <% if ENV['CI'] %>
  host: 127.0.0.1
  port: <%= ENV['DB_PORT'] %>
  <% end %>

production: &production
  <<: *default
  url: <%= ENV['DATABASE_URL'] %>

The interesing part here is found in the test configuration. If the environment is CI, we connect to the host 127.0.0.1 and the port available in the variable DB_PORT.

Hope this was useful!

Alejandro 👾.