CircleCI2.0のWorkflowを試してみる

CircleCI2.0の特徴のひとつであるWorkflowを触ってみます。

# workflowを設定してみる

CircleCI2.0でWorkflowを設定しなかった場合、`build`jobのみが走ります。

試しに以下を試してみます。

```yaml:.circleci/config.yml
version: 2
jobs:
  hoge: # <-- ここ
    working_directory: ~/workspace
    docker:
      - image: node:9.4.0
    steps:
      - checkout
      - run:
          name: Echo
          command: echo hello, world.
```

すると、ビルドは失敗し、以下のようなエラーが出ます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/83325/e598bf89-b353-63d3-0fdd-b417c9f53550.png)

workflowで`hoge`を実行するように修正します。

```yaml:.circleci/config.yml
version: 2
jobs:
  hoge:
    working_directory: ~/workspace
    docker:
      - image: node:9.4.0
    steps:
      - checkout
      - run:
          name: Echo
          command: echo hello, world.
workflows:
  version: 2
  fuga:
    jobs:
      - hoge
```

これでhogeが実行され、ビルドが通るようになりました。

# ビルドとテストを走らせる

ここからは[前記事のNuxtアプリケーション](https://qiita.com/sawadashota/items/d1382b1e1b9a5595ab66)を土台に説明していきます。

「ビルドしてからテストを走らせる」例です。

```yaml:.circleci/config.yml
version: 2
jobs:
  build:
    working_directory: ~/workspace
    docker:
      - image: node:9.4.0
    steps:
      - checkout
      - restore_cache:
          key: yarn-{{ .Branch }}-{{ checksum "yarn.lock" }}
      - run:
          name: Install dependencies
          command: yarn
      - run:
          name: Build
          command: yarn build
      - save_cache:
          key: yarn-{{ .Branch }}-{{ checksum "yarn.lock" }}
          paths:
            - ~/workspace/node_modules
  test:
    working_directory: ~/workspace
    docker:
      - image: node:9.4.0
    steps:
      - checkout
      - restore_cache:
          key: yarn-{{ .Branch }}-{{ checksum "yarn.lock" }}
      - run:
          name: Install dependencies
          command: yarn
      - run:
          name: Build
          command: yarn build
      - run:
          name: Test
          command: yarn test
workflows:
  version: 2
  build_and_test: # workflow名
    jobs:
      - build
      - test:
          requires: # buildが成功したら実行する
            - build
```

## `test`

基本的な構造は`build`と同じで、`Test`を追加しました。

## `workflows`

実行する順序を規定しています。

その他、コメントを参照。

## 実行結果

実行結果を見てみると2つのビルドが走っています。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/83325/6b5ab8c5-badf-f15f-d669-cdbc2dcefd62.png)

`build_and_test`をクリックすると、Workflow図を見ることができます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/83325/9b67ea7b-e8fa-f2b1-97d9-27317521affe.png)

# Workspaceを後続のビルドにシェアする

上記のビルドでは、`build`でソースコードをビルドして、`test`でも同じことをやっていて非効率的ですので、`build`でビルドしたものを`test`でも使えるようにします。

```yaml:.circleci/config.yml
version: 2
jobs:
  build:
    working_directory: ~/workspace
    docker:
      - image: node:9.4.0
    steps:
      - checkout
      - restore_cache:
          key: yarn-{{ .Branch }}-{{ checksum "yarn.lock" }}
      - run:
          name: Install dependencies
          command: yarn
      - run:
          name: Build
          command: yarn build
      - save_cache:
          key: yarn-{{ .Branch }}-{{ checksum "yarn.lock" }}
          paths:
            - ~/workspace/node_modules
      - persist_to_workspace:
          root: . # workspaceのrootパス（絶対パスかworking_directoryからの相対パス）
          paths:
            - . # 共有するパス（絶対パスかrootからの相対パス）
  test:
    working_directory: ~/workspace
    docker:
      - image: node:9.4.0
    steps:
      - attach_workspace: # workspaceをアタッチする
          at: .
      - run:
          name: Test
          command: yarn test
workflows:
  version: 2
  build_and_test:
    jobs:
      - build
      - test:
          requires:
            - build
```

## `persist_to_workspace`

共有するパスを設定します。

`paths`では共有するパスを指定します。

`.`としているので、`~/workspace`ディレクトリがまるごと共有されます。

例えば、`.`を`dist`と置き換えた場合は、`dist`ディレクトリのみが共有されます。

## `attach_workspace`

`persist_to_workspace`で共有したworkspaceをアタッチします。

## 実行結果

`build`の`persist_to_workspace`の処理に4秒ほどとられていますが、22秒かかっていた`test`が3秒になりました。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/83325/853dc04f-21b8-a09d-2047-3fc175b664d0.png)

# 特定のブランチでしか実行しないようにする

「masterブランチのときのみ`deploy`を実行する」というのをやってみます。

まずは、`deploy`jobを作ります。

```yaml:.circleci/config.yml
  deploy:
    working_directory: ~/workspace
    docker:
      - image: node:9.4.0
    steps:
      - attach_workspace:
          at: .
      - deploy:
          command: echo Deploy!
```

次にworkflowに`deploy`を追加します。

```yaml:.circleci/config.yml
workflows:
  version: 2
  build_and_test:
    jobs:
      - build
      - test:
          requires:
            - build
      - deploy:
          requires:
            - build
            - test
          filters:
            branches:
              only: master
```

## 実行結果

masterにpushしたときは`deploy`しますが、

![image.png](https://qiita-image-store.s3.amazonaws.com/0/83325/e2d6fd24-4562-a630-5479-d2b5d5c0ba0f.png)

developにpushしたときは`deploy`を実行していません。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/83325/5381c517-ae59-9d95-cc41-f6c36b4d2796.png)

# 特定のjobを実行する前に人間の承認を待つようにする

自動で実行されると困るタスクに使います。

`build`と`test`のみ自動で行い、`deploy`は手動で承認ボタンを押したら実行するようにしてみます。

```yaml:.circleci/config.yml
workflows:
  version: 2
  build_and_test:
    jobs:
      - build
      - test:
          requires:
            - build
      - deploy-approval:
          type: approval
          requires:
            - build
            - test
          filters:
            branches:
              only: master
      - deploy:
          requires:
            - deploy-approval
```

`deploy-approval`は承認を待つだけのjobなので、処理は行いません。なので、`deploy`に`type: approval`をつけると、`deploy`が走らなくなります。

## 実行結果

`test`が成功した時点で、処理が止まるようになりました。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/83325/20c98ad5-6a74-87bf-6cbe-3e14f970e56d.png)

丸いの一時停止ボタンをクリックすると確認のダイアログが出現します。「Approve」を押すと、`deploy`が実行されます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/83325/279da6d9-8bbc-97c1-0b43-7fb8cf058c6a.png)


# tagがついているときのみ実行するようにする

「リリースノートを書いたらデプロイ」としたい場合は、タグ作成が行われたときのみ、`deploy`が実行されるようにします。

```yaml:.circleci/config.yml
workflows:
  version: 2
  build_and_test:
    jobs:
      - build:
          filters:
            tags:
              only: /.*/
      - test:
          requires:
            - build
          filters:
            tags:
              only: /.*/
      - deploy:
          requires:
            - build
            - test
          filters:
            branches:
              ignore: /.*/
            tags:
              only: /^v[0-9]+(\.[0-9]+){2}/
```

## すべてのjobにtags filterをかける

すべてのjobに、全タグで実行すると明示的に記述していることに違和感があると思いますが、これは、filterのデフォルト設定が**「すべてのブランチに対して実行する」**に対して、**「いかなるタグにも実行しない」**ということになっているからです。

CircleCIのドキュメントには以下のように書いてあります。

> * If neither only nor ignore are specified then all branches will run the job.

> * If neither only nor ignore are specified then the job is skipped for all tags.

## 特定のtagのみで実行したいjobにbranches filterをかける

`deploy`の`filters`に**「すべてのブランチで実行しない」**と設定しています。これがないと、タグがなくても実行されてしまいます。

```yaml:.circleci/config.yml
          filters:
            branches:
              ignore: /.*/
```

## 有効なtagのフォーマットを正規表現で指定する

tagsは正規表現でフィルタリングできます。

`v1.0.0`や`v1.0.0beta`などを許可する例がこちら。

```yaml:.circleci/config.yml
            tags:
              only: /^v[0-9]+(\.[0-9]+){2}.*/
```

## 実行結果

### pushをトリガーにしたworkflow

![image.png](https://qiita-image-store.s3.amazonaws.com/0/83325/7fdace5f-a24a-e7f0-492b-a8a57d2c33a0.png)

### タグの作成をトリガーにしたworkflow

![image.png](https://qiita-image-store.s3.amazonaws.com/0/83325/43ccbfce-b53b-cf81-e5c4-e166fa526d7a.png)

# まとめ

workflowには他にもcronがあったりますが、ここまでで充分実践でも使えるようになるのではないでしょうか。

# 参考
https://circleci.com/docs/2.0/workflows/