📚

【React】GitHubActionsでS3へのデプロイを自動化する

に公開

GitHubActionsを使用してReact アプリを S3 へ自動デプロイしてみたいと思います。

構築の流れ

  1. AWS で OIDC を作成する
  2. 対象のリポジトリで GitHubActions のワークフローを定義する
  3. 動作確認

AWS で OIDC を作成

GitHubActions→AWS へアクセスできるよう下記記事を参考に OIDC を作成します

・AWS コンソールで作成する方

https://zenn.dev/kuuki/articles/aws-create-oidc/

・Terraform で作成する方

https://zenn.dev/kuuki/articles/aws-create-oidc-terraform/

OIDC が使用するロールのポリシーでs3:PutObjectを許可してください

私が使っているポリシーを一例として載せておきます

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "VisualEditor0",
      "Effect": "Allow",
      "Action": "s3:PutObject",
      "Resource": "arn:aws:s3:::<Reactアプリのアップロード先となるS3バケット名>/*"
    }
  ]
}

<React アプリのアップロード先となる S3 バケット名>は、自分の使用しているバケット名に変更してください

ワークフローを定義する

PR 作成時に動作するワークフロー

plan.yml
name: ci

on:
  pull_request:
    branches:
      - main

jobs:
  ci:
    runs-on: ubuntu-latest
    steps:
      # チェックアウトするアクション
      - uses: actions/checkout@v2
      # 特定バージョンのnodeを設定するアクション
      - name: Use NodeJS
        uses: actions/setup-node@v1
        with:
          node-version: 18.16.0
      # dependencyのキャッシュ
      - name: Cache node_modules
        uses: actions/cache@v1
        with:
          #キャッシュしたファイルとキーを格納する場所(OSによって異なる)
          path: ~/.npm
          #キャッシュのリストアや保存をするためのキー
          key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
          #キャッシュをリストアするためのキーのリスト
          restore-keys: |
            ${{ runner.os }}-node-
      # dependencyのインストール
      - run: npm ci
      # テスト
      - run: npm test

コード解説

まずは、いつ実行されるワークフローか定義します

CI のほうはmain ブランチへのマージリクエストを作成した際に実行するよう定義。

on:
  pull_request:
    branches:
      - main

実行する環境を Ubuntu に指定します

runs-on: ubuntu-latest

steps にワークフローを定義していきます

まずは、リポジトリのチェックアウトと Node.js をインストールします

# チェックアウトするアクション
- uses: actions/checkout@v2
# 特定バージョンのnodeを設定するアクション
- name: Use NodeJS
  uses: actions/setup-node@v1
  with:
    node-version: 18.16.0

毎回、依存関係をインストールすると時間かかるので、node_mpodules をキャッシュしておきます

# dependencyのキャッシュ
- name: Cache node_modules
  uses: actions/cache@v1
  with:
    #キャッシュしたファイルとキーを格納する場所(OSによって異なる)
    path: ~/.npm
    #キャッシュのリストアや保存をするためのキー
    key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
    #キャッシュをリストアするためのキーのリスト
    restore-keys: |
      ${{ runner.os }}-node-

次に依存関係をインストールします

npm ci をつかうと、package-lock.json だけをみてインストールするため、 npm install よりも時間を短縮できます

# dependencyのインストール
- run: npm ci

今回はテストのみ実行します。

他にもフォーマットやバリデーションなどを行ってもいいと思います。

# テスト
- run: npm test

main ブランチへマージしたときに動作するワークフロー

ワークフローを定義している YAML ファイル

apply.yml
name: Deploy to S3

on:
  push:
    branches:
      - main

env:
  # GitHubActionsOIDCRole:作成したIAMロール名
  AWS_ROLE_ARN: arn:aws:iam::<AWSアカウントID>:role/GitHubActionsOIDCRole

permissions:
  id-token: write
  contents: read

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      # チェックアウトするアクション
      - uses: actions/checkout@v2
      # 特定バージョンのnodeを設定するアクション
      - name: Use NodeJS
        uses: actions/setup-node@v1
        with:
          node-version: 18.16.0
      # dependencyのキャッシュ
      - name: Cache node_modules
        uses: actions/cache@v1
        with:
          #キャッシュしたファイルとキーを格納する場所(OSによって異なる)
          path: ~/.npm
          #キャッシュのリストアや保存をするためのキー
          key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
          #キャッシュをリストアするためのキーのリスト
          restore-keys: |
            ${{ runner.os }}-node-
      # dependencyのインストール
      - run: npm ci
      # ビルド
      - run: npm run build
      # awsへのアクセス情報を設定
      - uses: aws-actions/configure-aws-credentials@v1
        with:
          role-to-assume: ${{ env.AWS_ROLE_ARN }}
          aws-region: ap-northeast-1
      # S3にビルドしたものをアップロード
      - run: aws s3 cp --recursive build s3://<アップロード先のS3バケット名>/

コード解説

CD のほうはmain ブランチに push されたときに実行されるようにします

Pull Request をマージしたときだけでなく、git push origin main でプッシュした際にも実行されるのでご注意ください!

on:
  push:
    branches:
      - main

env に今回使用するOIDC のロールを指定します

env:
  # GitHubActionsOIDCRole:作成したIAMロール名
  AWS_ROLE_ARN: arn:aws:iam::<AWSアカウントID>:role/GitHubActionsOIDCRole

GitHub の ID トークンの取得とリポジトリへのアクセスを許可しておきます

permissions:
  id-token: write
  contents: read

steps で CI の時と違うのはビルドして S3 にアップロードするところぐらいです。

S3 にアップロードするために AWS へアクセスするときに先ほどの OIDC のロールを指定します。

# ビルド
- run: npm run build
# awsへのアクセス情報を設定
- uses: aws-actions/configure-aws-credentials@v1
  with:
    role-to-assume: ${{ env.AWS_ROLE_ARN }}
    aws-region: ap-northeast-1
# S3にビルドしたものをアップロード
- run: aws s3 cp --recursive build s3://<アップロード先のS3バケット名>/

動作確認

ワークフローをリモートブランチに push

test ブランチを作成し、ワークフローをコミット、プッシュしていきます

// testブランチを作成
$ git checkout -b test
// testブランチが作成され、そのブランチにいることを確認
$ git branch

// add
$ git add .
// 対象のYAMLファイルがaddされていることを確認
$ git status

// addしたファイルをコミット
$ git commit -m "commit workflow"
// コミット履歴を確認
$ git log

// リモートブランチへpush
$ git push origin test

PR を作成

push できたら、GitHub へアクセスし PR を作成します。

push したリポジトリの画面に行くと ↓ の画像の赤枠のPR 作るボタンを押下します

PR 作成画面に遷移しますので、「Create pull request」を押下しPR を作成します。

PR の詳細画面に遷移し、PR 作成時に動作するワークフローの実行結果を確認します。

青枠内のようにチェックが入っていれば GitHubActions は成功しています。

詳細な実行結果は Hide all checks > Detailsをクリックしてみることができます。

ワークフロー内の 1 つ 1 つの処理の実行結果を見ることができます

main ブランチへマージ

PR 詳細画面に戻り、マージしていきますMerge pull requestをクリックします。

Confirm mergeをクリックします

Delete branchでマージ元のブランチは消しておきます。

GithubActions の実行結果を見に行きます。Actionsをクリックします。

Merge pull request #1 from /testというような名前のワークフローがあります

赤枠内が ✓ となっていれば成功、× となっていたら失敗しています。

また、クリックして詳細画面に遷移できるので、失敗している場合は確認してみてください

次に、S3 を確認してみます

React アプリを保存している S3 バケット内の最終更新日時を見てみます。

GitHubActions を実行した時間になっていれば OK です!

これで、React アプリの自動デプロイを設定できました!

GitHubActions ででたエラー

main ブランチへマージときに実行されるワークフローで出たエラーをまとめます。

同じく遭遇した方はご参考ください

OIDC が使用するロールの信頼関係のリポジトリが正しくない

Error: Not authorized to perform sts:AssumeRoleWithWebIdentity

こんなエラーが出た場合は、 OIDC が使用するロールの信頼関係で設定しているリポジトリが正しいか確認してください

下記のように設定されていれば OK です

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "",
      "Effect": "Allow",
      "Principal": {
        "Federated": "arn:aws:iam::<AWSアカウントID>:oidc-provider/token.actions.githubusercontent.com"
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringLike": {
          "token.actions.githubusercontent.com:aud": "sts.amazonaws.com",
          "token.actions.githubusercontent.com:sub": "repo:<GitHubのユーザ名>/<リポジトリ名>:*"
        }
      }
    }
  ]
}

ワークフローに permissions を設定してない

Error: Credentials could not be loaded, please check your action inputs: Could not load credentials from any providers

これはワークフロー内で permissions を設定していないことが原因かもしれません。

↓ をワークフローを定義しているの YAML ファイルに追記してください

permissions:
  id-token: write
  contents: read

GitHhubActions の permissions ってなんだ? と思った方は下記の記事を読んでみてください

https://zenn.dev/not75743/scraps/926f2693809744

ポリシーに権限が足りない

Run aws s3 cp --recursive build s3://<Reactアプリのアップロード先となるS3バケット名>/
upload failed: build/logo192.png to s3://<Reactアプリのアップロード先となるS3バケット名>/logo192.png An error occurred (AccessDenied) when calling the PutObject operation: Access Denied

PutObject で Access Deniedが出ています。

これは使用しているロールのポリシーで S3 の PutObject を許可しているか確認してください

参考記事

https://qiita.com/suzuki0430/items/de0cc70f0b9d2b1ad00b

https://snowsystem.net/git/github-actions-react-firebase/

https://qiita.com/KOBA-RYOTA/items/dea38dd8007a8007cd8f

https://dev.classmethod.jp/articles/deploy-web-site-with-github-actions

https://dev.classmethod.jp/articles/items-if-error-occures-when-deploying-on-github-actions/

Discussion