GithubActions + LocalStack = AWS使うシステムのCI

# この記事は？
[Github Actions](https://github.co.jp/features/actions)と[LocalStack](https://localstack.cloud/)を使って、インフラにAWSを利用しているシステムのCI環境を構築した時のメモ

- **Github Actions**
  - Githubにpushしたりmergeしたりしたら自動であれこれやらせたいときに使う機能
  - masterのコードを取ってきてDockerコンテナ内でビルド＆AWSへデプロイ等ができる
  - Privateリポジトリかつフリープランなら月2,000分まで無料、Publicなら完全無料
- **LocalStack**
  - Dockerコンテナ内に仮想のAWS環境を作れるツール
  - 今回はGithub Actionsでこのコンテナにデプロイして統合テストを実行してみる

LocalStack公式のドキュメントは↓

https://docs.localstack.cloud/ci/github-actions/

サンプルコードは↓

https://github.com/damepo9696/localstack-githubactions-example

# デプロイするシステム
![68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f323834303435352f36633362653836652d353839352d636539622d363839392d6131353235613731363132652e706e.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2840455/b229f107-b7f3-8b01-8f85-c4a7caf425d4.png)
Lambdaを介してS3へファイルをアップロードするシステム
詳しくは[こちらの記事](https://qiita.com/damepo9696/items/b3b38a4c64f76e10b0ac)を参照(ぶっちゃけするまでもないと思う)

# 手を動かす
## workflowの定義
まずはGithub Actionsでやってほしいことを書いていく

プロジェクトの`.github/workflows`以下に適当な名前のyamlファイルを置くことで、Github Actionsがそれを読み込み処理を実行してくれる
このyamlファイルを[**ワークフロー**](https://docs.github.com/ja/actions/using-workflows/about-workflows)という

とりあえず↓のようなファイルを作成してほしい
※今回はyamlファイルに何もかもベタ書きしてるけど実務で利用する場合は適宜スクリプトファイル化した方が見やすいと思う(ITのところとか)

```.github/workflows/integration-test.yaml
name: IntegrationTest

on:
  push:
    branches: [main]

env:
  REGION: ap-northeast-1

jobs:
  test:
    runs-on: ubuntu-20.04

    steps:
      - name: Checkout
        uses: actions/checkout@v3

      - name: Install modules
        run: |
          pip install --upgrade pyopenssl
          pip install localstack awscli-local
          docker pull localstack/localstack
          npm install -g aws-cdk-local aws-cdk
          npm install

      - name: Start LocalStack
        run: localstack start -d

      - name: Check container status
        run: |
          count=0
          while [ $(docker inspect --format='{{json .State.Health}}' localstack_main | jq '.Status == "healthy"') != "true" ];
          do
            count=$(($count + 1))
            echo "waiting..."
            sleep 5
            if [ $count == 10 ]; then
              echo "Check container status timeout"
              exit 1
            fi
          done

      - name: Configure aws credeintals
        run: |
          aws configure set region ${{ env.REGION }}

      - name: Deploy
        run: |
          cdklocal bootstrap
          cdklocal deploy LocalstackStack --require-approval never

      - name: Execute integration test
        run: |
          awslocal lambda invoke \
            --function-name test-function	\
            --payload '{ "file": "test", "key": "test.txt" }' \
            --cli-binary-format raw-in-base64-out \
            --region ${{ env.REGION }} \
            response.json
          awslocal s3api get-object \
            --bucket test-bucket \
            --key test.txt \
            --region ${{ env.REGION }} \
            response.json
```

ちなみに、ワークフローの構文については↓のドキュメントが詳しいのでよくわかんないことがあったら読んでみよう

https://docs.github.com/ja/actions/using-workflows/workflow-syntax-for-github-actions

## このワークフローのポイント
### [on](https://docs.github.com/ja/actions/using-workflows/workflow-syntax-for-github-actions#on)
どんなイベントが起きたときにこのワークフローを実行するかの設定
今回はmainブランチにpushしたときにしているが、PullRequest作成時やmainブランチにマージした時など細かく設定できる


### [runs-on](https://docs.github.com/ja/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idruns-on)
このワークフローを動かす仮想環境を指定する
今回は`ubuntu-20.04`なので↓の環境で動かすことになる

https://github.com/actions/runner-images/blob/main/images/linux/Ubuntu2004-Readme.md

[このページの"Installed Software"](https://github.com/actions/runner-images/blob/main/images/linux/Ubuntu2004-Readme.md#installed-software)以下にインストール済みのソフトが列挙してあるが、これと今回のCIに必要なソフトとを照らし合わせると...

- ✅Node.js
- ✅Python
- ✅Docker
- ✅AWS CLI
- AWS CLI Local
- AWS CDK
- AWS CDK Local
- LocalStack

✅が付いているのは予めインストールされているので、それ以外をインストールするステップを追加してあげればOK、ということになる

### steps.Checkout
ワークフローを実行する対象となるブランチへチェックアウトしているステップ
儀式みたいなもんです

### steps.Install modules
node_modulesやらLocalStackやら色々インストールするステップ

:::note warn
**これいる？ってなりそうなコマンド**
```bash
pip install --upgrade pyopenssl
docker pull localstack/localstack
```
[こちら](https://docs.localstack.cloud/ci/github-actions/#troubleshooting--faq)にもあるようにGithubActionsの`ubuntu-20.04`に入っているpyOpenSSLのバージョンが原因でエラーとなってしまうためアップデートしている
また、[localstackコマンド実行時にDockerHubから最新のイメージをとってきてはくれない](https://docs.localstack.cloud/get-started/#starting-localstack-with-the-localstack-cli)ので、あらかじめ最新版をpullしてきている
:::

### steps.Check container status
LocalStackのコンテナ(localstack_main)の起動に時間がかかるため、ステータスが`healthy`になっているか定期的にチェックするスクリプトを実行
ここはもっとスマートなやり方があると思う...

### steps.Deploy
ここではcdklocalでbootstrap→deployと実行しているだけなので特に問題はない
ただし、**deployコマンド実行時にyes noを聞かれてしまう**のでそれを無効化する必要がある

方法は以下の2通り
1. `--require-approval never`オプションをつける
1. `cdk.json`に`"requireApproval": "never"`を追加する

### steps.Execute integration test
名前そのまま統合テストを実行している
Lambdaを実行してS3に出力された成果物をとってきているだけ

きっちりやるならファイルの中身をjqコマンドで期待値通りか確認したりロググループ見てエラーレベルのログがないか確認いしたりした方がいいと思う

# 動作確認
`.github/workflows/integration-test.yaml`が書けたらさっそくGithubにpushしよう
すると`Actions`タブにこんなのが表示されるので特にエラーがなければOK

![スクリーンショット 2022-09-16 104848.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2840455/36c2eba3-435b-cea7-17c8-511ab4e013a6.png)