【超基礎】circleCIの設定ファイルをゼロから作りながら、何してるのかを開発エンジニアがまとめてみた

# はじめに


仕事で「**自分、CIの理解が浅いなあ**」と思うことが度々あり、
特に最近だとローカル環境ではエラーが起きず、CircleCI上でのみエラーが発生する事象に遭遇し、CIの設定ファイルで何をしてるのかに関して、ブラックボックスだったので即時対応できませんでした。

教訓として、
基本的なCIのサンプルファイルを作成し、lint(rubocop)とtest(RSpec)を実行する設定ファイルの書き方を未来の自分に向けて、噛み砕いてまとめました。


# 想定読者
- CircleCIでビルド・テスト・デプロイを自動で行う開発環境で仕事をしているが、CIの設定の中身がブラックボックスな人

![kaisya_man_bad.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1584967/363b8d53-7582-b574-22a5-94fffad20659.png)



# 目標
- CircleCIの `config.yml` ファイルを作成
  - LintツールとRSpecをCIで実行

▼図解してみた
![段落テキスト (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1584967/4904f429-909d-a955-5c98-477028ff1e44.png)


それではいってみましょう！

# 1. CircleCIの基本構成
CircleCIの設定ファイル `config.yml` は、主に以下の要素で構成されています。

- `version`: CircleCIの設定バージョン
- `jobs`: 個別のタスクを定義する場所。ビルドやテスト、デプロイなどの「タスク」をまとめてます
- `workflows`: 定義されたジョブをどういうタイミングで実行するかを指定します。複数のジョブを並列や順番に実行する場合に必要です。

### 基本的な `config.yml` の例

```yaml
version: 2.1 

jobs:
  build:  # 自由に名前を定義できる
    docker:
      - image: circleci/ruby:3.0.0
    steps:
      - checkout #コードをGitリポジトリからチェックアウト
      - run:
          name: Bundle Install
          command: bundle install # 依存パッケージをインストール
# ワークフローの定義
workflows:
  version: 2
  build_and_test: # 自由に名前を定義できる
    jobs:
      - build # ビルドジョブを実行
```

**【Jobsセクションについて】**
build ジョブでは、まずCircleCI上にRuby環境を構築します。
そして、checkoutでGitリポジトリから最新のソースコードを取得。
その後、bundle install を使ってプロジェクトに必要な依存関係を一気にインストールします。

**【Workflowsセクションについて】**
workflow を定義することで、どの順序でジョブが実行されるかを指定できます。
今回の設定では、シンプルに build ジョブのみを実行するフローです。

この基本構成にこれから追加・修正を加えていきます！

# 2.　`lint`ジョブでrubocop自動実行の設定
`rubocop`を自動実行するため`lint`ジョブを設定します。
※rubocopはRubyのコードを静的解析するツールです。

### Lint用のジョブ追加

```yaml
jobs:
  lint:
    docker:
      - image: circleci/ruby:3.0.0
    steps:
      - checkout
      - run:
          name: Bundle Install
          command: bundle install
      - run:
          name: Run RuboCop
          command: bundle exec rubocop # rubocopを実行
```
この設定では、`lint`という新しいジョブを作成し、`rubocop`を実行しています。
これで、プッシュ時にrubocopが自動実行され、コードの静的解析が行われます。


# 3. `test`ジョブでRSpec自動実行の設定
続いて、RSpecを実行するジョブ`test`を設定します。
※RSpecはRubyのテストフレームワークです。
```yaml
jobs:
  test:
    docker:
      - image: circleci/ruby:3.0.0
    steps:
      - checkout
      - run:
          name: Bundle Install
          command: bundle install
      - run:
          name: Run RSpec
          command: bundle exec rspec # RSpecを実行
```

ここでは、test というジョブを追加し、RSpecテストを実行するコマンドを定義しています。



# 4. ワークフローの設定
最後に、LintとRSpecテストを連続して実行するためのワークフローを定義します。

ワークフローの追加
```yaml
workflows:
  version: 2
  build_and_test:
    jobs:
      - lint
      - test
```
この設定では、lintのチェックが完了した後にtest(RSpec)を実行する流れにしています。
つまり、もしlintにエラーがあればそこで止まり、testは実行されない形になります。

# 完成した`config.yml`

```yaml
version: 2.1

# ジョブの定義
jobs:
  lint:
    docker:
      - image: circleci/ruby:3.0.0 # 使用するDockerイメージの指定
    steps:
      - checkout  # ソースコードのチェックアウト
      - run:
          name: Bundle Install
          command: bundle install
      - run:
          name: Run RuboCop
          command: bundle exec rubocop  # Gemの依存関係をインストール

  test:
    docker:
      - image: circleci/ruby:3.0.0
    steps:
      - checkout
      - run:
          name: Bundle Install
          command: bundle install
      - run:
          name: Run RSpec
          command: bundle exec rspec

workflows:
  version: 2
  lint_and_test:
    jobs:
      - lint
      - test

```
この設定では、2つのJobの中で、以下の`bundle install`の設定を毎回してますが、[restore_cache](https://circleci.com/docs/ja/caching/#restoring-cache)や`save_cache`を用いて、依存関係のインストールをキャッシュし、ジョブの実行時間を短縮することもできるっぽいですね。

```yaml
# 毎回のジョブで実行するのは非効率
      - run:
          name: Bundle Install
          command: bundle install
```



【キャッシュを使用して、効率化した`config.yml`】

```yaml
version: 2.1

# ジョブの定義
jobs:
  lint:
    docker:
      - image: circleci/ruby:3.0.0
    steps:
      - checkout  # ソースコードのチェックアウト
      - restore_cache:
          keys:
            - v1-bundle-cache-{{ checksum "Gemfile.lock" }} # キャッシュの復元
      - run:
          name: Bundle Install
          command: bundle install --path vendor/bundle # 依存関係のインストール
      - save_cache:
          paths:
            - vendor/bundle
          key: v1-bundle-cache-{{ checksum "Gemfile.lock" }} # キャッシュの保存
      - run:
          name: Run RuboCop
          command: bundle exec rubocop

  test:
    docker:
      - image: circleci/ruby:3.0.0
    steps:
      - checkout
      - restore_cache:
          keys:
            - v1-bundle-cache-{{ checksum "Gemfile.lock" }} # キャッシュの復元
      - run:
          name: Bundle Install
          command: bundle install --path vendor/bundle # 依存関係のインストール
      - save_cache:
          paths:
            - vendor/bundle
          key: v1-bundle-cache-{{ checksum "Gemfile.lock" }} # キャッシュの保存
      - run:
          name: Run RSpec
          command: bundle exec rspec

# ワークフローの定義
workflows:
  version: 2
  lint_and_test:
    jobs:
      - lint
      - test

```

# まとめ・感想
CircleCIの config.yml を使ってRubocopとRSpecを実行する基本的な設定方法を紹介しました。
まだまだ実務で扱っているymlファイルの中身は「完全に理解した」とは言えませんが、少しづつ時間の空いているときに読み進めようと思ってます。
また、自分の開発してるプロダクトではコードをプッシュしてからCIの自動実行が完了するまでが長い（20~30分くらい）ので、config.ymlの設定を見直すことで時短につなげることにもチャレンジしたいなと思ってます.





# 参考
https://www.tweeeety.blog/entry/2018/02/09/195345

https://qiita.com/gold-kou/items/4c7e62434af455e977c2

[ご紹介！ CircleCIの超基本！ 設定ファイルの基礎を優しく解説！【CircleCI入門】](https://www.youtube.com/watch?v=6uZltaISUvo)


https://circleci.com/docs/ja/introduction-to-yaml-configurations/