GitHubActions+S3+CloudFrontでキャッシュクリアしてSPAをデプロイする

# はじめに

## ここでやってること

1. 前回は[Dockerでsvelteの開発環境を作成](https://qiita.com/mkin/items/22272dc052e9cdf80a8b)しました
2. 今回は、**svelteをGitHubにpushしたら、GitHubActionsがbuildしてS3にアップロード & CloudFrontのキャッシュ消去して配信**します（svelteでなくても設定自体は可能です）

## AWSの設定

### 1. S3にアップロード用のバケットを作成

今回はCloudFrontで配信するので「パブリックアクセスをすべてブロック」して作成します。
この記事では`svelte-spa`というバケット名で作成しています。
（よくこのシンプルな名前で取れたな...

### 2. CloudFrontを設定

Create Distribution > Web(Get Startedボタンクリック)

- `Restrict Bucket Access` : S3へのアクセスを不可にするためYesを選択
- `Grant Read Permissions on Bucket` : CloudFrontからS3バケットへの読み取りを許可する権限設定をしてもらうためYesを選択

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/134221/38b9240b-1966-46d0-1f93-9a58dae5b006.png)

### 3. デプロイ用のIAMポリシーを作成

- Action
    - `s3:PutObject` : S3にbuildした成果物をアップロードするため
    - `s3:ListBucket` : アップロード先のバケットを取得するため
    - `cloudfront:CreateInvalidation` : キャッシュクリアするため。Invalidationは無効化という意味
- Resource
    - `arn:aws:cloudfront` : 先に設定したCloudFrontの詳細でARNを確認できます
    - `arn:aws:s3` : 先に作成したバケットそのものとその配下すべて

```json:IAMポリシー
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "s3:PutObject",
                "s3:ListBucket",
                "cloudfront:CreateInvalidation"
            ],
            "Resource": [
                "arn:aws:cloudfront::123456789012:distribution/EDFDVBD6EXAMPLE",
                "arn:aws:s3:::<INPUT-YOUR-BUCKET>/*",
                "arn:aws:s3:::<INPUT-YOUR-BUCKET>"
            ]
        }
    ]
}
```

### 4. デプロイ用のIAMユーザーを作成

1. 先に作成したIAMポリシーをアタッチしたグループを作成（ここでは`svelte-deploy`グループとする）
2. 先のグループに属するIAMユーザーを作成（ここでは`svelte-deployer`ユーザーとする）
    - 作成時に表示される`AWS_ACCESS_KEY_ID`と`AWS_SECRET_ACCESS_KEY`は後で使うのでメモしておいてください

### 5. GitHubに設定するSecrets情報をメモ

1. デプロイ用のIAMユーザー`svelte-deployer`の`AWS_ACCESS_KEY_ID`と`AWS_SECRET_ACCESS_KEY`
2. CloufFrontの`Distribution ID`
3. 上記のAWSリソースを作成したAWSリージョン

## GitHubのの設定

### Secrets情報の設定

Settings > Secrets から追加

- `AWS_ACCESS_KEY_ID` : IAMユーザー作成時に取得した値
- `AWS_SECRET_ACCESS_KEY` : IAMユーザー作成時に取得した値
- `DISTRIBUTION ` : CloudFrontの詳細画面から取得した`Distribution ID`の値
- `AWS_REGION ` : 

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/134221/173e80b0-af08-f023-4c47-aeff68e20190.png)

### Actionsの設定

ついに来ました...

Github Actions > set up a workflow yourself をクリック

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/134221/92629857-26dc-8890-00ef-ed2147f76a67.png)

ymlに下記の設定をします。

```yml:.github/workflows/main.yml
name: CI

# トリガーの設定
on:
  push:
    branches: [ master ]

# ジョブの設定（直列に動かしたり並行に動かしたり指定できる）
# 今回はbuildジョブとdeployジョブの2つを設定します
jobs:
  build:
    runs-on: ubuntu-latest

    strategy:
      matrix:
        node-version: [14.5.0]

    steps:
    # masterブランチをチェックアウト
    - uses: actions/checkout@v2
    # nodeでパッケージをインストール&ビルド
    - name: Use Node.js ${{ matrix.node-version }}
      uses: actions/setup-node@v1
      with:
        node-version: ${{ matrix.node-version }}
    - run: npm install
    - run: npm run build
    # ビルド結果をdeployジョブに渡すためにアップロード
    - name: Upload build result
      uses: actions/upload-artifact@v1
      with:
        name: build
        path: public/

  deploy:
    # ジョブは並行で動くので前後関係の指定が必要
    needs: build

    runs-on: ubuntu-latest

    steps:
    # ビルド結果をダウンロード
    - name: Download build result
      uses: actions/download-artifact@v2
      with:
        name: build
        path: public/
    # S3にアップロード
    - name: Publish to AWS S3
      uses: opspresso/action-s3-sync@master
      env:
        AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
        AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
        AWS_REGION: ${{ secrets.AWS_REGION }}
        FROM_PATH: "public/"
        DEST_PATH: "s3://svelte-spa"
    # CloudFrontのキャッシュクリア
    - name: Clear cache in CloudFront
      uses: chetan/invalidate-cloudfront-action@v1.2
      env:
        DISTRIBUTION: ${{ secrets.DISTRIBUTION }}
        PATHS: "/*"
        AWS_REGION: ${{ secrets.AWS_REGION }}
        AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
        AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
```

## 動作確認

何かしらmasterに変更を加えてプッシュしてみます。

### GitHub Actionsのページを確認

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/134221/4715cac3-823a-c190-3b40-96c07f628b84.png)

- 成功したことを確認できます
- 失敗した場合、ログを確認しながら調整してください

### S3にビルド結果が保存されていることを確認

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/134221/e8b393c4-792c-a795-bb6b-1e9fc120b4a0.png)

### CloudFrontのエンドポイントにブラウザからアクセス

CloudFrontのドメイン名+index.htmlでアクセス

![d1l74xlahycwg6.cloudfront.net_index.html.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/134221/f29009cb-50dc-4fb8-c658-eec6c0811d3e.png)

- 成功です！
- 再度、masterに変更を加えても自動デプロイされてることが確認できました!!

# 結びの言葉

## 何でこれしたの？

技術を触れるにつけて、ただやってみたい！という想いだけだと、現場の推進力にはなれないなと、自分の中で最近思うようになりました。やはり、どうやって導入するの？とかリリース作業はどうするの？とか、、、技術単体で完結しない難しさがあります。
じゃあ、自分にそのスキルがあるか？と自問すると、、、無かった。
なので、まずはミニマムでもリリースするまでの感覚を養いたいなと思い着手しました。

後日、テストのジョブを組み込んで、エラーならチャット通知をして、CI/CDのレベルアップが必要だなと思います。

# 参考
- [Github ActionでCloudfrontのキャッシュをクリアする](https://blog-hello-world.web.app/posts/2020-05-15-github-action-invalidate-cloudfront/)