Nuxt3 × S3のデプロイを完全自動化！GitHub ActionsとSlack活用術

bravesoft株式会社でフロントエンドエンジニアをしているみくと申します。
今回、アドベントカレンダーの記事として、Nuxt3で作成したSSGのサイトを自動化した話をまとめてみました:santa_tone1:
この自動化を行う前は、デプロイ権限を持ったエンジニアが多い時1日に複数回デプロイをしなくてはならなかったのですが、自動化後はデプロイの手間はもちろん、デプロイ後の連絡不要になったので、かなり負荷が減りました！
## この記事でできること:lifter_tone1:
1. SSGのNuxt3を自動デプロイ
2. Slackにデプロイ成功時/失敗時、通知を送る
## 前提
- Nuxt3のSSG
- GitHub
- AWSのS3権限を持ったaccess_keyとsecret_keyを発行してあること
## 構築:information_desk_person_tone1:
### Slackアプリを作成
https://api.slack.com/apps
CreateNewAppから選択
![スクリーンショット 2024-12-19 15.40.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2363725/b5fcb293-0bfa-6b7c-0f41-9683159fa099.png)
From scratchで作成する
![スクリーンショット 2024-12-19 15.41.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2363725/f5477c1a-02ad-b1cb-9c53-3568050e92a1.png)
サイドバーのOAuth & Permissionsから、Scopesでincoming-webhookを追加
![スクリーンショット 2024-12-19 15.49.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2363725/a06674dd-9927-b97d-31b2-d9a00fc01e76.png)
サイドバーのIncoming WebhooksからAdd New Webhook to WorkspaceでWebhookのURLを追加する。発行されたURLはコピーしておく

### GitHubの設定
リポジトリの設定ページのサイドバーからSecrets and variablesのActionsへ
![スクリーンショット 2024-12-19 15.52.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2363725/6f54660d-7a34-4b7b-6b54-6405be5705da.png)
New repository secretから各キーを追加する
| 環境変数名                   | 説明                                   |
|-----------------------------|--------------------------------------|
| `AWS_ACCESS_KEY_ID`         | AWSの `access_key_id`。S3へのアクセスに必要。       |
| `AWS_SECRET_ACCESS_KEY`     | AWSの `secret_access_key`。S3へのアクセスに必要。   |
| `AWS_S3_CONTENTS_BUCKET_DEV`| 開発環境用のS3バケット名。                    |
| `AWS_S3_CONTENTS_BUCKET_STG`| ステージング環境用のS3バケット名。               |
| `SLACK_INCOMING_WEBHOOK_URL`| Slackアプリを作成で作成したWebhookのURL。|
## ymlの追加をする
プロジェクトのルート直下に.github>workflows>deploy.ymlを追加する。
```
name: Build and Deploy Workflow

on:
  pull_request:
    branches:
      - develop
  push:
    branches:
      - develop
      - staging

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v2

      - name: Setup Node.js
        uses: actions/setup-node@v2
        with:
          node-version: '16.13.2'

      - name: Install dependencies
        run: npm install

      - name: Generate static files
        id: generate
        run: npm run generate
        continue-on-error: true

      - name: Check if generation successful
        id: check_generation
        run: |
          if [ $? -eq 0 ]; then
            echo "Generation successful!"
            echo "::set-output name=status::success"
          else
            echo "Generation failed!"
            echo "::set-output name=status::failure"
            exit 1
          fi
        continue-on-error: true

      - name: Slack Notification - Build Failure
        if: steps.generate.outcome == 'failure'
        uses: rtCamp/action-slack-notify@v2
        env:
          SLACK_WEBHOOK: ${{ secrets.SLACK_INCOMING_WEBHOOK_URL }}
          SLACK_COLOR: '#dc143c'
          SLACK_LINK_NAMES: true
          SLACK_TITLE: 'Build Failed :boom:'
          SLACK_MESSAGE: |
            @team-devops @team-frontend
            Repository: ${{ github.repository }}

      - name: Deploy to Development
        if: github.event_name == 'push' && github.ref == 'refs/heads/develop'
        id: deploy_dev
        env:
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
        run: aws s3 sync --delete --region ap-northeast-1 ./dist s3://${{ secrets.AWS_S3_CONTENTS_BUCKET_DEV }}/
        continue-on-error: true

      - name: Deploy to Staging
        if: github.event_name == 'push' && github.ref == 'refs/heads/staging'
        id: deploy_stg
        env:
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
        run: aws s3 sync --delete --region ap-northeast-1 ./dist s3://${{ secrets.AWS_S3_CONTENTS_BUCKET_STG }}/
        continue-on-error: true

      - name: Slack Notification - Deploy Success
        if: steps.deploy_dev.outcome == 'success' || steps.deploy_stg.outcome == 'success'
        uses: rtCamp/action-slack-notify@v2
        env:
          SLACK_WEBHOOK: ${{ secrets.SLACK_INCOMING_WEBHOOK_URL }}
          SLACK_COLOR: '#5cb85c'
          SLACK_LINK_NAMES: true
          SLACK_TITLE: 'Deploy Successful :rocket:'
          SLACK_MESSAGE: |
            @team-devops @team-frontend
            Repository: ${{ github.repository }}

      - name: Slack Notification - Deploy Failure
        if: steps.deploy_dev.outcome == 'failure' || steps.deploy_stg.outcome == 'failure'
        uses: rtCamp/action-slack-notify@v2
        env:
          SLACK_WEBHOOK: ${{ secrets.SLACK_INCOMING_WEBHOOK_URL }}
          SLACK_COLOR: '#dc143c'
          SLACK_LINK_NAMES: true
          SLACK_TITLE: 'Deploy Failed :boom:'
          SLACK_MESSAGE: |
            @team-devops @team-frontend
            Repository: ${{ github.repository }}
```
#### 解説
__ワークフローのトリガー__
``` yaml
on:
  pull_request:
    branches:
      - develop
  push:
    branches:
      - develop
      - staging
```
develop ブランチ：プッシュとプルリクエストの両方をトリガーします。プルリクエスト時はビルドできるかどうかのみをチェックしています。これを行うことで、ビルドできないプルリクエストをマージすることを防ぐことができます。
staging ブランチ：プッシュ時のみトリガーします。

__ビルドを行い、生成が成功したかを確認__
``` yaml
- name: Generate static files
  id: generate
  run: npm run generate
  continue-on-error: true
```
npm run generate が失敗しても、続行できるようにしています。

```yaml
- name: Check if generation successful
  id: check_generation
  run: |
    if [ $? -eq 0 ]; then
      echo "Generation successful!"
      echo "::set-output name=status::success"
    else
      echo "Generation failed!"
      echo "::set-output name=status::failure"
      exit 1
    fi
```
$? を使い、成功か失敗かを確認します。
__Slack通知__
ビルドやデプロイの成否をSlackに通知
```yml
- name: Slack Notification - Build Failure
  if: steps.generate.outcome == 'failure'
  uses: rtCamp/action-slack-notify@v2
  env:
    SLACK_WEBHOOK: ${{ secrets.SLACK_INCOMING_WEBHOOK_URL }}
    SLACK_COLOR: '#dc143c'
    SLACK_TITLE: 'Build Failed :boom:'
```
- 失敗時には赤色で通知 (SLACK_COLOR: '#dc143c')
![スクリーンショット 2024-12-19 16.24.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2363725/18b74733-16a1-34a9-c6d7-f2049c5900d9.png)
- 成功時には緑色 (SLACK_COLOR: '#5cb85c') で通知
![スクリーンショット 2024-12-19 16.20.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2363725/1357edb0-4b51-81d7-773e-7c0f4f52eaa6.png)

__S3へのデプロイ__
aws s3 sync を使って S3 にファイルを同期
```yaml
run: aws s3 sync --delete --region ap-northeast-1 ./dist s3://${{ secrets.AWS_S3_CONTENTS_BUCKET_DEV }}/
```
## おわりに
この記事では、Nuxt3で作成したSSGサイトをGitHub Actionsで自動デプロイする方法と、Slack通知でデプロイの成否をみんなにお知らせする仕組みを紹介しました！
手動でやっていたデプロイ作業がなくなり、Slack通知のおかげで「デプロイ終わったよ！」の連絡も不要になり、チームのコミュニケーションがかなりスムーズになりました:raised_hands_tone1:

こんな感じで、自動化の力を使うとエンジニアの負担をグッと減らせます。もちろん、これが完成形じゃなくて、もっと効率よくできる方法があるかもしれません！
これを読んで「自分のプロジェクトにも使ってみよう！」と思ってもらえたら嬉しいです:sunny: