【CircleCI】CircleCIの自動テストでハマった（rails + mysql）【メモ】

#はじめに
CircleCIを使ってRailsアプリの自動テストをやろうとしてハマったのでメモ。
特にmysqlコンテナとの接続が上手くいきませんでした。
各種設定ファイルの内容や、エラーへの対策を備忘録として残すことに。

#環境
```
CircleCI  2.1
Bundler  2.1.4
Rails 6.0.3.4
ruby 2.6.5
MySQL 5.7
```
開発環境はDocker-composeで構築。
サービス名とコンテナ名の関係は以下のよう。

| サービス名 | コンテナ名 |
|:--------:|:---------:|
| web     | webapp_web_1      |
| db   | webapp_db_1    |
| app     | webapp_app_1      |

#設定ファイル
アプリ名はwebappとする。
## database.ymlとdb.env
データベースの設定ファイル。
この設定がCircleCI側で上手く使えなかったので、今回は以下の記事を参考にCircleCI用のデータベース設定ファイルを作成することにしました。
よって、ここの設定はCircleCIとは関係ないです。
 [CircleCI 2.0の設定メモ](https://qiita.com/toyoken/items/16f3b6df06bbe393e644#mysql%E3%81%A8%E3%81%AE%E7%96%8E%E9%80%9A%E8%A8%AD%E5%AE%9A%E3%81%AE%E5%89%8D%E3%81%AB "CircleCI 2.0の設定メモ")

```config/database.yml
default: &default
  adapter: mysql2
  encoding: utf8
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: <%= ENV.fetch('MYSQL_USER') { 'root' } %>
  password: <%= ENV.fetch('MYSQL_PASSWORD') { 'rootpass' } %>
  port: 3306
  host: db
  
development:
  <<: *default
  database: webapp_development
    
test:
  <<: *default
  database: webapp_test
```

```environments/db.env
MYSQL_ROOT_USER=root
MYSQL_ROOT_PASSWORD=rootpass
MYSQL_USER=kagamiya
MYSQL_PASSWORD=kagamiya
```
ポートの設定は要らないかも。
host: dbはDocker-composeのサービス名です。
ENV.fetchの{ 'root' }とかも要らないと思います。

##database.yml.ci
CI用のデータベース設定ファイル。
CirclCIでの自動テストではこちらを使います。

```yaml:config/database.yml.ci
test:
  adapter: mysql2
  encoding: utf8
  pool: 5
  username: 'root'
  port: 3306
  host: '127.0.0.1'
  database: webapp_test
```

##config.yml
肝心のCircleCIの設定ファイル。
参考はこちら。
[CircleCIでテストを自動化](https://bbbbruno.qrunch.io/entries/u3ohhiRLw0tP459I)

```.circleci/config.yml
version: 2.1 # バージョン指定  

executors:  
  default:  
    working_directory: ~/webapp  
    docker:  
      - image: circleci/ruby:2.6.5-node
        environment:  
          BUNDLER_VERSION: 2.1.4
          RAILS_ENV: test
          DB_HOST: 127.0.0.1
          DB_USERNAME: 'root'
          DB_PASSWORD: ''
      - image: circleci/mysql:5.7 
        environment:
          MYSQL_ROOT_HOST: '%'
          MYSQL_ALLOW_EMPTY_PASSWORD: 'true'

commands:  
  setup:  
    steps:  
      - checkout  
      - run:  
          name: Update bundler # bundlerのバージョン２へのアップデート  
          command: gem update bundler  

      - run:  
          name: Which bundler? # バージョン確認  
          command: bundle -v  

      - restore_cache: # キャッシュを読み込む  
          keys:  
            - gem-cache-v1-{{ checksum "Gemfile.lock" }}  
            - gem-cache-v1-  

      - run:  
          name: Bundle Install  
          command: bundle check --path vendor/bundle || bundle install --deployment  

      - save_cache: # キャッシュを保存する  
          key: gem-cache-v1-{{ checksum "Gemfile.lock" }}  
          paths:  
            - vendor/bundle  
      
      # 以下はRails6でWebpackerを使う場合は必須   
      - restore_cache:  
          keys:  
            - yarn-cache-v1-{{ checksum "yarn.lock" }}  
            - yarn-cache-v1-  

      - run:  
          name: Yarn Install  
          command: yarn install --cache-folder ~/.cache/yarn  

      - save_cache:  
          key: yarn-cache-v1-{{ checksum "yarn.lock" }}  
          paths:  
            - ~/.cache/yarn  

jobs:  
  test:  
    executor: default  
    environment:  
      RAILS_ENV: test  
    steps:  
      - checkout  
      - setup  
      - run:  
          name: Wait for DB  
          command: dockerize -wait tcp://127.0.0.1:3306 -timeout 90s  
      
      - run:
          name: Use specific database.yml # CircleCI用のデータベース設定を使う
          command: mv config/database.yml.ci config/database.yml

      - run:  
          name: Database setup  
          command: |
            bin/rails db:create
            bin/rails db:schema:load --trace  

      - run: # 普通のテストとシステムテストを実行する  
          name: Rails Test  
          command: |  
            bin/rails test  
            bin/rails test:system  

workflows:  
  build_and_test:  
    jobs:  
      - test  
```
(MySQLの8系を使う場合は記述の追加が必要なようで、5.7を使うことにしました。
また、rubyのイメージはnodeがついていないとyarnコマンドが使えませんでした。)

特にデータベース周りの設定でハマりました。
というのも、私がMySQLの仕様をあまり理解できていなかった（というか今も理解できていない）からで…
当初は参考記事にならって、

```yml:
DB_USER: 'kagamiya'
DB_PASSWORD: 'kagamiya'

MYSQL_USER: 'kagamiya'
MYSQL_PASSWORD: 'kagamiya'
```
としていました。
しかし、そもそもkagamiyaなんていうユーザーはCircleCIのMySQLイメージには登録されていません。(開発環境のdbコンテナにはもちろんありますが)
そこで必然的にrootを使うことになるので、以下のようにしました。

```yml:
DB_USER: 'root'
DB_PASSWORD: 'rootpass'

MYSQL_USER: 'root'
MYSQL_PASSWORD: 'rootpass'
```
しかし、上手くいきませんでした。
というのは、rootユーザーのデフォルトのパスワードは設定されていないからです（多分）。
そういうわけで、パスワードを空にしても上手くいきません。

```yml:
DB_USER: 'root'
DB_PASSWORD: ''

MYSQL_USER: 'root'
MYSQL_PASSWORD: ''
```
というわけで、ユーザーはroot、パスワードはMYSQL_ALLOW_EMPTY_PASSWORD: 'true'として（空を許可？）で接続しなければならないようです。

```yml:
      - image: circleci/ruby:2.6.5-node
        environment:  
          BUNDLER_VERSION: 2.1.4
          RAILS_ENV: test
          DB_HOST: 127.0.0.1
          DB_USERNAME: 'root'
          DB_PASSWORD: ''
      - image: circleci/mysql:5.7 
        environment:
          MYSQL_ALLOW_EMPTY_PASSWORD: 'true'
```
ここでまたまたエラーを吐きました。

```
Access denied for user 'root'@'127.0.0.1'
```
ここの理解に苦しんだのですが、どうやら'root'@'127.0.0.1'というユーザーはいないということのようです。
mysqlのユーザー一覧を見てみると、'root'@'%'やら'root'@'localhost'はいますが、'root'@'127.0.0.1'はいません。
mysqlのホスト名（DB_HOST）にlocalhostを使うか127.0.0.1を使うかは、また別の問題として色々あるようです。
しかしながら、今は接続することが最優先なので、rootのホストとして%を指定してやります。
(%は「全てのホスト」という意味だそうですが、よく分かりません)

```yml:
MYSQL_ROOT_HOST: '%'
MYSQL_ALLOW_EMPTY_PASSWORD: 'true'
```
理由はどうあれ、これでやっと接続できました。

次に、CircleCIのイメージでは独自のデータベース設定を使うため、database.ymlをdatabase.yml.ciで上書きします。
データベースのセットアップ前に処理を追加します。

```yml:
      - run:
          name: Use specific database.yml # CircleCI用のデータベース設定を使う
          command: mv config/database.yml.ci config/database.yml
```
私の環境では、指定したデータベース(webapp_test)が無いと怒られたので、データベースの作成を行いました。

```yml:
      - run:  
          name: Database setup  
          command: |
            bin/rails db:create
            bin/rails db:schema:load --trace  
```

（ここは次回以降の実行でエラーの原因になるかも…？）

#まとめ
MySQLの仕様とconfigの内容は理解しきれていません。
configのサンプルは多くの方がアップしておられますが、詳細まで解説されているものが少ない印象です。
よって、自分が理解できていない部分でエラーとなればまたハマる可能性が大ですね…