Github ActionsによるAWS lambdaへのzipデプロイ

Github Actionsを全く使ったことがなかったので、学習がてらlambdaにzipを上げてみました。
なお、私はおひとりさま開発なので、チーム開発で参考にならないところがあったらごめんなさい。
また、現在も試行錯誤中なので、この記事がベストというわけではないです。
開発用ブランチはdevelop、マジ用ブランチはmain、lambdaの関数名は`area_information_city`とします。
roleのarnも適宜読み替えてください。

# Github Actionsとは？
Githubが提供するCI/CDツールです。
利用にお金かかりそう...と思いましたが、[ここ](https://github.co.jp/features/actions)に書いてある通り、パブリックリポジトリでは無料です。

# 準備すること
リポジトリに`.github/workflows`というディレクトリを作り、その中にxxxx.yaml(yml)を作ります。
このyamlファイルは[ワークフローファイルと呼びます](https://docs.github.com/ja/actions/learn-github-actions/understanding-github-actions#understanding-the-workflow-file)。
yaml拡張子について蛇足ですが、[YAML公式はyaml推し](https://yaml.org/faq.html)だったようなので、[Githubの例](https://docs.github.com/ja/actions/quickstart#creating-your-first-workflow)ではymlとなっていますが、我が家ではyamlで統一してます。

# ワークフローの計画を立てる
次に、ワークフローの計画を立てます。今回やりたいのは以下です。

#### 開発用ブランチのプッシュ時
開発ブランチではプッシュを検知してmypyとpylintと実行して、コード品質を担保します。

- python3.10の環境を準備する
- poetryをインストールする
- mypyとpylintを実行する

#### マジ用ブランチのプッシュ時
マジブランチではプッシュを検知してビルドとデプロイを行います。

- python3.10の環境を準備する
- AWSの認証情報をロードする
- poetryをインストールする
- poetryでビルドする
- ビルドしたパッケージを一旦ローカルに展開した上でzip化する
- lambdaにデプロイする

# 開発用ブランチのワークフローファイルを作成する
完成したものがこちらです。各項目の意味はコメントを参照。

```test.yaml
# nameにワークフロー名を設定する。これがGithubリポジトリの[Actions]に表示される。
# なお、書かないと表示がフルパスのファイル名になってわかりにくい。
name: Test 
on: # 実行のトリガーアクションを書く。
  push: # pushでトリガーされる。
    branches: # トリガー対象のブランチ。
    - develop

jobs: # 複数ジョブをグルーピング。
  test: # 単一ジョブをグルーピング。ここが[Actions]でジョブ名として表示される。
    runs-on: ubuntu-latest # どの仮想マシン上でジョブを動かすかを決める。なんとMacOSもある...。
    steps: # ジョブ内の処理(ステップ)をグルーピング。
    - name: update python # ステップ名。[Actions]でステップ名として表示される。
      # usesで、Githubやコミュニティが用意してくれているワークフローを使うことができる。
      uses: actions/setup-python@v2
      with: # この例では、ubuntuのpythonを3.10にしている。
        python-version: '3.10'
        architecture: 'x64'

    - name: install poetry
      # コマンドの実行はrunで行う。
      run: curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python -

    - name: set env
      # 環境変数の設定はやや癖があり、以下のような設定をしないといけない。
      run: echo "PATH=${PATH}:$HOME/.poetry/bin" >> $GITHUB_ENV

    - name: checkout branch
      uses: actions/checkout@v2 # このリポジトリをチェックアウトする

    - name: install packages via poetry
      run: poetry install

    - name: check type annotation
      run: poetry run mypy

    - name: check lint
      run: poetry run pylint area_information_city
```

pushを検知すると、以下のような感じで実行されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/161568/bce7e202-d12f-0bc0-8660-a811ef03cddf.png)

# マジ用ブランチのワークフローファイルを作成する
こちらも完成品を提示します。

```build_deploy.yaml
name: Build Deploy
on:
  push:
    branches:
    - main

  workflow_dispatch:

jobs:
  build-deploy:
    runs-on: ubuntu-latest
    steps:
    - name: update python
      uses: actions/setup-python@v2
      with:
        python-version: '3.10'
        architecture: 'x64'

    - name: aws credential
      # AWS公式のワークフローで、認証周りをよしなにやってくれるっぽいです。
      # が、ENVへの設定でも良いのではという気も若干してます。
      # secretsはGithubのSettings->Secrets->Actionsから登録します。
      uses: aws-actions/configure-aws-credentials@v1
      with:
        aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
        aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
        aws-region: us-west-2

    - name: install poetry
      run: curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python -

    - name: set env
      run: echo "PATH=${PATH}:$HOME/.poetry/bin" >> $GITHUB_ENV

    - name: checkout branch
      uses: actions/checkout@v2

    - name: build package
      run: poetry build

    - name: install package to local
      run: pip install --upgrade -t package dist/*.whl

    - name: copy lambda_function
      run: cp lambda_function.py package/.

    - name: zip local package for lambda
      run: (cd package ; zip -r ../artifact.zip . -x '*.pyc')

    # lambdaデプロイは、まず関数があるかどうかをチェック
    # →あるならupdate、ないならcreate、という感じで実行します。
    - name: check function
      # idを付与することにより、後続でステップの終了ステータスを扱えるようになります。
      # https://docs.github.com/ja/actions/learn-github-actions/contexts#steps-context
      id: check-function
      # continue-on-errorで失敗した場合でもジョブを継続するようにします。
      continue-on-error: true
      run: > # このyaml記法(>)は、最終行意外の行末がスペースに変換されます
        aws lambda get-function
        --function-name area_information_city

    - name: create function
      id: create-function
      # ifを利用し、check-functionの結果がfailureだった場合(=関数が存在しない場合)に、
      # このステップを実行するようにします。
      if: steps.check-function.outcome == 'failure'
      run: >
        aws lambda create-function
        --function-name area_information_city
        --zip-file fileb://artifact.zip
        --handler lambda_function.lambda_handler
        --runtime python3.9
        --role arn:aws:iam::999989709999:role/lambda-ex
        --timeout 10

    - name: update function
      # ifを利用し、create-functionがskipされた場合(=関数が存在する場合)に、
      # このステップを実行するようにします。(check-function.outcome == 'success'でも多分OK)
      # なお、continue-on-errorを利用するとconclusionは必ずsuccessになってしまうので、
      # create-functionではoutcomeを使いました。
      # (outcome=continue-on-error適用前の結果 conclusion=continue-on-error的用語の結果)
      if: steps.create-function.conclusion == 'skipped'
      run: >
        aws lambda update-function-code
        --function-name area_information_city
        --zip-file fileb://artifact.zip
```

# おわりに
ざっくりと備忘がてらGithub Actionsの使用例を提示しました。
どなたかの参考になれば幸いです。
にしても、こんな機能がタダで使えるとは、すごい世の中になったものですね...。