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 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 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 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 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 . 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 .
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 ,
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 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 .
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
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 prompted the realization that we might inadvertently be attempting to access a running MySQL instance other than the one we intended.
We initiated the MySQL instance following the guidance from this article . 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 to propel your project forward!