GitHubActionsでRuby on RailsのRSpecテストを実行する

Ruby on RailsのRSpecテストをGitHubActionsで実行しようとしたら、そこそこ大変だったのでメモに残しておきます。

# GitHubActionsとは

GitHub上で動作するサーバレスな実行環境です。
リポジトリ内のファイルに設定を記述することで、GitHubの各種操作にトリガして任意のアクションを実行できる仕組みです。
パブリックリポジトリであれば無料で使えます。

今回はPull Requestの作成に紐づけてRSpecを実行することで、Rails用のCIにしようと思います。

# 結論

Railsを実行できるDockerイメージをDockerHubにアップロードして、DockerPullしてテストを実行するのが良さそうです。

# 環境

- Ubuntu 16.04 LTS
- Ruby on Rails 6.0
- Ruby 2.6.6

OS以外は違っても簡単に対応できます。

# Dockerイメージを作ってDockerHubにアップロードする

## Dockerfile

ベーシックな環境です。
必要なライブラリがあれば追記してください。

```dockerfile:tools/ci/Dockerfile
FROM ubuntu:16.04

SHELL ["/bin/bash", "-c"]

ENV RUBY_VERSION="2.6.6"
ENV BUNDLER_VERSION="2.1.4"
ENV DEBIAN_FRONTEND="noninteractive"
ENV PATH=/root/.rbenv/bin:/root/.rbenv/shims:$PATH

WORKDIR /app

COPY Gemfile .
COPY Gemfile.lock .

RUN set -x \
  && apt update \
  && apt install -y \
    build-essential \
    curl \
    git \
    libssl-dev \
    libreadline-dev \
    libmysqlclient-dev \
    mysql-client \
    mysql-server \
    tzdata \
    zlib1g-dev \
  # Install rbenv and ruby
  && git clone https://github.com/sstephenson/rbenv.git ~/.rbenv \
  && git clone https://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build \
  && CONFIGURE_OPTS='--disable-install-rdoc' /root/.rbenv/bin/rbenv install ${RUBY_VERSION} -s \
  && /root/.rbenv/bin/rbenv global ${RUBY_VERSION} \
  # Install bundler
  && echo 'gem: --no-rdoc --no-ri' > /root/.gemrc \
  && /root/.rbenv/shims/gem install bundler -v ${BUNDLER_VERSION} \
  # bundle install
  && /root/.rbenv/shims/bundle config set without development \
  && /root/.rbenv/shims/bundle install \
  # delete unused file
  && rm -rf /var/lib/apt/lists/* \
  && rm Gemfile Gemfile.lock
```

RUNを複数書いてDockerのレイヤを重ねるとMySQL周りで実行時にエラーが出るので1つのコマンドで全環境を構築します。

## Dockerのビルド

```bash
docker build -t utyosu/build-rails:latest . -f tools/ci/Dockerfile
```

## Dockerイメージのアップロード

DockerHubのアカウントが必要になります。
[はじめてのDockerHubリポジトリ登録](https://qiita.com/watyanabe164/items/9b2ad241a479f6ba9a70)あたりが参考になりそうです。

```bash
docker push utyosu/build-rails:latest
```

# GitHubActionsのトリガを設定する

プロジェクトのリポジトリ内にGitHubAction用のファイルを作成します。

```:.github\workflows\build.yml
name: build

# Pull Requestをトリガにする
on: [pull_request]

jobs:
  build:
    # Ubuntuの最新版をベースにする
    runs-on: ubuntu-latest

    # DockerHubからイメージをPullする
    container:
      image: utyosu/build-rails:latest

    # このブランチをcheckoutする
    steps:
    - uses: actions/checkout@v1

    # test.shを実行する
    - name: run test
      run: . tools/ci/test.sh
```

# テストスクリプト

テストしたいことを記述します。
このスクリプトが失敗するとビルド失敗になります。

```bash:tools/ci/test.sh
#!/bin/bash

# プロセスが何も起動してないので、まずはMySQLを立ち上げておく
service mysql start

# MySQLユーザの作成と権限の付与
mysql -e 'create user "user_name";'
mysql -e 'grant all on *.* to "user_name";'

export RAILS_ENV=test
bundle config set without development

# Gemをインストールする。
# 何もない状態から
bundle install

# Capistranoが動いているか確認する
# （バージョンの不一致で動かないときがあるので念の為チェックしている）
bundle exec cap -T

# Rubocopによる静的解析
bundle exec rubocop

# railsで定義されているデータベースを作成する
bundle exec rails db:create

# ridgepoleによるデータベースのスキーマ作成
# ridgepoleを使っていない人は代わりに bundle exec rails db:schema:load を実行する
bundle exec ridgepole -c config/database.yml --apply -f db/schema -E test

# RSpecの実行
bundle exec rspec
```

# 実際にやってみる

PullRequestを作成すると `Some checks haven't completed yet` と表示されます。

![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/458229/8b91bd00-064b-d0f9-89c9-fd66953b3e67.png)

DetailsをクリックするとCIの進捗状況を確認できます。

![3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/458229/22372781-0ad0-11ac-0a53-42652fe14bb1.png)

少し待っているとコンソール出力が表示されます。

![4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/458229/e8b68976-2245-3b2d-0c25-6a2d118d44fb.png)

しばらくすると完了します。

![5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/458229/74e8628c-9f8e-67d9-389c-e5b0ef77c736.png)

PullRequestに戻ると `All checks have passed` と表示されて、問題なくテストが通ったことが分かります。

![6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/458229/b7d4ff3b-07d3-0cc9-da0a-33863cd8acc5.png)

# 経緯

GitHub標準のRuby実行環境が用意されているので、最初はそれをベースにして試していました。
しかし、RailsやGemのインストールで30分以上かかってしまうので、軽めのプロジェクトにおけるCIには向いてないと思いました。
DockerHubからイメージをPullして実行する方法であれば、40秒くらいでテストの実行が開始できることが分かったのでこの方法を用いています。

# メモ

別コンテナにMySQLとか用意してコンテナ同士でやり取りする方法もあるようですが仕組みが難しすぎて実現できませんでした。
Rails環境で上手く実現できたらいいなーって思ってます。