CiecleCI の Workflow job に名前をつけて依存を管理する

## 動機: CircleCI Orbs を使ったときに困ったこと

CircleCI Orbs を使うことで、AWS ECRへのイメージPushなどを行ってくれますが、ブランチごとに別々のリポジトリにpushしたい場合などは少し困ったことになります。

以下、正しく動作しないケースです。

```yml:.circleci/config.yml
version: 2.1
orbs:
  aws-ecr: circleci/aws-ecr@6.15.1
  aws-ecs: circleci/aws-ecs@1.4.0
# 中略
workflows:
  build:
    jobs:
      # 中略
      # Staging 用のデプロイ
      - aws-ecr/build-and-push-image
          repo: "Staging用リポジトリ"
          tag: "${CIRCLE_SHA1}"
          requires:
            - test
            - lint
          filters: &staging-filter
            branches:
              only:
                - staging
      - aws-ecs/deploy-service-update:
          requires:
            - aws-ecr/build-and-push-image
          family: 'Staging用ファミリー'
          cluster-name: 'Staging用クラスター名'
          container-image-name-updates: 'container=Staging用コンテナ名,tag=${CIRCLE_SHA1}'
          filters: *staging-filter
      # Production用のデプロイ
      - aws-ecr/build-and-push-image
          repo: "Production用リポジトリ"
          tag: "${CIRCLE_SHA1}"
          requires:
            - test
            - lint
          filters: &master-filter
            branches:
              only:
                - master
      - aws-ecs/deploy-service-update:
          requires:
            - aws-ecr/build-and-push-image
          family: 'Production用ファミリー'
          cluster-name: 'Production用クラスター名'
          container-image-name-updates: 'container=Production用コンテナ名,tag=${CIRCLE_SHA1}'
          filters: *master-filter
```

こんなかんじのエラーが出てしまいます。

```
Job 'aws-ecs/deploy-service-update' requires 'aws-ecr/build-and-push-image', which is the name of 2 other jobs in workflow 'build'

You can give a job within a workflow an explicit name by adding a `name` key
```
 
Orbs に定義されている、 `aws-ecs/deploy-service-update` job 実行の前提として必要としている、`aws-ecr/build-and-push-image` が  Staging 用と、Production 用の2つ用意してしまっているため、うまく動作しないとのことです。

ヘルプメッセージに従って、`name` を Workflow の job に指定すれば良さそうです。

ひとまず、[ドキュメント](https://circleci.com/docs/2.0/configuration-reference/#job_name-1)を読んでみたのですが、 ~~今の所 (2021年1月8日現在) この点についての記載はなさそうです。。。~~

**2021/1/9 14:00 修正**

[ドキュメントに記載はありました。](https://circleci.com/docs/2.0/configuration-reference/#requires) ドキュメントの解釈を誤っており、`name` は `requires` の下で使えるものだと勘違いしておりました。
`job` の下で `name` が `requires` のために使えるという記載っぽいです。  @PanonCotta さんご指摘ありがとうございます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61997/61503822-3103-0e41-e539-26206d4a6374.png)



## 修正後

```yml:.circleci/config.yml
version: 2.1
orbs:
  aws-ecr: circleci/aws-ecr@6.15.1
  aws-ecs: circleci/aws-ecs@1.4.0
# 中略
workflows:
  build:
    jobs:
      # 中略
      # Staging 用のデプロイ
      - aws-ecr/build-and-push-image
          name: "ecr-build-and-push-image-stg"
          repo: "Staging用リポジトリ"
          tag: "${CIRCLE_SHA1}"
          requires:
            - test
            - lint
          filters: &staging-filter
            branches:
              only:
                - staging
      - aws-ecs/deploy-service-update:
          name: "ecs-deploy-stg"
          requires:
            - ecr-build-and-push-image-stg
          family: 'Staging用ファミリー'
          cluster-name: 'Staging用クラスター名'
          container-image-name-updates: 'container=Staging用コンテナ名,tag=${CIRCLE_SHA1}'
          filters: *staging-filter
      # Production用のデプロイ
      - aws-ecr/build-and-push-image
          name: "ecr-build-and-push-image-prod"
          repo: "Production用リポジトリ"
          tag: "${CIRCLE_SHA1}"
          requires:
            - test
            - lint
          filters: &master-filter
            branches:
              only:
                - master
      - aws-ecs/deploy-service-update:
          name: "ecs-deploy-prod"
          requires:
            - ecr-build-and-push-image-prod
          family: 'Production用ファミリー'
          cluster-name: 'Production用クラスター名'
          container-image-name-updates: 'container=Production用コンテナ名,tag=${CIRCLE_SHA1}'
          filters: *master-filter
```

これで、ECRにpushするイメージ作成と、ECSによるサービス展開までについてを環境別に分けることができました。

この設定のやりかたは、同じjobを再利用しつつ、環境別にパラメータを変えるといったときにも便利そうです。
検証用に、以下のようなサンプルを作ってみて実行してみたところ、job1, job2 を再利用することができました。

```yml:.circleci/config.yml
version: 2.1

executors:
  ruby-executor:
    docker:
      - image: circleci/ruby:3.0.0

jobs:
  job1:
    executor: ruby-executor
    # https://circleci.com/docs/2.0/reusing-config/#authoring-parameterized-jobs
    # Workflow からパラメータを渡すことができる
    parameters:
      env:
        type: string
        default: development
    steps:
      - checkout
      - run: 'ruby --version'
      # パラメータを使う時は、<<parameters.キー>> で呼び出しできる
      - run: 'echo <<parameters.env>>'
  job2:
    executor: ruby-executor
    parameters:
      env:
        type: string
        default: development
    steps:
      - checkout
      - run: 'ruby --version'
      - run: 'echo <<parameters.env>>'

workflows:
  build:
    jobs:
      # master のみ
      - job1:
          name: job1-master
          # env パラメータを production として job にわたす
          env: production
          filters: &master-filter
            branches:
              only:
                - master
      - job2:
          name: job2-master
          env: production
          filters: *master-filter
          requires:
            - job1-master
      # master 以外
      - job1:
          name: job1-non-master
          # env パラメータを指定してないので、job の `<<parameters.env>>` は、
          # default にセットしている、development に置き換わる
          filters: &non-master-filter
            branches:
              ignore:
                - master
      - job2:
          name: job2-non-master
          filters: *non-master-filter
          requires:
            - job1-non-master

```

## まとめ

Workflow Job に名前が使えるようになったのも、Jobにパラメータが渡せるようになったのも、
ドキュメントの雰囲気を見るに2.1からのようです。

昔から運用している、 `.circleci/config.yml` の場合、環境ごとに別々にjobを作ったり、
command で以下のように書いているケースがありました。

```yml
          command: |
            if [ "${CIRCLE_BRANCH}" == "master" ]; then
                bundle exec cap production deploy
            elif [ "${CIRCLE_BRANCH}" == "staging" ]; then
                bundle exec cap staging deploy
            fi
```

なかなか難儀なのでコンパクトに

```yml
          command: bundle exec cap <<parameters.env>> deploy
```

などに変更するなどして、スマートなCiecleCI設定ライフを送りたいものです。