CircleCIでfirebase function(Node.js)のCI/CDする

# 概要
GithubとCircleCIの連携はアカウント連携するだけなので、適当にすすめてください。
この記事ではgitのディレクトリ構成とCircleCIの設定ファイル(タスクを記述する)について述べます。

# gitのディレクトリ構成

`firebase init`を実行した階層をgit管理のルートディレクトリとします。

```
.
├── .circleci
├── firebase.json
├── functions
│   ├── index.js
│   ├── node_modules
│   ├── package.json  ←　この階層と
│   └── test
├── node_modules
└── package.json　←　この階層を行き来する
```

もともとfunctionsディレクトリをgit管理のルートディレクトリとしていましたが、firebase CLIの都合上この階層を管理しています。(後述)

# .circleci/config.yml

circleciで実行するタスクはこのファイルに記述しますので、ディレクトリ/ファイルを作成します。
おもにデプロイ用のタスクとテスト用のタスクを記載しています。


```yaml
 version: 2
 jobs:
   function-deploy:
     docker:
       - image: circleci/node:latest
     steps:
       - checkout
       # functions配下のnpm設定
       # 基本的にfunctions配下で作業するため、working_directoryでfunctionsを指定している
       # キャッシュ関連処理ではworking_directoryが利用できない
       - run: sudo npm install -g npm@latest
       # 毎回npm installに時間がかかると面倒なのでキャッシュから取ってくる
       - restore_cache:
          key: dependency-cache-{{ checksum "functions/package.json" }}
       - run: 
          command: npm install
          working_directory: functions
       # 毎回npm installに時間がかかると面倒なのでキャッシュに保存する
       - save_cache:
          key: dependency-cache-{{ checksum "functions/package.json" }}
          paths:
             - functions/node_modules
       # トップレベルのnpm設定 
       # npm run deployは最上位レベルから叩くため、working_directoryを移動しない
       - restore_cache:
          key: dependency-cache-{{ checksum "package.json" }}
       - run: 
          command: npm install
       - save_cache:
          key: dependency-cache-{{ checksum "package.json" }}
          paths:
             - ./node_modules
       - run: 
          command: npm run deploy
   function-test:
     docker:
       - image: circleci/node:latest
     steps:
       - checkout
       - run: sudo npm install -g npm@latest
       - restore_cache:
          key: dependency-cache-{{ checksum "functions/package.json" }}
       - run: 
          command: npm install
          working_directory: functions
       - save_cache:
          key: dependency-cache-{{ checksum "functions/package.json" }}
          paths:
             - functions/node_modules
       - run: 
          # functions直下のpackage.jsonで設定したテストを実行する
          command: npm test
          working_directory: functions
 workflows:
  version: 2
  # testというワークフローを作成
  test:
    jobs:
     # 上の方で設定した'function-test'を実行する
      - function-test:
          filters:
            branches:
              # 対象はdevelopブランチ
              only: develop
  # deployというワークフローを作成
  deploy:
    jobs:
     # 上の方で設定した'function-deploy'を実行する
      - function-deploy:
          filters:
            branches:
              # 対象はmasterブランチ
              only: master

```

キャッシュ系のタスク(restore_cacheとsave_cache)は無くても動くので初期動作の確認時は抜いてもいいと思います。

＃ `save_cache`のpathsに指定するディレクトリ配下がキャッシュされるので、意図せず自身のソースコードをキャッシュしないように気をつけましょう。
＃　キャッシュを消したい場合は、
`key: dependency-cache-{{ checksum "package.json" }}-{{ .Environment.NPM_VERSION }}`
のようにキーを設定することで、`NPM_VERSION`の値をCircleCI管理画面から変更し、フローをRe-runするとキャッシュが消えます。（Git上のソース変更無しでキャッシュがない状態からタスクを実行できる。）

# package.json

トップレベルのpackage.jsonに以下を設定します。
これにより`npm run deploy` でfirebaseのデプロイコマンドが実施されます。
また、firebaseデプロイのために`firebase-tools`をインストールします。

```json
   "scripts": {
    "deploy": "firebase deploy --only functions --token $FIREBASE_TOKEN --project $PROJECT_NAME",
   },
   "devDependencies": {
    "firebase-tools": "^7.0.2"
  }
```

ちなみにこのデプロイコマンドは、コマンド実行した階層直下に`functions`ディレクトリを対象にデプロイするようです。
`functions`ディレクトリにいる状態で、このデプロイコマンドを実行すると直下に`functions`ディレクトリがないため失敗します。

# 環境変数の設定

package.jsonに記載した`$FIREBASE_TOKEN`, `$PROJECT_NAME`はCircleCIの管理画面から設定します。
また、`$FIREBASE_TOKEN`は`firebase login:ci`コマンド実行で取得できます。

CircleCIの環境変数設定はjobs->歯車マークのクリックでそこから設定してください。


![Screen Shot 2019-07-05 at 10.59.36 PM.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/190055/fa1914d1-3f2f-9e5d-b8de-2fc990d1bd33.png)


# さいごに

![Screen Shot 2019-07-05 at 10.57.33 PM.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/190055/0f6ba421-1aa4-b827-d0bd-6bf58fb19658.png)

あとはgithubでdevelopブランチかmasterブランチが動けばタスクが実行されます。
（滅茶苦茶失敗した）