CircleCIのparallelismと2.1からの新機能を使ってスッキリと並列化する

# 概要
私の担当するサービスでは、CircleCIによる自動テストを行っています。

最近、実行時間が伸びてきた事もあり、CircleCIの `parallelism` という並列実行数の設定項目と、Version 2.1 からの新機能 ( 2018年頃のリリースなので別に`新`ではないけど )である `commands` `parameters` `executors` を使って、今まで直列1本で行っていたCircleCIのテストのビルドを並列化して実行時間を10分→4〜5分程度に短くしてみたのでその際の情報を共有します。

いざ、ymlファイルに並列的な書き方をしようとすると、たくさんの重複記述を書く必要が出てしまい、可読性もテンションも下がってしまいます。そこで、

* `executors` 項目に実行環境設定( `machine: true` )をまとめて、複数のジョブで再利用する
* `commands` に何度も出てくるコマンドをまとめて名前をつけて、ステップ内から呼び出す際に再利用する
* `parameters` オプションを使って `commands` のコマンドに渡すパラメータの名前や型を定義して可読性を上げる

を行い、その後

* `parallelism` を任意の数値に設定し、テスト対象のファイルをCircleCIの仕組みを使って **よしなに** 分割し実行する

をしました。

# 例 (雰囲気)

もともと、`build` というjob一本で直列に実行していたものを

```yml
version: 2
jobs:
  build:
    machine: true
    working_directory: ~/work
    steps:
      - checkout
      - run:
          name: Execute test 1
          command: |
             echo "You can execute any command"
      - run:
          name: Execute test 2
          command: |
            ./test-runner --files \
            app/tests/cases/models \
            app/tests/cases/controllers
workflows:
  version: 2
  my-build:
    jobs:
      - build
```

↓こうした。

```yml
version: 2.1
executors:
  my-executor:
    machine: true
commands:
  my_command_1:
    steps:
      - run:
          name: Execute test 1
          command: |
             echo "You can execute any command"
  my_command_2:
    parameters:
      target:
        type: string
    steps:
      - run:
          name: Execute test 2
          command: |
            ./test-runner --files << parameters.target >>
 jobs:
  my-unittest-job:
    executor: my-executor
    parallelism: 3
    steps:
      - checkout
      - my_command_1:
      - my_command_2:
          target: |
            $(circleci tests glob \
            "app/tests/cases/{models,controllers}/**/*.test.php" \
            | circleci tests split --split-by=filesize
workflows:
  version: 2
  my-build:
    jobs:
      - my-unittest-job
```

コマンドを別途定義するので、このサンプルだと変更後の方が記述量が増えているが、実際はもっとたくさんのジョブやステップが存在しているので、この対策を行わないとコピペに次ぐコピペで大変なことになる。

## parameters について
コマンド側で `parameters` の中にパラメータ名と型、デフォルト値、説明書きを定義することが出来る。
また、実際のコマンド実行の中では << parameters.パラメータ名 >> という形で取り出すことが出来る。
https://circleci.com/docs/2.0/reusing-config/#parameter-syntax

```yml
commands:
# ...略...
  my_command_2:
    parameters:
      tekitouna-parameter-name:
        description: Tekitou na setsumei.
        type: string
        default: "FUGA"
    steps:
      - run:
          command:  echo << parameters.tekitouna-parameter-name >>
```

呼び出し側は、以下のように実行するコマンドにパラメータをセットすることが出来る。

```yml
 jobs:
  my-unittest-job:
   steps:
      - my_command_2:
          tekitouna-parameter-name: HOGE
```

## CircleCIの仕組みを使って **よしなに** 分割し実行する、件
https://circleci.com/docs/2.0/parallelism-faster-jobs/ によると、`circleci tests` コマンドにて `glob` を使って対象となるファイル名を取得した後に、`split` に渡す事で、`parallelism: 3` のように指定した並列実行の数に分割してくれる様子。
`--split-by` には `filesize` (ファイルのサイズでよしなに分割)と`timing` (過去の実行時間の実績を元によしなに分割)があるようで、`timing` のほうが `filesize`より更に最適化出来るようだが、設定が若干面倒そうだったので、今回はやめておいた。

```
circleci tests glob "**/*.go" | circleci tests split --split-by=filesize
```

サンプルでも、以下のようにテストランナーにテスト対象のファイル名を渡すようにしている。

```yml
target: |
  $(circleci tests glob \
  "app/tests/cases/{models,controllers}/**/*.test.php" \
  | circleci tests split --split-by=filesize
```

# 苦労した点
今まで直列に、固定された順序で実施されていたため、 **たまたま運良く通っていたテスト** というのが存在していたが、それらが並列化したタイミングでFailするようになってしまった。そういうのが複数箇所、複数原因あり、特定してテスト側を修正するのが悲しかった。例えば↓の様に a→bの順番で実行されていたからbでrequireしなくても動いてたが、並列化によって実行グループが変わったために b で someFunc() なんて知らない、と怒られたり。

```a.test.php
require_once "./functions.php"

someFunc();
```
```b.test.php
someFunc();
```

# 参考
https://circleci.com/docs/ja/2.0/configuration-reference/