【GitHub Actions・Rails】NoMethodError: Cannot load database configuration: undefined method `[]' for nil:NilClass

# 0. 問題

`rails db:create` または `rails db:migrate` が途中で失敗してしまう。
エラーログは以下のような、ちょっと根深そうな感じ。

```
$ bundle exec rake db:create
rake aborted!
NoMethodError: Cannot load database configuration:
undefined method `[]' for nil:NilClass
(erb):20:in `<main>'
hoge/fuga/app/models/application_record.rb:1:in `<main>'
...
/opt/hostedtoolcache/Ruby/2.7.2/x64/bin/bundle:23:in `load'
/opt/hostedtoolcache/Ruby/2.7.2/x64/bin/bundle:23:in `<main>'
Tasks: TOP => db:create => db:load_config => environment
(See full trace by running task with --trace)
Error: Process completed with exit code 1.
```

application_record.rbの1行目で発生しているのでどういうことなのか、、

# 1. 原因

ここら辺を参考。

- [何度やってもNill class、原因不明のmigrate faild、問題はcredentials.yml.encが握っていた。
](https://qiita.com/Nedward/items/d9da3867af6d50e4213e)
- [【Rails】rails sを実行すると「Cannot load database configuration」とエラーが出て強制終了される
](https://teratail.com/questions/295996)
- [cannot load 'rails.application.database_configuration' undefined method'[]' for nil:NilClass](https://stackoverflow.com/questions/28698108/cannot-load-rails-application-database-configuration-undefined-method-for)

要は、例えば以下の場合、

```erb
<%= Rails.application.credentials.database[:host] %>
```

のようにcredentials.yml.encの秘匿情報を呼び出している記述で、
`Rails.application.credentials.database` が空になってしまっているため、
`[:host]` が呼び出せず、
`undefined method '[]' for nil:NilClass` が発生してしまう。

`Rails.application.credentials.database` が空になってしまうのは~~環境依存の問題で、
GitHub Actions上のUbuntu仮想環境であるせいで credentials.yml.enc を見れない。~~
GitHub Actions側で秘匿情報の設定をする必要があったため。

# 2. 解決策

今回の例では、config/database.ymlで定義された
` Rails.application.credentials.database[:host]` 
を呼べるようにするか、呼ばなければ良い。

以下のいずれかを適用すれば通るようになる。

## 解決策1. GitHubリポジトリ上で `RAILS_MASTER_KEY` を設定する

GitHub Actionsを設定している、GitHubリポジトリ上で
`Settings > Secrets` で、リポジトリに秘匿情報を設定することができます。

![スクリーンショット 2020-11-23 17.07.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/54467/34713aa0-9dbf-73b1-eb5b-48e026c90b39.png)


 `config/master.key` の値を入力します（改行やスペースを含めてしまうとうまくいかないかもしれません）。

![スクリーンショット 2020-11-23 17.08.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/54467/7e59eda4-86a1-fa02-feab-75db1872ff22.png)


![スクリーンショット 2020-11-23 17.09.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/54467/0918fe4f-77a5-2cf1-1c09-6a66a785cbb0.png)


上記で設定した秘匿情報は、 `${{ secrets.設定した名前 }}` として、GitHub Actionsの設定ファイルから呼び出すことができます。

```yml
# 例
...
    - name: Run tests
      env:
        RAILS_ENV: test
        RAILS_MASTER_KEY: ${{ secrets.RAILS_MASTER_KEY }}
      run: bundle exec rspec
...
```

これにより、GitHub Actionsの環境上で、 `Rails.application.credentials` を見ることができるようになります。

### 参考

- [GitHub ActionでDockerコンテナをビルドしてAmazon ECRに保存する](https://dev.classmethod.jp/articles/github-action-ecr-push/)

## 解決策2. database.yml.ci を作成・使用する

```yml
# .github/workflows/ruby.yml
...
    - name: Setup test database
      env:
        RAILS_ENV: test
      run: |
        cp config/database.yml.ci config/database.yml
        bundle exec rake db:create
        bundle exec rake db:migrate
...
```

```yml
# config/database.yml.ci
default: &default
  adapter: mysql2
  encoding: utf8mb4
  charset: utf8mb4
  collation: utf8mb4_general_ci
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  host: 127.0.0.1
  port: 3306
  username: root
  password:

test:
  <<: *default
  database: sample_app_test

```

## 解決策3. Rails.application.credentials.hoge[:fuga]を呼ばない

ymlファイルでもif文を書ける。

```yml
# config/database.yml
...
production:
  <<: *default
  <% if Rails.application.credentials.database.present? %>
    database: sample_app_production
    host: <%= Rails.application.credentials.database[:host] %>
    username: <%= Rails.application.credentials.database[:username] %>
    password: <%= Rails.application.credentials.database[:password] %>
  <% end %>
```

`<% if Rails.env.production? %>` でも良いかも。

# 3. GitHub Actions設定ファイル

一旦こんな感じになりました。

```yml
# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.
# This workflow will download a prebuilt Ruby version, install dependencies and run tests with Rake
# For more information see: https://github.com/marketplace/actions/setup-ruby-jruby-and-truffleruby

name: Rails Tests

on: push

jobs:
  test:
    runs-on: ubuntu-latest

    services:
      mysql:
        image: mysql:5.7
        env:
          MYSQL_USER: root
          MYSQL_ALLOW_EMPTY_PASSWORD: yes
          BIND-ADDRESS: 0.0.0.0
        ports:
        - 3306:3306
        options: >-
          --health-cmd "mysqladmin ping -h localhost"
          --health-interval 20s
          --health-timeout 10s
          --health-retries 10

    steps:
    - name: Checkout code
      uses: actions/checkout@v2

    - name: Set up Ruby 2.7
      uses: actions/setup-ruby@v1
      with:
        ruby-version: 2.7

    - name: Cache gems
      uses: actions/cache@preview
      with:
        path: vendor/bundle
        key: ${{ runner.os }}-gem-${{ hashFiles('**/Gemfile.lock') }}
        restore-keys: |
          ${{ runner.os }}-gem-

    - name: Cache node modules
      uses: actions/cache@preview
      with:
        path: node_modules
        key: ${{ runner.os }}-node-${{ hashFiles('**/yarn.lock') }}
        restore-keys: |
          ${{ runner.os }}-node-

    - name: bundle install
      run: |
        gem install bundler
        bundle install --jobs 4 --retry 3 --path vendor/bundle

    - name: yarn install
      run:  yarn install --check-files

    - name: Setup test database
      env:
        RAILS_ENV: test
      run: |
        cp config/database.yml.ci config/database.yml
        bundle exec rake db:setup

    - name: Run Lint
      run: bundle exec rubocop

    - name: Run tests
      env:
        RAILS_ENV: test
        RAILS_MASTER_KEY: ${{ secrets.RAILS_MASTER_KEY }}
      run: bundle exec rspec

```

参考になれば幸いです。