[Rails5.2] CircleCI × Capistrano でテスト成功後にAWSへ自動デプロイする

# 概要
以下の2つの機能を実装するための方法を記載しています。

- CircleCIを使用してプッシュと同時にRuboCop, Rspecを走らす。
- テスト成功後にCapistranoによってAWSに自動デプロイする

## 前提

- Rails5.2
- Capistranoによる自動デプロイを実装している

実装に当たって、以下の記事を大変参考にさせていただきました。
[rails5.2/Capistrano/CICD環境によるAWSへの自動デプロイ](https://qiita.com/AK4747471/items/882b70f663f4a80ae770)

上記記事の内容だけでも、テスト→デプロイの処理は実現できます。
この記事は、上の参考記事の補足として、workflowによる処理を追加することで、テストが成功した場合のみデプロイを実行する方法を解説しています。

補足部分のみ記事にしているため、そこに到るまでの過程については上の参考記事をご覧下さい。

# 実装
早速ですが、最終形のコードは以下のようになりました。
後でいくつかのブロックに分けながら解説していきます。

```circleci/config.yml
version: 2.1

executors:
  default:
    working_directory: ~/repo
    docker:
      - image: circleci/ruby:2.5.1-node-browsers
        environment:
          - BUNDLER_VERSION: 2.1.4
      - image: circleci/mysql:5.7
        environment:
          - MYSQL_ALLOW_EMPTY_PASSWORD: 'true'
          - MYSQL_ROOT_HOST: '%'

commands:
  setup:
    steps:
      - checkout
      - restore_cache:
          keys:
            - v1-dependencies-{{ checksum "Gemfile.lock" }}
            - v1-dependencies-
      - run:
          name: install dependencies
          command: |
            gem install bundler -v 2.1.4
            bundle install --jobs=4 --retry=3 --path vendor/bundle
      - save_cache:
          paths:
            - ./vendor/bundle
          key: v1-dependencies-{{ checksum "Gemfile.lock" }}
  
jobs:
  test:
    executor: default
    environment:
      RAILS_ENV: test
    steps:
      - setup
      - run: mv config/database.yml.ci config/database.yml 
      - run: bundle exec rake db:create
      - run: bundle exec rake db:schema:load

      - run:
          name: Rubocop
          command: bundle exec rubocop

      - run:
          name: run tests
          command: |
            mkdir /tmp/test-results
            TEST_FILES="$(circleci tests glob "spec/**/*_spec.rb" | \
              circleci tests split --split-by=timings)"
            bundle exec rspec \
              --format progress \
              --format RspecJunitFormatter \
              --out /tmp/test-results/rspec.xml \
              --format progress \
              $TEST_FILES

      - store_test_results:
          path: /tmp/test-results
      - store_artifacts:
          path: /tmp/test-results
          destination: test-results

  deploy:
    executor: default
    steps:
      - setup
      - add_ssh_keys:
          fingerprints:
            - "XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX"
      - deploy:
          name: Capistrano deploy
          command: bundle exec cap production deploy

workflows:
  test_and_deploy:
    jobs:
      - test
      - deploy:
          requires:
            - test
          filters:
            branches:
              only: master
```

## コンテナ定義
executor = 執行者という意味ですが、テストやデプロイを誰に行わせるかをここで定義しています。
ここでのexecutorはDockerコンテナに当たります。

```circleci/config.yml
executors:
  default:
    working_directory: ~/repo
    docker:
      - image: circleci/ruby:2.5.1-node-browsers
        environment:
          - BUNDLER_VERSION: 2.1.4
      - image: circleci/mysql:5.7
        environment:
          - MYSQL_ALLOW_EMPTY_PASSWORD: 'true'
          - MYSQL_ROOT_HOST: '%'
```

作成するDockerコンテナにdefaultという名前をつけ、後のjob部分で呼び出すようにしています。

## 共通コマンドの定義
先ほど作成したコンテナが、この後 test・deploy の２つのjobを実行します。
このそれぞれのjob内で共通して実行させたい内容を setup という名前のコマンド群にまとめ、呼び出すようにしています。

```circleci/config.yml
commands:
  setup:
    steps:
      - checkout
      - restore_cache:
          keys:
            - v1-dependencies-{{ checksum "Gemfile.lock" }}
            - v1-dependencies-
      - run:
          name: install dependencies
          command: |
            gem install bundler -v 2.1.4
            bundle install --jobs=4 --retry=3 --path vendor/bundle
      - save_cache:
          paths:
            - ./vendor/bundle
          key: v1-dependencies-{{ checksum "Gemfile.lock" }}
```

## jobの実行
test・deploy の２つのjobの実行部分を定義しています。
それぞれのjobのはじめに、defaultと名付けたコンテナを呼び出し、共通コマンド setup を実行します。
その後に、各jobに応じた処理を実行させます。

```circleci/config.yml
jobs:
  test:
    executor: default
    environment:
      RAILS_ENV: test
    steps:
      - setup
      - run: mv config/database.yml.ci config/database.yml 
      - run: bundle exec rake db:create
      - run: bundle exec rake db:schema:load

      # rubocopの実行
      - run:
          name: Rubocop
          command: bundle exec rubocop

      # rspecの実行
      - run:
          name: run tests
          command: |
            mkdir /tmp/test-results
            TEST_FILES="$(circleci tests glob "spec/**/*_spec.rb" | \
              circleci tests split --split-by=timings)"
            bundle exec rspec \
              --format progress \
              --format RspecJunitFormatter \
              --out /tmp/test-results/rspec.xml \
              --format progress \
              $TEST_FILES

      - store_test_results:
          path: /tmp/test-results
      - store_artifacts:
          path: /tmp/test-results
          destination: test-results

  # デプロイ
  deploy:
    executor: default
    steps:
      - setup
      - add_ssh_keys:
          fingerprints:
            - "XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX"
      - deploy:
          name: Capistrano deploy
          command: bundle exec cap production deploy
```

## workflow
最後にworkflowの定義です。
test成功後にデプロイ処理に移行するようにします。
また、デプロイはブランチがmasterの時のみ実行させます。

```circleci/config.yml
workflows:
  test_and_deploy:
    jobs:
      - test
      - deploy:
          requires:
            - test
          filters:
            branches:
              only: master
```

# 最後に
以上で、workflowを用いてテスト成功後に自動デプロイする処理を実装できました。
もっと効率的に書けそうな感じもしますが、そういった点があればご指摘頂けると助かります。

最後までご覧いただき、ありがとうございました。

### 参考記事
[rails5.2/Capistrano/CICD環境によるAWSへの自動デプロイ](https://qiita.com/AK4747471/items/882b70f663f4a80ae770)
[RailsにcircleCIを構築し、githubと連携してrubocopとrspecテストを走らせる](https://qiita.com/AK4747471/items/b2161784065f21cd1645)
[CircleCIでデプロイを自動化](https://bbbbruno.qrunch.io/entries/9070Lw9PVmQBJnvP)