GitHub Actionsを使ってWebサイト(EC2)の更新を自動化する



GitHub Actionsを使ってWebサイトの更新を自動化する手法を備忘録がてらにご紹介します。
この記事を読むことで、下記の作業を自動化できます。

1. Node.jsを用いた静的アセット(html,css,js)のビルド
2. scpを用いたビルドアーティファクトのAWS EC2インスタンスへのデプロイ
3. slackへの通知

## 技術・ツール:hammer_pick:
前提となる技術とツールをご紹介します。
Node.js、EC2インスタンス、slackは既に用意されているものとして説明します。

| 名前 |
| ---- |
| GitHub Actions |
| AWS EC2 |
| Node.js(v12.18.3) |
| slack |

## GitHub Actions を始める前に
slackへの通知やEC2インスタンスへのssh接続が必要となるため、GitHubのsecretsを使用して秘匿情報を隠蔽します。
[ Settings > Secrets > New secret ]より、下記のsecretsを追加してください。

![85b5fbbf85f9f4aa46660dc1aae31e25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/81942/88eb4fdd-1767-ad06-b0e1-340000be1c4e.png)


| 名前 | 用途|
| ---- | ---- |
| EC2_HOST  | EC2インスタンスへ接続するためのホスト名です。 |
| EC2_KEY | EC2インスタンスへ接続するための秘密鍵です。 |
| EC2_OUTPUT_DIR | 実際に公開されるサイトのドキュメントルートです(`/var/www/html`が一般的ですが、各種Webサーバの設定に依存します)。 |
| EC2_USER | EC2インスタンスへ接続するためのユーザー名です。 |
| SLACK_WEBHOOK_URL | slackのAppディレクトリで確認できるIncoming WebhookのURLです。詳しくは[こちら](https://slack.com/intl/ja-jp/help/articles/115005265063-Slack-%E3%81%A7%E3%81%AE-Incoming-Webhook-%E3%81%AE%E5%88%A9%E7%94%A8)をご確認ください。 |

## GitHub Actions の用意
**詳細な説明が不要な場合は、下記のymlファイルをコピペすれば問題なく動くと思います**(たぶん)。

GitHub Actionsを使うには、リポジトリのルートディレクトリに `.github/workflows/file-name.yml` というファイルを追加する必要があります。このymlファイルの構文は[公式のリファレンス](https://docs.github.com/ja/actions/reference/workflow-syntax-for-github-actions)を参考にするのが良いと思います。
今回は、ビルド・デプロイ・slack通知を自動化したいので、下記のようなymlファイルを書きます。

```yml:build-deploy-notify.yml
name: Build and Deploy to EC2

on:
  push:
    branches:
      - master

env:
  SSH_PRIVATE_KEY: ${{ secrets.EC2_KEY }}
  REMOTE_HOST: ${{ secrets.EC2_HOST }}
  REMOTE_USER: ${{ secrets.EC2_USER }}
  TARGET: ${{ secrets.EC2_OUTPUT_DIR }}
  SOURCE: 'public'
  SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
  SLACK_CHANNEL: ci

jobs:
  build-and-deploy:
    name: Build and Deploy
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2

      - name: Use Node.js v12.18.3
        uses: actions/setup-node@v1
        with:
          node-version: '12.18.3'

      - name: Build
        run: |
          npm install
          npm run build

      - name: Deploy to EC2
        uses: easingthemes/ssh-deploy@v2.1.1

      - name: Slack Notification on Success
        if: success()
        uses: rtCamp/action-slack-notify@v2.0.2
        env:
          SLACK_TITLE: Deploy Success
          SLACK_COLOR: good

      - name: Slack Notification on Failure
        if: failure()
        uses: rtCamp/action-slack-notify@v2.0.2
        env:
          SLACK_TITLE: Deploy Failure
          SLACK_COLOR: danger
``` 

## yml ファイルの詳解
### `name`
Workflowの名前となり、実際にGitHub上のActionsページにて表示される名前です。処理に直接的な影響は及ぼしません。
### `on`
Workflowが実行される条件の部分です。
今回はmasterブランチにpushされたイベントを検知して、Workflowが実行されるようになっています。
push以外にも[様々なイベントをトリガーとして設定可能](https://docs.github.com/ja/actions/reference/events-that-trigger-workflows)で、ブランチも複数選択可能です。

```yml:build-deploy-notify.yml
# workflowの名前です。
name: Build and Deploy to EC2

# masterブランチにpushしたときのイベントをトリガーにしています。
on:
  push:
    branches:
      - master
``` 

### `env`
このWorkflowにおける環境変数です。
`${{ secrets.SOME_SECRET }}` で [ Settings > Secrets ]で設定した値を使用することができます。
後述しますが、今回いくつかの外部Actionを使用しており、ここで設定した環境変数は外部のActionを使用する際に必須のパラメータとなります。GitHub Actionsは[Marketplace](https://github.com/marketplace?type=actions)に公開されている外部のアクションを使用することができます。

```yml:build-deploy-notify.yml
env:
  SSH_PRIVATE_KEY: ${{ secrets.EC2_KEY }}
  REMOTE_HOST: ${{ secrets.EC2_HOST }}
  REMOTE_USER: ${{ secrets.EC2_USER }}
  TARGET: ${{ secrets.EC2_OUTPUT_DIR }}
  # publicはビルドアーティファクトが出力されるディレクトリです。
  # このプロジェクトではWebpackを使って、静的アセットをpublicディレクトリに出力しています。
  SOURCE: 'public'
  SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
  SLACK_CHANNEL: ci
```

### `jobs`
実際の処理群を記述する場所です。

`steps`にビルド・デプロイ・通知などの処理をそれぞれ記述しています。
`Use Node.js v12.18.3`で、Node.jsとnpmのセットアップ、`Build`で`npm scripts`を実行、 `Deploy EC2`でEC2インスタンスへのデプロイ、`Slack Notification on Success(or Failure)`でslackへの通知作業を行っています。ここまでの記述でわかるとおり、実際の処理はほとんど書いていません。これは`uses`に記述している外部のアクションを用いているからです。
[easingthemes/ssh-deploy@v2.1.1](https://github.com/easingthemes/ssh-deploy)では、scpを用いた静的アセットの輸送を、[rtCamp/action-slack-notify@v2.0.2](https://github.com/rtCamp/action-slack-notify)では、Incoming Webhookを用いたSlackへの通知をそれぞれ担っています。また、環境変数`env`は各ステップごとにも設定することが可能で、slackへの通知では成功・失敗でそれぞれメッセージや文字色を分けるよう設定しています。

いずれかのstep中にエラーが発生すると当該のWorkflowは失敗となります。
ビルドやデプロイなど、失敗する可能性が0%ではない処理を含んでいる場合は、成功・失敗の条件分岐をさせることも可能で、今回はSlackの通知をそれぞれ成功・失敗で出し分けられるようにしています。
`if:failure()`もしくは`if:success()`で条件分岐させています。


```yml:build-deploy-notify.yml
jobs:
  build-and-deploy:
    name: Build and Deploy
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2

      - name: Use Node.js v12.18.3
        uses: actions/setup-node@v1
        with:
          node-version: '12.18.3'

      - name: Build
        run: |
          npm install
          npm run build

      - name: Deploy to EC2
        uses: easingthemes/ssh-deploy@v2.1.1

      - name: Slack Notification on Success
        if: success()
        uses: rtCamp/action-slack-notify@v2.0.2
        env:
          SLACK_TITLE: Deploy Success
          SLACK_COLOR: good

      - name: Slack Notification on Failure
        if: failure()
        uses: rtCamp/action-slack-notify@v2.0.2
        env:
          SLACK_TITLE: Deploy Failure
          SLACK_COLOR: danger
``` 

## まとめ
EC2にデプロイする場合は、AWSのCodePipeLine(CodeBuild,CodeDeploy)を使うことが一般的かと思いますが、今回はあえてGitHub Actionsを使ってみました。それぞれメリット・デメリットがあるので、プロジェクトにあった技術を選定するのが良いと思います。GitHub Actionsを使えば、デプロイ先をEC2以外にすることもできますし、通知先をSlack以外にすることも可能かと思いますので、可用性や汎用性といった部分が非常に高いように感じました。価格も無料ですし、コードのテストを追加したいといった場合にCI的に扱えることも魅力の一つかなと思います。