Rails on GitHub Actions チュートリアル

Github Actions 使ってますか？
まだβ版ですし本稼働させている人は多くないと思いますが、 ~~[2019年11月中旬頃にようやくキャッシュ機構が実装される](https://github.community/t5/GitHub-Actions/Caching-files-between-GitHub-Action-executions/m-p/30974/highlight/true#M630)そうなので、 CircleCI 非課金勢としては非常に楽しみにしています。~~
宣言通り実装されたので追記しました！

Rails の CI を Github Actions で動かすにあたって、システムテスト周りで少しハマったので、導入方法をまとめてみました。

## Rails プロジェクトの初期化

いつも通り rails new していきます。
今回は Rails 標準の minitest を使っていますが RSpec の場合でもほぼ変わりません。

```bash
$ mkdir rails_on_github_actions
$ bundle init
```

```ruby:Gemfile
# frozen_string_literal: true
source "https://rubygems.org"
git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
gem "rails"
```

```
$ bundle
$ bundle exec rails new .
$ bin/rails db:migrate
```

テストをするために user リソースを追加しておきます。

```
$ bin/rails g scaffold users
$ bin/rails db:migrate
$ bin/rails test
```

## Workflow の作成

Github Actions でテストを走らせるために、 workflow を作っていきます。
scaffold によってシステムテストも作られていますが、一旦ここではブラウザを使わないテストだけ扱います。

```yaml:.github/workflows/test.yml
name: Test
on: [push]
jobs:
  build:
    runs-on: ubuntu-latest
    container:
      image: ruby:2.6.4
    steps:
    - uses: actions/checkout@v1
    - name: Set up node and yarn
      run: |
        curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -
        echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list
        curl -sL https://deb.nodesource.com/setup_12.x | bash -
        apt install -y nodejs yarn
    - name: Build and setup
      run: |
        bundle -j 4
        bin/rails yarn:install db:setup assets:precompile
        bin/rails test
```

これを push すると workflow が自動的に走ります。

## System Test

Github Actions で system test を実行するため、 Chrome をインストールするスクリプトを workflow に追加しています。

```yaml:.github/workflows/test.yml
name: Test
on: [push]
jobs:
  build:
    runs-on: ubuntu-latest
    container:
      image: ruby:2.6.4
    steps:
    - uses: actions/checkout@v1
    - name: Set up YARN and NodeJS
      run: |
        curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -
        echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list
        curl -sL https://deb.nodesource.com/setup_12.x | bash -
        apt install -y yarn nodejs
    - name: Install chrome
      run: |
        wget -q -O - https://dl.google.com/linux/linux_signing_key.pub | apt-key add -
        echo 'deb http://dl.google.com/linux/chrome/deb/ stable main' | tee /etc/apt/sources.list.d/google-chrome.list
        apt update -y
        apt install -y google-chrome-stable
    - name: Build
      run: |
        bundle -j 4
        bin/rails yarn:install db:setup assets:precompile
    - name: Run test
      run: |
        bin/rails test
    - name: Run system test
      run: |
        bin/rails test:system
```

ローカル環境なら `driven_by :selenium_chrome_headless` だけで Headless Chrome を使ってテストできますが、 Github Actions のような Docker 環境だと `/dev/shm` のサイズ割り当てが小さいため、それを使わないようにするため `disable-dev-shm-usage` という起動オプションを追加しています。また、 `no-sandbox` は Docker 環境のように root 権限で Chrome を立ち上げるのに必要なオプションです。

```ruby:test/application_system_test_case.rb
require "test_helper"

class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
  driven_by :selenium, using: :headless_chrome, screen_size: [1400, 1400] do |driver_options|
    driver_options.add_argument('--disable-dev-shm-usage')
    driver_options.add_argument('--no-sandbox')
  end
end
```

これを push すると Github Actions で system test も無事に成功します。

## MySQL

実際のアプリケーションでは SQLite を使うことはほぼ無いと思うので、 Github Actions でも MySQL を使ってみます。

Gemfile に mysql2 を追加

```ruby:Gemfile
gem 'mysql2'
```

database.yml を MySQL 用に更新します。ほぼデフォルトですが、ホスト名とパスワードを環境変数で設定できるようにしています。

```yaml:config/database.yml
default: &default
  adapter: mysql2
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: root
  charset: utf8mb4
  collation: utf8mb4_bin
  encoding: utf8mb4
  password: <%= ENV.fetch('MYSQL_ROOT_PASSWORD', '') %>
  host: <%= ENV.fetch('MYSQL_HOST', 'localhost') %>

development:
  <<: *default
  database: rails_on_github_actions_development
test:
  <<: *default
  database: rails_on_github_actions_test
production:
  <<: *default
  database: rails_on_github_actions_production
```

テストが正常に動くかローカルで確認しておきます。

```
$ bin/rails db:create
$ bin/rails test
$ bin/rails test:system
```

workflow の services に MySQL を追加します。root のパスワードは環境変数で設定します。
services で追加したコンテナのホスト名はサービス名と同じになるようです。

```yaml:.github/workflows/test.yml
name: Test
on: [push]
jobs:
  build:
    runs-on: ubuntu-latest
    services:
      mysql:
        image: mysql:5.7
        env:
          MYSQL_ROOT_PASSWORD: password
    container:
      image: ruby:2.6.4
      env:
        MYSQL_HOST: mysql
        MYSQL_ROOT_PASSWORD: password
    steps:
    - uses: actions/checkout@v1
    - name: Set up yarn and node
      run: |
        curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -
        echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list
        curl -sL https://deb.nodesource.com/setup_12.x | bash -
        apt install -y yarn nodejs
    - name: Install chrome
      run: |
        wget -q -O - https://dl.google.com/linux/linux_signing_key.pub | apt-key add -
        echo 'deb http://dl.google.com/linux/chrome/deb/ stable main' | tee /etc/apt/sources.list.d/google-chrome.list
        apt update -y
        apt install -y google-chrome-stable
    - name: Build
      run: |
        bundle -j 4
        bin/rails yarn:install db:setup assets:precompile
    - name: Run test
      run: |
        bin/rails test
    - name: Run system test
      run: |
        bin/rails test:system
```
これを push すると Github Actions で MySQL を使ったテストが無事に成功します。
services と環境変数を書き換えれば postgres などの他のデータベースも使用できるかと思います。


## キャッシュ

Actions リリース当時はキャッシュ機構が実装されておらず、自前でキャッシュ機構を用意しない限りビルド毎に gem や Node モジュールのインストールが必要でしたが、[actions/cache](https://github.com/actions/cache) が2019年11月に提供され、一般的な CI サービスと同等レベルに使えるサービスになりました。

ここでは前回の MySQL のサンプルをベースにして、Gemflie.lock と yarn.lock のハッシュ値をキャッシュのキーとして使用し、ライブラリに変更がなければ余計なダウンロードが行われないようにしてみます。

```yaml:.github/workflows/test.yml
name: Test
on: [push]
jobs:
  build:
    runs-on: ubuntu-latest
    services:
      mysql:
        image: mysql:5.7
        env:
          MYSQL_ROOT_PASSWORD: password
    container:
      image: ruby:2.6.4
      env:
        MYSQL_HOST: mysql
        MYSQL_ROOT_PASSWORD: password
    steps:
    - uses: actions/checkout@v1
    - uses: actions/cache@v1
      with:
        path: vendor/bundle
        key: bundle-${{ hashFiles('**/Gemfile.lock') }}
    - uses: actions/cache@v1
      with:
        path: node_modules
        key: yarn-${{ hashFiles('**/yarn.lock') }}
    - name: Set up yarn and node
      run: |
        curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -
        echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list
        curl -sL https://deb.nodesource.com/setup_12.x | bash -
        apt install -y yarn nodejs
    - name: Install chrome
      run: |
        wget -q -O - https://dl.google.com/linux/linux_signing_key.pub | apt-key add -
        echo 'deb http://dl.google.com/linux/chrome/deb/ stable main' | tee /etc/apt/sources.list.d/google-chrome.list
        apt update -y
        apt install -y google-chrome-stable
    - name: Build
      run: |
        bundle -j 4 --path vendor/bundle
        bin/rails yarn:install db:setup assets:precompile
    - name: Run test
      run: |
        bin/rails test
    - name: Run system test
      run: |
        bin/rails test:system
```

詳細なドキュメントはこちらになります。上限2GBってなかなか太っ腹ですね。
https://help.github.com/en/actions/automating-your-workflow-with-github-actions/caching-dependencies-to-speed-up-workflows


今回作ったサンプルプロジェクトは Github に置いてますので良かった参考にしてみてください。
https://github.com/d-mato/rails_on_github_actions