CircleCIによるRailsのCI

# はじめに

CircleCIを使用してRailsのサンプルプロジェクトのCIをしてみました。
古い記事だとOrbを使っていなかったり、イメージが古かったりすることがありますが、本記事では現在CircleCI公式が出している情報に基づいて設定し、動作確認まで取れたものを記載します。

# 環境

- Ruby:2.7.2
- Rails:6.1
- DB:PostgreSQL

# CircleCIのRailsデモプロジェクトの動作確認

CircleCI公式が用意してくれている[デモ用のリポジトリ](https://github.com/CircleCI-Public/circleci-demo-ruby-rails)があるので、まずはそれをforkして動作確認してみます。

以下の通り、Rubyバージョンの修正は必要だったものの、それだけで正常に動作することを確認できました。

- CircleCIによるCIが成功した画像
  ![2022-05-21-17-57-59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80955/fd6c1b8b-71f1-cd82-c483-a5e9a0b7916e.png)


- 修正内容

https://github.com/inayuky/circleci-demo-ruby-rails/commit/c08585acfac6436f2f6d2fda0a5f56e83c9f3b33

# 自作のサンプルプロジェクトに適用する

[公式ドキュメント](https://circleci.com/docs/ja/2.0/language-ruby/)と[デモプロジェクト](https://github.com/CircleCI-Public/circleci-demo-ruby-rails/blob/master/.circleci/config.yml)の設定を参考にして、自作のサンプルプロジェクトで動作する設定(config.yml)を作成しました。

主な修正点は以下です。
これだけでCIが正常に動作することを確認できました。

- PostgreSQLのイメージを更新
- DB名とDBのユーザ名をプロジェクトに合わせて変更
- Rubyバージョンはプロジェクトに合わせて変更
- RubocopはWarning以上のみ検知するように変更

特に設定しなくても`Insights`からテスト結果の詳細を確認できました。

![2022-05-26-10-25-09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80955/829ccbe6-e27c-fed7-2c99-85ca9588edfc.png)

動作確認済みのソースは以下の通りです。

```.circleci/config.yml
version: 2.1 # 2.1 を使うと Orb や他の機能を使用することができます。

# 設定で使用する Orb を宣言します。
# Orb に関する詳細は、https://circleci.com/docs/ja/2.0/orb-intro/をご覧ください。
orbs:
  ruby: circleci/ruby@1.8.0
  node: circleci/node@2.0.0

jobs:
  build: #  "build"という名前の最初のジョブです。
    docker:
      - image: cimg/ruby:2.7.2-node # カスタマイズされた CircleCI Docker イメージを使用します。
    steps:
      - checkout # Git コードをプルダウンします。
      - ruby/install-deps # Ruby Orb を使って依存関係をインストールします。
      # Node Orb を使ってパッケージをインストールします。
      # Yarn の使用および 依存関係のキャッシュに yarn.lock の使用を指定します。
      # 詳細は、 https://circleci.com/docs/2.0/caching/　を参照してください。
      - node/install-packages:
          pkg-manager: yarn
          cache-key: "yarn.lock"

  test:  # "test"という名前の２つ目のジョブです。
    # テストを高速化するために「並列ジョブコンテナ」を実行します。
    # これによりテストが複数のコンテナに分割されます。
    parallelism: 3
    # ここでは、2 つの Docker イメージを設定します。
    docker:
      - image: cimg/ruby:2.7.2-node # プライマリ Docker イメージです。ここでステップコマンドが実行されます。
      - image: cimg/postgres:14.2
        environment: # add POSTGRES environment variables.
          POSTGRES_USER: rails_cushion
          POSTGRES_DB: rails_cushion_test
          POSTGRES_PASSWORD: ""
    # Ruby/Rails 固有の環境変数をプライマリコンテナに適用します。
    environment:
      BUNDLE_JOBS: "3"
      BUNDLE_RETRY: "3"
      PGHOST: 127.0.0.1
      PGUSER: rails_cushion
      PGPASSWORD: ""
      RAILS_ENV: test
    # 実行する一連のステップです。「ビルド」のステップと似たステップもあります。
    steps:
      - checkout
      - ruby/install-deps
      - node/install-packages:
          pkg-manager: yarn
          cache-key: "yarn.lock"
      # ここでは、データベース上で実行する前に
      # セカンダリコンテナが起動することを確認します。
      - run:
          name: DB の待機
          command: dockerize -wait tcp://localhost:5432 -timeout 1m
      - run:
          name: データベースのセットアップ
          command: bundle exec rails db:schema:load --trace
      # RSpec を並列実行します。
      - ruby/rspec-test
      - run:
          name: RuboCop
          command: bundle exec rubocop --fail-level W --display-only-fail-level-offenses

workflows:
  version: 2
  build_and_test:     # ワークフローの名前は "build_and_test" です。
    jobs:             # このワークフローの一部として実行するジョブのリストです。
      - build         # まず、ビルドを実行します。
      - test:         # 次に、テストを実行します。
          requires:   # テストを実行するにはビルドを渡す必要があります。
            - build
```

動作確認に使用したRailsのサンプルリポジトリは以下です。(Dockerは使っていません)

https://github.com/inayuky/rails-cushion


# 設定の解説

以下、今回使用したCircleCIの設定について少し解説しておきます。

## Orb

```yml
orbs:
  ruby: circleci/ruby@1.8.0
  node: circleci/node@2.0.0
```

- Orbとは特定の機能がパッケージ化されたもので、たとえばRubyのOrbにはBundlerやRspecなどの機能が含まれています。
- 各Orbの詳細は[ここ](https://circleci.com/developer/ja/orbs)から検索して確認できます。たとえば、[RubyのOrb](https://circleci.com/developer/ja/orbs/orb/circleci/ruby)だと`install-deps`というコマンドが含まれていて、`ruby/install-deps`とすることで、Bundlerによってgemをインストールできます。
- よく使う共通機能はOrbにまとまっていることが多いので、Orbの活用により設定を簡潔に記載することができます。
- 自身で実装すると複雑で長くなりがちなキャッシュや並列実行の処理が既に実装されているケースが多いので、Orbにある機能は積極的に使った方がいいと思っています。

## image

```yml
    docker:
      - image: cimg/ruby:2.7.2-node # プライマリ Docker イメージです。ここでステップコマンドが実行されます。
      - image: cimg/postgres:14.2
```

- 基本的にCIはDockerコンテナ上で実行されるので、そのイメージを指定します。
- `steps`に記載した処理は最初に指定したイメージ上で実行されます。今回の[PostgreSQLのイメージ](https://circleci.com/developer/ja/images/image/cimg/postgres)のように２番目に指定して使用することが前提となっているイメージも存在します。
- 最近はcimgというプレフィックスがあるものを使用します。(circleciというプレフィックスは2021年末にサポート終了したらしい)
- imageは[ここ](https://circleci.com/developer/ja/images)から検索できます。

## 並列実行

```yml
    # テストを高速化するために「並列ジョブコンテナ」を実行します。
    # これによりテストが複数のコンテナに分割されます。
    parallelism: 3
```

- 複数のコンテナを起動し、実行するテストをそれらに分割して割り当てることで高速化を実現する機能です。
- 以前は有料版でしか使えなかったはずですが、[今年2022年から無料版がアップデート](https://circleci.com/ja/blog/new-cicd-free-plan-what-devs-get/)されて、30ジョブまで並列実行できるようになったようです。
- 機能を使用するには本来タイミングデータ等を使用してうまくテストを分割する必要がありますが、Orbs内のRspecには既にその処理が組み込まれているため、自動で分割されるようになります。

## ジョブの実行順序

```yml
workflows:
  version: 2
  build_and_test:     # ワークフローの名前は "build_and_test" です。
    jobs:             # このワークフローの一部として実行するジョブのリストです。
      - build         # まず、ビルドを実行します。
      - test:         # 次に、テストを実行します。
          requires:   # テストを実行するにはビルドを渡す必要があります。
            - build
```

- デフォルトでは複数のジョブは並列実行されるため、実行順序を指定したい場合は`requires`を使用します。
- 今回の設定例では、`test`ジョブの実行を開始する前に`build`ジョブの成功が必要という意味になります。

# 参考情報

https://circleci.com/docs/ja/2.0/language-ruby/

https://qiita.com/dodonki1223/items/98dbdac6f31f9b486ecf