はじめに
私は現在、通っているプログラミングスクールの卒業制作としてアプリを作成しています。
その実装の一環でGithub Actionsを用いたHerokuへの自動デプロイとCIテスト機能を実装しました。
私自身が初学者であり、かつ初めての技術記事を投稿するので、拙い箇所や間違っているところがあるかもしれません。もし指摘等ありましたらご連絡ください。
環境
Ruby 3.3.6
Ruby on Rails7.2.2
Docker/docker-compose
GitHub Actions
MySQL
Heroku/Cloudflare
Github Actionsとは
- 公式
ここでは簡単に解説すると、開発中の本番環境へのデプロイやRubocop・Rspecなどのテストといった作業を自動で実行してくれる機能です。
これらはGitHubのリポジトリに特定のアクションを登録しておくことで実行できます。
私は今回、以下の2つの機能を実装しようと思っています。
- プルリクエストを作成した段階でRubocop・rspecのテストを実行
- mainブランチにプッシュした際、本番環境(Heroku)へ自動プッシュを実行
以下に、実装までの手順やコード解説、つまづいた箇所の解決までの経緯を記述します。
ワークフロー
ワークフローとは、複数のジョブからなる設定可能な自動化プロセスのことで、これを用いることで様々な動作を実装します。
- 簡単な解説
- name:ワークフロー名
- on: トリガーイベント
- job: 実行されるジョブ一連の流れをひとまとめにしたもの
- steps: ジョブ内での流れをステップごとにまとめたもの
etc...
・・・正直、この辺りはたくさんありすぎて書ききれないので、残りはこちらの公式や他の方の記事などで確認してください。
実装手順
GitHub上でのセットアップ
- 対象のリポジトリの[Settings]にアクセス
| - 左サイドバーのSecurityという項目内にある[Secrets and variables]から、[Actions]を選択
| - [New repository secret]ボタンをクリックし、HEROKU_API_KEYとHEROKU_EMAILを設定
この時登録するHEROKU_API_KEYとHEROKU_EMAILは以下の手順で確認できます。
- Herokuにログインして[Account Settings]→[API Key]セクションまでスクロール→[Reveal]ボタンをクリックすると表示されるものを取得
- 上記の流れで[Account Settings]のprofileにあるEmail Addressを取得
ファイル作成
.github/workflows/の配下に作成します。
ci.ymlはコード品質を保つためのテストを実行する機能。デプロイする機能は新たにdeploy.ymlを作成します。
Herokuへ自動デプロイ
name: Deploy to heroku
# mainブランチにpushされた時に実行
on:
push:
branches:
- main
# 実行されるジョブ
jobs:
deploy:
# テスト環境を最新バージョンのUbuntu(Linux)に設定
runs-on: ubuntu-latest
steps:
# リポジトリのコードをチェックアウト
# ワークフローがリポジトリのコードにアクセスできる
- uses: actions/checkout@v4
# Heroku CLIをインストール
# 後述のマイグレーションファイルで使用
- name: Install Heroku CLI
run: curl https://cli-assets.heroku.com/install.sh | sh
# Herokuへのデプロイを実行
# 認証コードをGithubのSecretsから取得
- uses: akhileshns/heroku-deploy@v3.13.15
with:
heroku_api_key: ${{ secrets.HEROKU_API_KEY }}
heroku_app_name: "stockmate"
heroku_email: ${{ secrets.HEROKU_EMAIL }}
# デプロイ後にマイグレーションを実行
# Heroku CLIを利用してRailsのマイグレーションコマンドを実行
- name: Run database migrations
env:
HEROKU_API_KEY: ${{ secrets.HEROKU_API_KEY }}
run: heroku run rails db:migrate -a stockmate
CIチェック
name: CI
# プルリクエストが作成された時、またはmainブランチにpushされた時に実行
on:
pull_request:
push:
branches: [ main ]
jobs:
# Gem brakemanを使用してセキュリティの脆弱性をスキャン
scan_ruby:
runs-on: ubuntu-latest
steps:
# コードをチェックアウト
- name: Checkout code
uses: actions/checkout@v4
# Rubyをセットアップ
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: .ruby-version
bundler-cache: true
# brakemanでスキャン実行
- name: Scan for common Rails security vulnerabilities using static analysis
run: bin/brakeman --no-pager
# Rubocopを使用してコードの一貫性をチェック
lint:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: .ruby-version
# キャッシュを有効に設定
bundler-cache: true
# Rubocopによるチェックを実行
- name: Lint code for consistent style
run: bin/rubocop -f github
# アプリケーションのテストを実行
test:
runs-on: ubuntu-latest
# テスト環境を定義
services:
mysql:
image: mysql:8.0 # 使用するデータベース
# 環境変数・パスワード設定・ポート番号
# Dockerfile.devやdatabase.ymlとの関係性に注意する
env:
MYSQL_DATABASE: myapp_test # データベース名
MYSQL_PASSWORD: password # パスワード
MYSQL_ROOT_PASSWORD: password # MySQLのセキュリティ用ルートパスワード
ports: # 仮想マシン内のポート番号
# postgreSQLは5432になる
- 3306:3306
# オプション設定
options: --health-cmd="mysqladmin ping" # Mysqlが起動しているかチェック
--health-interval=10s # 10秒ごとにチェック
--health-timeout=5s # 5秒以内に完了しなければ失敗
--health-retries=3 # 最大3回はチェック
# テストを実行
steps:
# 必要なパッケージをインストール
- name: Install packages
run: sudo apt-get update && sudo apt-get install --no-install-recommends -y google-chrome-stable curl default-mysql-client libjemalloc2 libvips
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: .ruby-version
bundler-cache: true
# テスト実行
- name: Run tests
env:
RAILS_ENV: test
DATABASE_URL: mysql2://root:password@127.0.0.1:3306/myapp_test
run: |
# 空のデータベースを作成し、そこに開発環境のスキーマを取得してテスト実行
bin/rails db:test:prepare
bundle exec rspec
# 失敗時のスクリーンショットを保存
- name: Keep screenshots from failed system tests
uses: actions/upload-artifact@v4
if: failure()
with:
name: screenshots
path: ${{ github.workspace }}/tmp/screenshots
if-no-files-found: ignore
解説
bin/rails db:test:prepare
このコードは、以下の処理を行っています。
- 必要な場合にテストデータベースを作成
- development環境のスキーマ(db/schema.rb)をテストデータベースにコピー
これをさらに細かく分解します。
- 古いテストデータベースがあればを削除
- 新しいデータベースを作成
- テストデータベースを空の状態にする
- 開発環境のスキーマ情報を取得
- 取得したスキーマをテストデータベースに適応
この場におけるスキーマとは、データベースの構造のことです。
これをset up databaseというnameでstep内に定義することもできます。
- name: Set up Database
env:
RAILS_ENV: test
DATABASE_URL: mysql2://root:password@127.0.0.1:3306/myapp_test
run: |
bin/rails db:drop || true
bin/rails db:create
bin/rails db:schema:load
bin/rails db:migrate
こちらも上記と同様の流れの処理を実行しています。
set up databaseのメリットはエラーが発生した場合、どの段階でエラーが発生したかを明確に判断できるということです。
また、まだRspecの準備ができていない場合は、以下のようにRailsのシステムテストを実行するようにしておき、実装後に修正するということもできます。
- name: Run tests
env:
RAILS_ENV: test
DATABASE_URL: mysql2://root:password@127.0.0.1:3306/myapp_test # データベースの接続URL
run: |
bin/rails db:test:prepare
bin/rails test test:system # Railsのシステムテストを実行
ただし、この場合注意して欲しいことがあります。
このシステムテストはアプリ内の全てのファイルを読み取るため、不要なファイルなどが残っているとそれが原因でエラーとなります。
私の失敗談として、以下のようなエラーが出て解決に苦労しました。
Error:
StaticPagesControllerTest#test_should_get_top:
ActiveRecord::StatementInvalid: Mysql2::Error: Table 'myapp_test.installs' doesn't exist
installsというカラムが存在しないというエラーなのですが、当然そのようなカラムに覚えがありません。
メンターの方に相談したところ指摘されたのが、test/fixtures/installs.ymlというファイルが存在していて、それがエラーの原因になっていルのではとのことでした。
このように、予想外のところからエラーが飛び出してくるので気をつけましょう。
まとめ
今回の実装は公式や他の方の記事、Githubのコードを参照しながら作成しました。
失敗も多かったですが学びもあり、とても有意義なものだったと思います。
自身がメンターの方に助けてもらったように、この記事が誰かの助けになればさいわいです。
ここまで読んでくださり、ありがとうございました。
参考記事