CircleCIのmachine Executorでbuildジョブからdeployジョブへとファイルの受け渡しを行う（Workspace）

# 概要

CircleCIの`machine Executor`を使用して「Dockerビルド〜デプロイ」を行なっているプロジェクトにおいて、__build__ jobで生成したファイル/ディレクトリを__deploy__ jobに渡す手順をメモ書きします。

# 方法（結論）

__job間にWorkspaceを介して__deployに必要なファイル/ディレクトリを共有します。

## `persist_to_workspace`

後続のdeploy jobへ提供したいファイル/ディレクトリを__Workspace__へアップロードします。

具体的には、build jobの最後のstepで`persist_to_workspace`を実行します。

  ```yml:config.yml
      - persist_to_workspace:
          root: .
          paths:
            - ./app/build
            - ./app/deps
  ```

  - `root:`
    - ルートにしたいコンテナ上のディレクトリ。
    - 絶対パス（もしくは`working_directory`からの相対パス）の形で指定します。
  - `paths:`
    - 上記rootからの相対パスで、jobs間で共有したいファイル/ディレクトリを指定します。

## `attach_workspace`

__Workspace__に保存されたデータを`attach_workspace`で取得します。

  ```yml:.circleci/config.yml
      - attach_workspace:
          at: .
  ```

  - `at:`
    - 絶対パス（もしくは`working_directory`からの相対パス）の形で指定します。

## 補足説明

CircleCIの`jobs`は__隔離されている個々の環境で実行される__仕様になっています。

```yaml:.circleci/config.yml
# 例：build jobで生成されたファイルは、そのままではdeploy jobには渡らない
jobs:
  build:
    ...: ...

  deploy:
    ...: ...

```

そのため、特定のjobの結果を別のjobで使うためには、こういった「Workspaceでデータ共有する」などの方法で受け渡しを行う処理が必要となります。

![workspaces.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211052/c4104348-7156-0bb0-94b1-252cabec5f6c.png)

（画像引用：CircleCI / [Jobs overview](https://circleci.com/docs/2.0/jobs-steps/#jobs-overview)）

# 例

## 実行環境

- macOS
- GitHub（CircleCIと連携済）

## シチュエーション

```terminal
.
├── .circleci/
│   └── config.yml
├── app
│   ├── Dockerfile
│   ├── (build/)
│   ├── (deps/)
│   └── moge.json
└── docker-compose.yml
```

- (build)  
  build job中、`docker-compose build`により生成されたビルドファイル群を格納しています
- (deps)  
  擬似コード `moge deps.get` の実行により、app/moge.jsonへ記述した依存パッケージ群が生成されてdepsディレクトリに格納します
  - `npm install`でpackage.json記述パッケージをnode_modules/に格納するのと同じイメージです

## コード

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

orbs:
  # 擬似Orb
  deploy-orb: deploy-orb/deploy-orb@latest

executors:
  machine-executor:
    machine:
      image: ubuntu-1604:202010-01

workflows:
  version: 2
  build-deploy:
    jobs:
      - build
      - deploy:
          requires:
            - build

jobs:
  build:
    executor:
      name: machine-executor
    steps:
      - checkout
      - run:
          name: Build Docker container
          command: |
            set -x
            docker-compose build
      - run:
          name: Moge deps.get
          command: |
            set -x
           # 擬似コード（依存パッケージ取得）
            docker-compose run --rm app bash -c "moge deps.get"
      - run:
          name: Up Docker container
          command: docker-compose up -d
      - run:
          name: Moge test
          command: |
            set -x
           # 擬似コード（テスト実行）
            docker-compose exec app bash -c "moge test"
      - persist_to_workspace:
          root: .
          paths:
            - ./app/build
            - ./app/deps
      - run:
          name: Finish build
          command: echo "Finish build"
  deploy:
    executor:
      name: machine-executor
    steps:
      - checkout
      - attach_workspace:
          at: .
      - run:
          name: Check files/directories
          command: |
            set -x
            pwd && ls -a && ls app
      - run:
          name: Container push and release
          working_directory: app
          command: |
         # 擬似コード（本番環境へビルドコンテナをデプロイ）
            deploy-orb/install-and-push-release -a $YOUR_APP_NAME
      - run:
          name: Finish deploy
          command: echo "Finish deploy"
```
# （参考）

- CircleCI
  - Using Workflows to Schedule Jobs  
    https://circleci.com/docs/2.0/workflows/
  - Orbs, Jobs, Steps, and Workflows  
    https://circleci.com/docs/2.0/jobs-steps/
  - Choosing an Executor Type  
    https://circleci.com/docs/2.0/executor-types/