Rails + GitHub Actionsではまったこと


# やりたいこと
- gemのキャッシュ
- yarnライブラリのキャッシュ
- RSpecの実行
- システムスペックの実行
- システムスペック失敗時のスクショをArtifactとしてアップロード
- DBはmysql5.7


# 結論
こんなymlを作りました。

```yml:example.yml
name: RUN Rspec
on:
  pull_request:
    branches:
    - master

jobs:
  build:
    runs-on: ubuntu-latest

    services:
      mysql:
        image: mysql:5.7
        env:
          MYSQL_USER: root
          MYSQL_ALLOW_EMPTY_PASSWORD: yes
    container:
      image: ruby:2.6.4
      env:
        RAILS_ENV: test

    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: bundle install
        run: |
          gem install bundler
          bundle install --path vendor/bundle --quiet --jobs 4 --retry 3
      - name: yarn install
        run: yarn install
      - name: set database.yml
        run: cp -v config/database.ci.yml config/database.yml
      - name: db create
        run: bundle exec rails db:create db:schema:load --trace
      - name: run rspec
        run: bundle exec rspec

      - name: Archive rspec result screenshots
        if: failure()
        uses: actions/upload-artifact@v1
        with:
          name: rspec result screenshots
          path: tmp/screenshots/
```

# 以下はまったことや学んだこと

## ruby2.6.4がないと言われる

rubyのセットアップは`actions/setup-ruby@v1`を使うといくつかの記事で紹介されていましたが、2019/12/30現在、2.6系だと2.6.3しか使えないらしいです。

```yml
    - name: Set up Ruby 2.6
      uses: actions/setup-ruby@v1
      with:
        ruby-version: 2.6.4
```

なので普通にcontainerイメージをつかうことにしました。

```yml
    container:
      image: ruby:2.6.4
```


## gemをキャッシュしたい

gemをキャッシュしたい場合はこう書けば良さそうです。

```
      - uses: actions/checkout@v1
      - uses: actions/cache@v1
        with:
          path: vendor/bundle
          key: bundle-${{ hashFiles('**/Gemfile.lock') }}
```



## yarnライブラリをキャッシュしたい
そもそもyarnを入れる必要があります。

```yml
      - 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
```


bundleと同じようにyarnもキャッシュさせます。

```yml
      - uses: actions/cache@v1
        with:
          path: node_modules
          key: yarn-${{ hashFiles('**/yarn.lock') }}
```



## mysql5.7を使いたい

```yml
    services:
      mysql:
        image: mysql:5.7
        env:
          MYSQL_USER: root
          MYSQL_ALLOW_EMPTY_PASSWORD: yes
```

datbase.ci.ymlを作ってそれをCI上でdatabase.ymlにリネームして使ってます。


```yml
      - name: set database.yml
        run: cp -v config/database.ci.yml config/database.yml
```

```yml:database.ci.yml
default: &default
  adapter: mysql2
  encoding: utf8mb4
  charset: utf8mb4
  collation: utf8mb4_general_ci
  pool: 5
  host: mysql
  port: 3306
  username: root
  password:

test:
  <<: *default
  database: circle_test
```

**host名を`mysql`にしないとうまくmysqlに接続できませんでした。**

usernameは`MYSQL_USER`部分と合わせる必要があるでしょう。
passwordは不要なので`MYSQL_ALLOW_EMPTY_PASSWORD: yes`としてます。


## test環境としてrailsコマンドを打ちたい

```yml
    container:
      image: ruby:2.6.4
      env:
        RAILS_ENV: test
```

当然ちゃ当然ですが、`bundle exec rails db:create`コマンドなんかもtest環境で行いたいので環境変数として`RAILS_ENV: test`を設定しました。


## システムスペックを動かしたい
プロジェクトでは`headless_chrome`を使ってるのですが、特に何も考えずCIを動かすとこのエラーが出ました。

```shell
Webdrivers::BrowserNotFound:
Failed to find Chrome binary.
```

これはChromeをインストールすれば解消されました。

```yml
      - 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
```

が、次のエラーが出ました。

```shell
Selenium::WebDriver::Error::UnknownError:
unknown error: Chrome failed to start: exited abnormally
(unknown error: DevToolsActivePort file doesn't exist)
(The process started from chrome location /usr/bin/google-chrome is no longer running, so ChromeDriver is assuming that Chrome has crashed.)
```

このオプションをdriven_byに追加してあげればうまく動きました。

```ruby:spec_helper.rb
driven_by :selenium, using: :headless_chrome do |driver_options|
  driver_options.add_argument('--disable-dev-sim-usage')
  driver_options.add_argument('--no-sandbox')
end
```



## システムスペックの結果をArtifactとしてアップロードしたい

```yml
      - name: Archive rspec result screenshots
        if: failure()
        uses: actions/upload-artifact@v1
        with:
          name: rspec result screenshots
          path: tmp/screenshots/
```


システムスペックが成功すると `tmp/screenshots/` が作られないので no such file or directoryのエラーがでます。なので `if: failure()` を書いて『直前のジョブが失敗したら=RSpecが失敗したら』という条件を課しています。ただ、システムスペックは通ったけどモデルスペックは通らなかった、という場合でも動いてしまうはずなので何か他の方法をとる必要があると思われます。


CircleCIに比べてちょっと遅い気がしますが今後改善されていくことを期待します。
GitHub上で全て完結するのはとても良いですね。



今回作ったものはこちらです
https://github.com/DaichiSaito/github-action-example