github actionsで自動テストし、ReactのアプリをApp Engineに自動デプロイする

この記事では`github actions`を用いて、React+TypeScriptで作成したアプリを自動テストして、App Engineに自動デプロイする方法の一例を紹介します。僕自身、Circle CIに挑戦してみたけど挫折して、試しにGithub actionsを利用してみたら結構すんなりできたので、そういう人にもおすすめかもしれません。

以下、**自動デプロイ→CD、自動テスト→CI**とします。

また、想定している読者は以下の通りです。

- Circle CIに挑戦してみたけど挫折した人
- github actionsが何なのかなんとなくわかる人
- 既に手動であればテストもデプロイもできる人

ちなみに「github actions？なにそれ？」となる人は参考記事を紹介しておきます。

[Github Actionsの使い方メモ](https://qiita.com/HeRo/items/935d5e268208d411ab5a)
[Github Actionsが使えるようになったので使ってみる](https://qiita.com/1915keke/items/8b18097d2981e88eca93)

## どのタイミングでCI/CDが動くのか
まずは、どのタイミングでCICDが走るのか確認しておきましょう。

以下の表をご覧ください。

|  |タイミング  |
|---|---|
|CI|Pull Request(以下PR)が作成されたタイミング|
|CD |PRがmasterブランチにmergeされたタイミング|

今回は、これらのタイミングでCICDが走るようにコードを書きます。

## 最終的に完成したコード(CI)
github actionsでは`.github/workflows/`配下の1つのyamlファイルが1つのワークフローとなります。

今回は、CI用で1つ、CD用で1つのファイルを用意しました。

まずはCI用の`test.yaml`から。

```test.yaml

# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions

name: Node.js CI

on: pull_request

jobs:
  test:
    runs-on: ubuntu-latest

    strategy:
      matrix:
        node-version: [15.x]
        # See supported Node.js release schedule at https://nodejs.org/en/about/releases/

    steps:
      - uses: actions/checkout@v2
      - name: Cache Node.js modules
        uses: actions/cache@v2
        with:
          path: ~/.npm
          key: ${{ runner.OS }}-node-${{ hashFiles('**/package-lock.json') }}
          restore-keys: |
            ${{ runner.OS }}-node-
            ${{ runner.OS }}-
      - name: docker-compose up
        run: docker-compose up -d firestore
      - run: sudo chown -R $(whoami) $(npm config get prefix)/{lib/node_modules,bin,share}
      - run: npm install -g npm@latest
      - run: rm package-lock.json
      - run: rm -rf node_modules
      - run: sed -i 's/babel-jest//g' package.json
      - run: npm install
      - run: CI=false npm run build
      - run: npm test
```

### ファイルの中身について
github actionsの`new workflow`にあるテンプレートに自分なりに付け足していく感じになります。
そのテンプレートに手動でテストするときのコマンドを記載していきます。

僕の場合、手動でテストする場合は、

1. firebase emulatorを起動(docker-compose)
2. npm run test

という手順をとっているので、まずは`docker-compose up -d firestore`、`npm run test`というコマンドを記述したのですが、実際にPRを作成してみると様々なエラーでワークフローが止まります。これに関しては人によって違うと思うのですが、僕の場合はエラーメッセージを調べつつ、エラーを一つ一つ解消するようにコマンドを追加していった結果が上記のようになりました。

## 最終的に完成したコード(CD)

次にapp engineに自動デプロイするための`deploy.yaml`になります。

```deploy.yaml
name: CD

# Controls when the action will run.
on:
  pull_request:
    types: [closed]

# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
  # This workflow contains a single job called "build"
  build:
    # The type of runner that the job will run on
    runs-on: ubuntu-latest
    if: github.event.pull_request.merged == true

    # Steps represent a sequence of tasks that will be executed as part of the job
    steps:
      # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
      - uses: actions/checkout@v2
      - uses: google-github-actions/setup-gcloud@master
        with:
          project_id: ${{ secrets.GCP_PROJECT_ID }}
          service_account_key: ${{ secrets.GCP_SA_KEY }}
          export_default_credentials: true

      # Runs a single command using the runners shell
      - run: rm package-lock.json
      - run: rm -rf node_modules
      - name: npm install, build
        run: |
          npm install
          CI=false npm run build
        env:
          FIREBASE_TOKEN: ${{ secrets.FIREBASE_TOKEN }}

      # Runs a set of commands using the runners shell
      - name: GAE deploy
        run: |
          echo ${{ secrets.GCP_SA_EMAIL }} | 
          gcloud app deploy --quiet
```

### ファイルの中身について

デプロイは手動の場合は、

1. npm run build
2. gcloud app deploy

で行っています。test同様これだけをyamlに記述してもうまくいきません。エラーが色々起こります。
それを一つ一つ解決していき、最終的に上記のようなファイルになっています。

また、

```deploy.yaml
on:
  pull_request:
    types: [closed]
```
としているのですが、これは**PRがクローズされたタイミング、つまりmasterブランチにmergeされたタイミング**でファイルに記述したワークフローが動くというものです。

実行環境(runs-onの部分)は`ubuntu`を利用していますが、これは**gcloudコマンドがデフォルトで利用できる**からです。circle CIだとpathを通さないといけなく、そこで結局挫折したのですが、それがデフォルトで使えるとなるとubuntuを利用しない手はありません。

また、`uses: google-github-actions/setup-gcloud@master`の部分では、githubのsecretsに登録しておいた`gcloudのサービスアカウントの認証情報`を読み込んでいます。

## まとめ
github actionsはcircle CIに比べて、参考となる記事や自分と同じような環境で実行している人の情報が少ないため、色々手探りのような感じになると思いますが、環境ごとのワークフローのテンプレートが豊富に用意されているので、個人的にはcircle CIよりも使いやすい印象を受けました。デプロイでは認証の部分で少してこずりましたが、思いのほか簡単にできたので、github actionsに挑戦してみて良かったと思っています。