Angular プロジェクトのビルド・テスト・デプロイ環境を構築する

# はじめに
* 継続的なデリバリーはプロジェクトの初期段階でビルド・テスト・デプロイを自動化しておくことが秘訣、みたいな記事をよく見るので new したての Angular プロジェクトでビルド・テスト・デプロイ環境を構築した
* CI は CircleCI を使っている

# 動作環境
* 環境は下記

```
$ ng -v

    _                      _                 ____ _     ___
   / \   _ __   __ _ _   _| | __ _ _ __     / ___| |   |_ _|
  / △ \ | '_ \ / _` | | | | |/ _` | '__|   | |   | |    | |
 / ___ \| | | | (_| | |_| | | (_| | |      | |___| |___ | |
/_/   \_\_| |_|\__, |\__,_|_|\__,_|_|       \____|_____|___|
               |___/

Angular CLI: 1.6.7
Node: 8.9.0
OS: darwin x64
Angular: 5.2.4
... animations, common, compiler, compiler-cli, core, forms
... http, language-service, platform-browser
... platform-browser-dynamic, router

@angular/cli: 1.6.7
@angular-devkit/build-optimizer: 0.0.42
@angular-devkit/core: 0.0.29
@angular-devkit/schematics: 0.0.52
@ngtools/json-schema: 1.1.0
@ngtools/webpack: 1.9.7
@schematics/angular: 0.1.17
typescript: 2.5.3
webpack: 3.10.0
```


# Angular プロジェクトの作成
* まずは Angular プロジェクトを作成する
    * 以降、プロジェクトに対する変更は何もしない(笑)

```
$ ng new <sample-app>
```

# ビルド・テストの実行
* Angular はプロジェクトを作成した状態でテストが備わっている

### Lint, 単体テスト, E2E テスト
* 下記コマンドでそれぞれ実行できる

```bash
$ yarn lint # TSLint
$ yarn test # 単体テスト
$ yarn e2e  # E2E テスト
```

# CircleCI 上でのビルド環境構築
* [CircleCI の Hello World](https://circleci.com/docs/2.0/hello-world/) を参考に CircleCI の設定ファイルを用意する
    * ver 2.0 からは `circle.yml` ではなく、 `.circleci/config.yml` になっている

```yaml:.circleci/config.yml
version: 2
jobs:
   build:
     docker:
       - image: circleci/node:8.9.4-stretch-browsers
     steps:
       - checkout
       - run: echo "hello world"
```
* Node.js の image のバージョンは LTS の stretch-browsers を使用
    * Angular のテストでブラウザが必要になるため
    * 指定可能なバージョンは [Pre-Built CircleCI Docker Images](https://circleci.com/docs/2.0/circleci-images/#nodejs) を見るとわかる

# CircleCI 上でのビルド・テストの設定
* ビルド・テストの設定は下記のようになった

```yaml:.circleci/config.yml
version: 2
jobs:
  build:
    docker:
      - image: circleci/node:8.9.4-stretch-browsers
    steps:
      - checkout
      - run: echo "Start build"
      - run: yarn install
      - run: yarn build
  lint:
    docker:
      - image: circleci/node:8.9.4-stretch-browsers
    steps:
      - checkout
      - run: echo "Start lint"
      - run: yarn install
      - run: yarn lint
  test:
    docker:
      - image: circleci/node:8.9.4-stretch-browsers
    steps:
      - checkout
      - run: echo "Start test"
      - run: yarn install
      - run: yarn test --single-run
  e2e:
    docker:
      - image: circleci/node:8.9.4-stretch-browsers
    steps:
      - checkout
      - run: echo "Start e2e"
      - run: yarn install
      - run: yarn e2e
workflows:
  version: 2
  build_and_test:
    jobs:
      - build
      - lint:
          requires:
            - build
      - test:
          requires:
            - build
      - e2e:
          requires:
            - build
```

* test は --single-run モードで実行した
    * オプションを付けないと watch し続けるので、1 回のみ実行
* [Workflows](https://circleci.com/docs/2.0/workflows/) を設定して　job が並列に実行されるようにした
    * `requires` を指定して lint, test, e2e は build が終わってから実行
    * build 後は lint, test, e2e が並列で実行される
    * 余談だがインデントがズレていたりすると CI が動かない

# CircleCI から Firebase へデプロイ
* Firebase へ deploy するための[設定](https://circleci.com/docs/2.0/deployment-integrations/#firebase)をする
    * Firebase にプロジェクトを作成する手順は割愛
* CircleCI から Firebase を操作できるように firebase-tools を devdependencies でインストールしておく

```bash
$ yarn add firebase-tools -D
```

* プロジェクトに Firebase の設定ファイルを作成するために下記コマンドを実行

```bash
$ firebase init
```
* 色々聞かれるのでよしなに進める
    * 使うサービスは Hosting
    * public directory は dist/
    * single-page app は yes

* CircleCI のために Firebase CLI token を生成する

```
$ firebase login:ci
```

* 生成された token を CircleCI の環境変数に設定
    * Setting → Environment Variables から環境変数を追加

```yaml:.circleci/config.yml
前略
...
  deploy:
    docker:
      - image: circleci/node:8.9.4-stretch-browsers
    steps:
      - checkout
      - run: echo "Start deploy"
      - run: yarn install
      - run: yarn build
      - run: yarn deploy
workflows:
  version: 2
  build_and_test:
    jobs:
      - build
      - lint:
          requires:
            - build
      - test:
          requires:
            - build
      - e2e:
          requires:
            - build
      - deploy:
          requires:
            - lint
            - test
            - e2e
          filters:
            branches:
              only: release
```

* deploy は lint, test, e2e が全て終わってから実行される
* また、branch が　release のときだけ実行

# GitHub 上から master ブランチに protection をかける
* Settings → Branches から protection をかけ、CI が通らないと master にマージできないようにする

<img width="621" alt="master_protection.png" src="https://qiita-image-store.s3.amazonaws.com/0/28573/cefaafbe-7a5c-72c0-191e-0e871e330bf1.png">

# 最後に
* README に Status Badge をつける！

# まとめ
* Pull Request 作成時に CircleCI でビルド・テストを走るようにした
    * lint, test, e2e すべてが SUCCESS にならないとマージできない
* release ブランチへマージすると Firebase へデプロイするようにした