【React】GitHubActionsでS3へのデプロイを自動化する
GitHubActionsを使用してReact アプリを S3 へ自動デプロイしてみたいと思います。
構築の流れ
- AWS で OIDC を作成する
- 対象のリポジトリで GitHubActions のワークフローを定義する
- 動作確認
AWS で OIDC を作成
GitHubActions→AWS へアクセスできるよう下記記事を参考に OIDC を作成します
・AWS コンソールで作成する方
・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 作成時に動作するワークフロー
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 ファイル
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 ってなんだ? と思った方は下記の記事を読んでみてください
ポリシーに権限が足りない
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 を許可しているか確認してください
参考記事
Discussion