GitHubでAWS SESのメールテンプレートを管理するワークフローを構築する

# 困ったこと
業務でAWS SES(Simple Email Service)のemailテンプレートを利用していて、便利なんだけど変更に弱いのがﾋﾞﾐｮｰだった。
テンプレートを変更するためにコマンド打って既存テンプレートを取得して、jsonファイル作って変更してまたテンプレート変更のコマンドを打たなければならない。面倒な上にミスに気付きにくい・管理しにくいと言うデメリットがあった。
んでワークフローに組み込もうと思って調べても記事が少なかったので備忘録にする。

# 実現したいこと
本番環境を管理するブランチ(prd)にメールテンプレートを管理するjsonファイルの変更(追加・削除)がマージ(もしくは直push)された時、本番環境のSESのテンプレートを変更する処理を走らせるようにする。

### 書くこと
- Github Actionsのyaml記載に関して
- 特定のファイルの差分の取り方について

### 書かないこと
- SESの設定方法に関して
- github actionsの初期設定に関して


# 作戦
ほんで作戦を立てたわけだが、だいたい以下の作戦通りに実装が進んだ。
- 対象となるリポジトリにメールテンプレートのjsonファイルを管理するためのフォルダである`mail_templates`を作成し、SESのテンプレート形式に則ったjsonファイルを配置
- github actionsの設定を行う
    - prdブランチに変更がpushされた時かつ`mail_templates`配下のjsonファイルに変更があった時に処理が走るようにする
    - pushしたPRとpush先のbaseブランチ(prd)との差分を取得する
        - 差分の種類を判別する(追加・変更・削除)
    - githubaction処理内にてSESの権限を持つIAMユーザーでAWSにログインし、差分を元にテンプレート変更のコマンドを実行する


# やっていく

## mail_templatesフォルダを作成する。
プロジェクトのrootにmail_templatesフォルダを作成し、その下にjsonファイルを配置する
```
mail_templates
├ uho.json
├ uha.json
└ oho.json
```
一応これでgit管理ができる形になる。

:::note warn
jsonの中身の書き方に関してはこの記事では詳細を記載しないが、json名と中身のテンプレート名はテンプレート削除の際に整合性がとれるように同じにしておいてほしい
:::

## github actionsの設定を行う
※ githubactonsを動くようにする方法は記載してないので動く状態になってる程で話を進めてますので他で調べていただけると！

以下の感じでyamlファイルを作成しておく(ことになると思う)
`.github/workflows/modify_ses_templates.yml`

以下、yamlを記述していく
#### prdブランチに変更がpushされた時かつ`mail_templates`配下のファイルに変更があった時に処理が走るようにする
PRがprdブランチにmergeされた時に発火して欲しかったのですが、
`prdブランチにmergeされる ≒ prdブランチにpushされる`
なので条件は以下の書き方でOKっぽい。
```yaml
on:
  push:
    branches:
      - prd
    paths:
      - "mail_templates/**"
```
翻訳すると、
prdブランチにpushされた時かつmail_templates配下に変更があった時
みたいなことを表現している(らしい)



#### 変数をセットしておく
```yaml
env:
  base_branch: prd # gitコマンドで使う
  region: ap-northeast-1 # AWSの設定で使う
```



#### pushしたPRとpush先のbaseブランチ(prd)との差分を取得する
※ 意外とここが鬼門だったりする
こっからjobを書いていくので`jobs:`と記載してから処理を書いていく

```yaml
jobs:
  get-diff-files:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v3
        with:
          fetch-depth: 0

      - name: Get template diffs
        run: |
          changes=$(git diff -M100% --name-only ${{ github.event.before }} origin/${base_branch} --diff-filter=M --relative mail_templates)
          adds=$(git diff -M100% --name-only ${{ github.event.before }} origin/${base_branch} --diff-filter=A --relative mail_templates)
          deletes=$(git diff -M100% --name-only ${{ github.event.before }} origin/${base_branch} --diff-filter=D --relative=mail_templates)

          echo changes=$changes >> $GITHUB_ENV
          echo adds=$adds >> $GITHUB_ENV
          echo deletes=$deletes >> $GITHUB_ENV
```

最新のubuntuで処理は実行していくので
```yaml
runs-on: ubuntu-latest
```
の記載

##### Checkoutの処理
`uses: actions/checkout@v3`を記述することによりプライベートリポジトリをcheckoutしてくれて、
`fetch-depth: 0`を指定してあげることにより、全タグ・全ブランチ・全履歴を取得するようになるらしいのでこれで差分とか取るための準備ができてるぽい。

この辺はちょっと概念の理解がむずかしいのでふーんと思いながら分かったふりをして記述した。

##### Get template diffs
ほんでここでスクリプトを実行しているのだが、gitコマンドのオプションに癖があるのでその解説と、変数の扱いに関しての解説をする


##### gitコマンドのオプションについて
SESの話に戻るのだが、テンプレートは 変更・追加・削除 のそれぞれのパターンによって実行するコマンドが違う。なので、prdブランチにpushされるものの差分を取得すると言っても変更・追加・削除で分けて取得する必要がある。
そこで役にたつオプションが以下だ。


```sh
—-name-only
# 名前のまんまのオプション。余計なものを取得しないように。
```


```sh
--diff-filter
# ファイルの変更の種類を識別するために。
# M: 変更ファイル取得
# A: 追加ファイル取得
# D: 削除ファイル取得
```

```sh
-M100%
# 削除と追加が同一コミット内で行われた際に、デフォルトでは類似性が50%以上だとadd/deleteではなく、renameとして扱われるため、その閾値を変更した。
```

```sh
--relative: # 変更を取得したい対象のpathを指定するのだが、"="の有無で挙動が違ったので使い分けた
--relative mail_templates # 絶対パスを取得してくれる
--relative=mail_templates # ファイル名のみを取得してくれるので、deleteのコマンドを実行する時にテンプレート名を作り易いかなと思ってdeleteのみ"="ありで指定
```

これはオプションじゃないけど…
```sh
github.event.before
# こいつのおかげでpush(merge)前後の差分を取ることができる。
# 逆にして "origin/${base_branch} ${{ github.event.before }}"としちゃうと取得されるdeleteとaddが逆になるので注意
```


##### 変数の取り扱いを工夫する
それぞれのstep間というか、次のstepでも取得したdiff情報を持つ変数を使いたいので、以下のように記載することで環境ファイルに変数を書き込む。
```sh
echo changes=$changes >> $GITHUB_ENV
echo adds=$adds >> $GITHUB_ENV
echo deletes=$deletes >> $GITHUB_ENV
```



#### AWS SESのコマンドを実行する(これはどっかのサイトをもろ参考にした)
普通に自分のAWSアカウントとか別で使ってるIAMユーザーとか使ってもいいけど、SES権限を持つIAMを作ってそのアカウントでAWSにログインできるといいかもねということで自分はそうした。
IAMユーザーの作り方・ロールのアタッチ方法はググってくれると。
```yaml
      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v1-node16
        with:
          aws-access-key-id: ${{ secrets.ACCESS_KEY }}
          aws-secret-access-key: ${{ secrets.SECRET_ACCESS_KEY }}
          aws-region: ap-northeast-1
```
アクセスキーとシークレットアクセスキーの直書きは怖いので一応githubのsecretsに`ACCESS_KEY`と`SECRET_ACCESS_KEY`格納し、それを利用する(secretsの作成方法に関してはググってね)





#### テンプレート更新コマンドを走らせる
AWSにログインできてる状態になったのでテンプレート更新コマンドを走らせ、テンプレートを更新

コマンドは以下なので、
```sh
# 変更
aws ses update-template --cli-input-json file://ファイルpath --region リージョン名

# 追加
aws ses create-template --cli-input-json file://ファイルpath --region リージョン名

# 削除 ここだけテンプレート名なのでdiffのオプションで工夫した--relativeのかきかたを有効活用できる
aws ses delete-template --template-name テンプレート名 --region リージョン名

```
取得したファイル一覧の値を利用して以下のような感じになる。
```yaml
      - name: Update Templates
        run: |
          if [ -n "$changes" ]; then
            for change in $changes; do
              echo "変更対象ファイル: ${change}";
              aws ses update-template --cli-input-json file://${change} --region ${region}
            done
          fi

          if [ -n "$adds" ]; then
            for add in $adds; do
              echo "追加対象ファイル: ${add}";
              aws ses create-template --cli-input-json file://${add} --region ${region}
            done
          fi

          if [ -n "$deletes" ]; then
            for delete in $deletes; do
              template_name=$(basename $delete .json) #　　ここでファイル名からテンプレート名に変換をしている
              echo "削除対象テンプレート: ${template_name}";
              aws ses delete-template --template-name ${template_name} --region ${region}
            done
          fi
```

## 結果的に出来上がったファイル
```yaml:.github/workflows/modify_ses_templates.yml
name: Modify ses templates
on:
  push:
    branches:
      - prd
    paths:
      - "mail_templates/**"

env:
  base_branch: prd
  region: ap-northeast-1

jobs:
  get-diff-files:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v3
        with:
          fetch-depth: 0

      - name: Get template diffs
        run: |
          changes=$(git diff -M100% --name-only ${{ github.event.before }} origin/${base_branch} --diff-filter=M --relative mail_templates)
          adds=$(git diff -M100% --name-only ${{ github.event.before }} origin/${base_branch} --diff-filter=A --relative mail_templates)
          deletes=$(git diff -M100% --name-only ${{ github.event.before }} origin/${base_branch} --diff-filter=D --relative=mail_templates)

          echo changes=$changes >> $GITHUB_ENV
          echo adds=$adds >> $GITHUB_ENV
          echo deletes=$deletes >> $GITHUB_ENV

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v1-node16
        with:
          aws-access-key-id: ${{ secrets.ACCESS_KEY }}
          aws-secret-access-key: ${{ secrets.SECRET_ACCESS_KEY }}
          aws-region: ap-northeast-1

      - name: Update Templates
        run: |
          if [ -n "$changes" ]; then
            for change in $changes; do
              echo "変更対象ファイル: ${change}";
              aws ses update-template --cli-input-json file://${change} --region ${region}
            done
          fi

          if [ -n "$adds" ]; then
            for add in $adds; do
              echo "追加対象ファイル: ${add}";
              aws ses create-template --cli-input-json file://${add} --region ${region}
            done
          fi

          if [ -n "$deletes" ]; then
            for delete in $deletes; do
              template_name=$(basename $delete .json)
              echo "削除対象テンプレート: ${template_name}";
              aws ses delete-template --template-name ${template_name} --region ${region}
            done
          fi
```


以上、めでたしめでたし。