Troubleshooting GitHub Actions with Rails and MySQL

Troubleshooting GitHub Actions with Rails and MySQL

Need help executing a GitHub Actions workflow for your Rails application, especially when dealing with a MYSQL database? Whether you’re just starting or transitioning from another CI service, navigating potential pitfalls can be challenging. If you’ve found yourself nodding along, then this blog post is tailored just for you.

Introduction

In this post, we will guide you through the process of debugging a GitHub Actions workflow. The workflow is designed to meet the following requirements:

  • Trigger the workflow when a pull request targeting the main branch is opened or updated.
  • Provision a MySQL 5.7 database.
  • Perform a bundle install for dependencies.
  • Execute RuboCop for code linting.
  • Run RSpec specs.

We’ll walk you through our step-by-step debugging process that ultimately led to the successful execution of the workflow. The aim is to share this debugging journey in the hope that it might inspire new ideas for those currently grappling with their workflows. If you’re eager to jump ahead and check out the result, feel free to review the workflow below.

name: CI

on:
  pull_request:
    branches:
      - main
  push:
    branches:
      - main
  workflow_dispatch:

jobs:
  main:
    name: Lint and Test
    runs-on: ubuntu-latest

    services:
      mysql:
        image: mysql:5.7
        ports:
          - 3306:3306
        env:
          MYSQL_DATABASE: app_test_db
          MYSQL_ALLOW_EMPTY_PASSWORD: true
        options: >-
          --health-cmd "mysqladmin ping"
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
    steps:
      - uses: actions/checkout@v4
      - uses: ruby/setup-ruby@v1
        with:
          # ruby-version: '2.7.8' # Not needed with a .ruby-version file
          bundler-cache: true

      - name: Lint
        run: bundle exec rubocop -c .rubocop_with_todo.yml

      - name: Setup DB, Run tests
        env:
          RAILS_ENV: test
          APP_DB_HOST: 127.0.0.1
        run: |
          bin/rails db:create db:schema:load
          bundle exec rspec --tag=~broken

1. Can’t connect to local MySQL server through socket

We started with an empty workflow. We compiled our workflow by referring to examples found online that utilized MYSQL 5.7. One such example suggested that we use the APP_DB_HOST MySQL environment variable, by setting it to localhost.

Upon running CI for the first time, we immediately encountered an error message

Mysql2::Error::ConnectionError: Can’t connect to local MySQL server through socket

Our google search opens a new window suggested that we might need to start the MySQL service. So we modified the workflow by adding the following configuration:

run: |
  sudo service mysql start
  bin/rails db:create db:schema:load
  bundle exec rspec --tag=~broken # exclude broken specs

Access denied for user ‘root’@’localhost’

Starting the MySQL service appeared to be the correct adjustment, as the error message we initially encountered evolved into:

Access denied for user ‘root’@’localhost’ (using password: NO)Please provide the root password for your MySQL installation

At this point, we shifted our debugging strategy by exploring open-source repositories that employ the same dependencies in their CI workflows. We consider this practice essential for moments when you need hints or ideas to overcome challenges during debugging.

We stumbled upon this example opens a new window demonstrating GitHub Actions with Rails and MySQL. Learning from this example, we discovered that there was no need to provide a MySQL user password. Therefore, it puzzled us when we encountered the ‘access denied for user’ error.

We experimented with various user and password combinations, including providing root user credentials and attempting to proceed without any user or password. Unfortunately, none of our attempts proved successful. Although altering the user and password did result in different error messages, the issue persisted. An example of one such error message is:

mysql Access denied for user ‘user_test’@’localhost’ (using password: YES)

We discovered that exploring not only GitHub repositories but also GitHub gists can be beneficial when tackling complex problems. In particular this gist opens a new window caught our attention, which modified the configuration impacting user permissions used by the MySQL daemon.

Adjusting the daemon configuration proves valuable when running a local instance of MySQL. However, in our case, we were utilizing a MySQL Docker image that inherently configured the correct user permissions

Reviewing source code

As mentioned earlier, our setup involved using a MySQL Docker image. In an effort to gain more insight, we delved into the source code of the Docker image. Our aim was to identify the available environment variables and, if lucky, uncover clues on accessing the MySQL database over the containerized network.

If you find yourself in a similar predicament, we highly recommend reviewing the MYSQL image configurations opens a new window for GitHub Actions.

After reviewing the configuration, the available environment variables and the impact of setting or not setting them became crystal clear.

Once again, we experimented with various combinations of environment variables, drawing inspiration from the insights gained in the source code. Unfortunately, none of the following options yielded success:

mysql:
  image: mysql:5.7
  ports:
    - 3306
  env:
    MYSQL_DATABASE: app_test_db
    MYSQL_ROOT_PASSWORD: password
    # MYSQL_ALLOW_EMPTY_PASSWORD: true
    # MYSQL_USER: user_test
    # MYSQL_PASSWORD: root
    # MYSQL_ROOT_PASSWORD: root
    # MYSQL_RANDOM_ROOT_PASSWORD: 'true'

At this stage, we hypothesized that the user lacked the necessary database permissions to access the database, given our exhaustive attempts with every conceivable combination of usernames and passwords

Even though our assumption was invalid it was supported by this Stack Overflow post opens a new window . This experience emphasizes the importance of caution when relying on assumptions, even when supported by online information.

Misleading information might make you think that you need to configure database permission.

undefined method ‘strip’ for nil:NilClass

As modifying user permissions and credentials failed to produce the desired outcome, we redirected our attention to the second part of the error message. When confronted with a persistent error, exploring different aspects of the error message might offer valuable insights.

Given the cryptic nature of the error, we delved into the Rails source code. Specifically, we examined the file defining MySQL database tasks opens a new window . Our focus on this file stemmed from the fact that the rake db:create task was failing to complete during the initialization of the workflow container.

From our analysis of the Rails source code, we concluded that the strip command was executed after the database connection failure. Take note of this section opens a new window , where the create task triggers the strip instruction only when the database connection fails and the user is not the root user.

Unknown MySQL server host ‘mysql’

Drawing from our observations in the Rails source code, we turned our attention to the possibility of a container network issue.

While investigating potential database connection problems within the Docker container, we came across an article opens a new window suggesting that updating the host name to match the service name defined in the workflow might be a solution.

Accordingly, we modified the DB_HOST environment variable to align with the service name we defined as mysql. Unfortunately, this adjustment led to a new error:

Mysql2::Error::ConnectionError: Unknown MySQL server host ‘mysql’

For reference, here’s what our workflow looked like at this stage

name: CI

on:
  pull_request:
    branches:
      - master
  push:
    branches:
      - main
  workflow_dispatch:

jobs:
  main:
    name: Lint and Test
    runs-on: ubuntu-latest

    services:
      mysql:
        image: mysql:5.7
        ports:
          - 3306
        env:
          MYSQL_DATABASE: app_test_db
          MYSQL_ROOT_PASSWORD: password
        options: >-
          --health-cmd "mysqladmin ping"
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
    steps:
      - uses: actions/checkout@v4
      - uses: ruby/setup-ruby@v1
        with:
          bundler-cache: true

      - name: Setup DB, Run tests
        env:
          RAILS_ENV: test
          DB_HOST: mysql
        run: |
          sudo service mysql start
          bin/rails db:create db:schema:load
          bundle exec rspec --tag=~broken

Tip: Save yourself time and remove linting while you debug. Only run the bare essentials required to debug the workflow.

Mysql2::Error: Host ‘172.18.0.1’ is not allowed to connect to this MySQL server

Despite our efforts, the persisting errors strongly suggested a failure to establish a connection with the MySQL instance, indicating potential issues with the host or port settings. For local MySQL use with Docker, specifying 127.0.0.1 as the database host is often necessary due to internal Docker image quirks, as highlighted in this resource opens a new window .

Recognizing this, we considered applying a similar approach in our CI environment. Initially, updating the DB_HOST didn’t yield the desired outcome, prompting us to also modify the MYSQL_ROOT_HOST environment variable. However, this adjustment resulted in a new error:

Mysql2::Error: Host ‘172.18.0.1’ is not allowed to connect to this MySQL server

In a subsequent attempt, we explicitly set the port number to 3306, yet we still encountered a connection failure:

Mysql2::Error::ConnectionError: Can’t connect to MySQL server on ‘127.0.0.1:3306’”

Success 💪

Our exploration led us to a helpful online article opens a new window that explicitly demonstrated the effectiveness of specifying the port number as 3306. Interestingly, we noted that the article didn’t include a line to start the MySQL service (i.e., sudo service mysql start).

In our quest for clarity on whether the MySQL instance needed explicit startup, we discovered a key insight: GitHub includes MySQL in their Ubuntu images. This article opens a new window prompted the realization that we might inadvertently be attempting to access a running MySQL instance other than the one we intended.

GitHub includes MySQL in their Ubuntu images. opens a new window .

We initiated the MySQL instance following the guidance from this article opens a new window . However, unbeknownst to us, we unintentionally started the MySQL instance that was included in the Ubuntu image. This inadvertent action hindered our ability to access the MySQL instance specified in our action configuration.

Upon realizing our mistake, we promptly removed the line (sudo service mysql start) responsible for starting the local MySQL instance. And finally, we were able to successfully connect to the intended MySQL instance.

Conclusion

In this blog post, we embarked on a detailed exploration of the challenges encountered while setting up a GitHub Actions workflow for a Rails application with MySQL integration. We faced various problems, like errors in the workflow and issues with Docker settings and starting MySQL.

Each problem was carefully looked into and solved. By checking source code, reading online articles, and seeking help from the community, we successfully resolved each hurdle, culminating in a clear understanding of how to establish a reliable CI/CD pipeline for Rails applications with MySQL.

In navigating the complexities of GitHub Actions and MySQL integration, we’ve shared our debugging journey to offer insights and solutions. Even though this post describes an application that made use of MySQL, the same issue can arise with application that make use of PostgreSQL, since it is also included but not started by default in the ubuntu images used by Github Actions. If you find yourself grappling with similar challenges in your CI configuration, don’t hesitate to reach out.

Need to streamline your CI/CD processes? Contact us today opens a new window to propel your project forward!

Get the book