CircleCIのconfig.ymlをリファクタリングする

# 概要

- CircleCI2.1のcommandsとexecutorsは便利なので使ってないなら使いましょう
- CircleCIというよりはYAMLの記法にあるアンカー（`&`）、エイリアス（`*`）、マージ（`<<`）をうまいこと使いましょう

# commandsとexecutorsについて

## [commands (requires version: 2.1)](https://circleci.com/docs/2.0/configuration-reference/#commands-requires-version-21)

よく使うパターンのstepsをパラメータ指定で使い回せる。

```yaml:sample
commands:
  sayhello:
    description: "A very simple command for demonstration purposes"
    parameters:
      to:
        type: string
        default: "Hello World"
    steps:
      - run: echo << parameters.to >>
```

ちなみに弊社で実際使ってる例はこのくらいでした （パラメータは結局使っていなかった :innocent:） 


```yaml:利用例
commands:
  xxxxxx_checkout: # 共通のチェックアウト
    steps:
      - checkout:
          path: /xxxxxx
  go_mod_download: # go moduleのdownloadとcache
    steps:
      - restore_cache:
          name: Restore go modules cache
          keys:
            - mod-{{ .Environment.COMMON_CACHE_KEY }}-{{ checksum "../go.mod" }}
      - run: go mod download
      - save_cache:
          name: Save go modules cache
          key: mod-{{ .Environment.COMMON_CACHE_KEY }}-{{ checksum "../go.mod" }}
          paths:
            - /xxxxxx/server/pkg/mod/cache
```

## [executors (requires version: 2.1)](https://circleci.com/docs/2.0/configuration-reference/#executors-requires-version-21)

dockerイメージ、環境変数、workingDirectoryを1セットにして使い回せる感じです。

```yaml:sample
version: 2.1
executors:
  my-executor:
    docker:
      - image: circleci/ruby:2.5.1-node-browsers
```

弊社だと[Monorepo](https://qiita.com/kyokomi/items/75d006a91cfc27f3b594)にしてるので、色々なbuildが存在しているのですがgolangのbuildは、同じexecutorを使いまわせたりできています。

```yaml:利用例
executors:
  lambda-build-executor:
    docker:
      - image: kyokomi/docker-golang
        environment:
          <<: *env-xxxxxx-go-path
    working_directory: /xxxxxx/server/src/cluster/lambda

  lambda-deploy-executor:
    docker:
      - image: hashicorp/terraform:latest
        environment:
          <<: *env-aws-key-zzzzzz-foobar-deploy

  yyyyyy-build-executor:
    docker:
      - image: kyokomi/docker-golang
        environment:
          APP_NAME: yyyyyy
          <<: *env-xxxxxx-go-path
    working_directory: /xxxxxx/server/src/cluster/yyyyyy

  aws-ecr-deploy-executor:
    docker:
      - image: kyokomi/docker-golang

  aws-ecs-deploy-executor:
    docker:
      - image: hashicorp/terraform:latest
```

# アンカー（`&`）、エイリアス（`*`）、マージ（`<<`）

## アンカーとエイリアス

たとえばこんな感じにmysqlが起動しているか確認する `run` があるとして、 `&run-waitmysql` というアンカーを設定すると `run: *run-waitmysql` で同じ内容を実行してくれます。

```yaml

...

      - run: &run-waitmysql
          name: Waiting for MySQL to be ready
          command: |
            i=0
            while true; do
              if exec 3<> /dev/tcp/127.0.0.1/3306; then
                exit 0
              else
                ((i++))
                echo $i
                [ $i -gt 30 ] && exit 1
              fi
              sleep 1
            done
          shell: /bin/bash

      - run: *run-waitmysql # run: &run-waitmysql と同じ内容を実行してくれます
```

## アンカーをマージ

AWS_ACCESS_KEY_IDやAWS_SECRET_ACCESS_KEYをそのままベタッと書くとあとで見たときにこのkeyって何のkeyだっけ？となることが多く。referencesに定義してアンカーを設定してマージを使うと整理できて便利です。

```yaml:整理しておく
references:
  # とある権限をもつaws keyその１
  env-aws-key-xxxxxx-ci: &env-aws-key-xxxxxx-ci
    AWS_DEFAULT_REGION:    ap-northeast-1
    AWS_ACCESS_KEY_ID:     AKIAIxxxxxxxxxxxxxx
    AWS_SECRET_ACCESS_KEY: xxxxxxxxxxxxxxxxxxxxxxxxxxxxx

  # とある権限をもつaws keyその2
  env-aws-key-zzzzzzz-foobar-deploy: &env-aws-key-zzzzzz-foobar-deploy
    AWS_DEFAULT_REGION:    ap-northeast-1
    AWS_ACCESS_KEY_ID:     AKIxxxxxxxxxxxxxxx
    AWS_SECRET_ACCESS_KEY: fffffffffffffffffffffffffffffffff

  # 全buildでGOPATHは共通、GO111MODULEはデフォルトでONにしたい環境変数
  env-go-path: &env-xxxxxx-go-path
    GOPATH:      "/xxxxxx/server:/go"
    GO111MODULE: "on"
```

```yaml:マージする
executors:
  lambda-build-executor:
    docker:
      - image: kyokomi/docker-golang
        environment:
          <<: *env-xxxxxx-go-path # ここでマージしている
    working_directory: /xxxxxx/server/src/cluster/lambda

  lambda-deploy-executor:
    docker:
      - image: hashicorp/terraform:latest
        environment:
          <<: *env-aws-key-zzzzzz-foobar-deploy # ここでマージしている
```

```yaml:マージ後のイメージ
executors:
  lambda-build-executor:
    docker:
      - image: kyokomi/docker-golang
        environment:
          GOPATH:      "/xxxxxx/server:/go"
          GO111MODULE: "on"
    working_directory: /xxxxxx/server/src/cluster/lambda

  lambda-deploy-executor:
    docker:
      - image: hashicorp/terraform:latest
        environment:
          AWS_DEFAULT_REGION:    ap-northeast-1
          AWS_ACCESS_KEY_ID:     AKIxxxxxxxxxxxxxxx
          AWS_SECRET_ACCESS_KEY: fffffffffffffffffffffffffffffffff
```

ちなみに、この辺をうまいこと使えば実は `commands` や `executors` もなくてもなんとかなっていました。
が、使い方をミスるとわけわからないDSLみたいになるので一定のルールを決めておくといいと思います。
（prefix `env-xxxx`なら 環境変数をマージする用、 `run-xxxx` なら run:でエイリアスする等など）

# おわり
`.circleci/config.yml` なかなかリファクタせずにコピペで済ませてしまうことが多く、いざ編集するときメンテナンスが大変でしたが、こうやって整理していくことでプログラムぽくしていくことで1000step超えのymlでも見渡しがいいようしていけるといいですね。
（config.yml分割したい...）

# 参考
- [commands (requires version: 2.1)](https://circleci.com/docs/2.0/configuration-reference/#commands-requires-version-21)
- [executors (requires version: 2.1)](https://circleci.com/docs/2.0/configuration-reference/#executors-requires-version-21)
- [YAML 基本記法まとめ]( https://qiita.com/tfrcm/items/5f8e4c5795ce41b214d1#%E3%82%A2%E3%83%B3%E3%82%AB%E3%83%BC%E3%81%A8%E3%82%A8%E3%82%A4%E3%83%AA%E3%82%A2%E3%82%B9)