はじめに
突然ですが、AWSのAPI Gatewayのクライアント用SDKって使ってますか?
そんでもってどのように取り込んでますか?
自分の場合だとこれまで、
と、手動でSDKの入れ替え作業をしてました。
面倒くさすぎる。。。!
「このまま人力で更新するオペレーションを続けているといつかヒューマンエラーが発生するな・・・」と思いつつも時は過ぎ、
こないだフロントエンド-バックエンド間の疎通がうまく行かない事象が発生し、SDKの更新忘れが原因という事が分かり、ついに恐れていたことが起きてしまいました。
そして良い機会だと思ってSDKの自動更新のパイプラインを構築するために本腰を入れたのでした。
構築するパイプラインの仕様
- 1時間に1回スケジュール起動する
- AWSからSDKをダウンロードする
- 既存のapigClient.jsと、DLしたapigClient.jsを比較する
- apigClient.jsに差分があった場合、SDK更新用のブランチを作成する
- SDK更新用のブランチが既に存在していた場合、ブランチ作成はせずにファイル更新をする
- 既にSDK更新用のPullRequestが作成されているかどうかをチェックする
- SDK更新用PullRequestが存在しない場合、PullRequestを作成する
- PullRequestが作成されたことをSlackにチームメンション付きでRev依頼投稿する
- PR発行イメージ
- Slack通知イメージ
パイプライン全体
name: update api gateway sdk
on:
schedule:
# 1時間ごと定期実行
- cron: '0 * * * *'
workflow_dispatch:
env:
SYNC_BRANCH: hotfix/apigateway_sdk_update
GH_TOKEN: ${{ github.token }}
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
permissions:
id-token: write
contents: write
pull-requests: write
jobs:
build:
name: get sdk
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Configure AWS credentials from account
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ap-northeast-1
- name: 加工場所を作る
run: |
mkdir ./tmp
- name: API GatewayからSDKを取得
id: get_sdk
run: |
aws apigateway get-sdk --rest-api-id [YOUR REST ID] --stage-name [STAGE NAME] --sdk-type javascript ./tmp/apiGateway-js-sdk.zip
- name: zip展開
run: |
unzip ./tmp/apiGateway-js-sdk.zip -d ./tmp
# apigClient内のコードを調整する必要があればここでsedコマンドなどを使って書き換えていく
- name: apigClient.jsの加工
run: |
echo "if Necessary, you can modify apigClient.js on this"
- name: apigClient.jsのchecksumを取得
run: |
echo NEW_SDK_CHECKSUM="$(shasum ./tmp/apiGateway-js-sdk/apigClient.js | awk '{ print $1 }')" >> "$GITHUB_ENV"
echo OLD_SDK_CHECKSUM="$(shasum ./[EXISTING SDK FILE PATH]/apigClient.js | awk '{ print $1 }')" >> "$GITHUB_ENV"
# apigClient.jsだけでなく、同じくDLされるlibディレクトリもまとめてコピーしている
- name: ファイル上書き
if: env.NEW_SDK_CHECKSUM != env.OLD_SDK_CHECKSUM
run: |
\cp -f ./tmp/apiGateway-js-sdk/apigClient.js ./[EXISTING SDK FILE PATH]/
\cp -rf ./tmp/apiGateway-js-sdk/lib ./[EXISTING SDK FILE PATH]/
- name: ブランチ作成/更新
id: create-sync-branch
if: env.NEW_SDK_CHECKSUM != env.OLD_SDK_CHECKSUM
run: |
git config user.email "actions@github.com"
git config user.name "Github Actions"
branch=${{ env.SYNC_BRANCH }}
git switch -C $branch
git add .
git commit -m "update AWS API Gateway SDK from Github actions"
git push -f origin $branch
- name: PR作成済みチェック
id: check_pr
if: env.NEW_SDK_CHECKSUM != env.OLD_SDK_CHECKSUM
run: |
pr_title='API Gateway SDK new Update🚀'
base_branch='master'
echo "::set-output name=count::$(gh pr list -S "${pr_title}"' in:title' -B $base_branch | wc -l)"
echo "::set-output name=pr_title::$pr_title"
echo "::set-output name=base_branch::$base_branch"
- name: PR作成
if: ${{ (env.NEW_SDK_CHECKSUM != env.OLD_SDK_CHECKSUM) && (steps.check_pr.outputs.count == 0) }}
env:
TZ: 'Asia/Tokyo' # タイムゾーン指定
run: |
CURRENT_DATETIME=$(date +'%Y-%m-%d %H:%M:%S')
gh pr create -B ${{ steps.check_pr.outputs.base_branch }} -t "${{ steps.check_pr.outputs.pr_title }}" -b "🕰️make PR date: $CURRENT_DATETIME"
# レビューアーをアサインしたい場合はオプション追加: -r [REVIEWER NAME ex: hoge,puga]
# 本当はチーム指定したいが、現状のgithub actions上だとうまく行かない -r [org]/[team]
# PR作成時はこちらのステップが実行される
- name: Prepare Slack Message
if: ${{ (env.NEW_SDK_CHECKSUM != env.OLD_SDK_CHECKSUM) && (steps.check_pr.outputs.count == 0) }}
id: make-pr-slack-message-creator
run: |
SLACK_MESSAGE="
<!subteam^[YOUR SLACK GROUP ID]>
AWS API Gateway SDKの更新が確認されたため、
PRを作成しました。
Revをお願いします。"
echo "::set-output name=slack-message::${SLACK_MESSAGE//$'\n'/'%0A'}"
# PR作成時はこちらのステップが実行される
- name: Slack Notification on Failure
uses: rtCamp/action-slack-notify@v2.0.2
if: ${{ (env.NEW_SDK_CHECKSUM != env.OLD_SDK_CHECKSUM) && (steps.check_pr.outputs.count == 0) }}
env:
SLACK_USERNAME: SDK更新通知
SLACK_CHANNEL: [YOUR SLACK CHANNEL]
SLACK_TITLE: AWS API Gateway SDKが更新されたためPRを作成
SLACK_COLOR: good
SLACK_LINK_NAMES: true
SLACK_MESSAGE: '${{ steps.make-pr-slack-message-creator.outputs.slack-message }}'
# 置き換えた時の資材を証跡として取っておく
- uses: actions/upload-artifact@v2
with:
name: target_SDK.zip
path: ./tmp/apiGateway-js-sdk/
また、.gitignore
に以下を追加している
/tmp/
githubに登録したsecret
key | param |
---|---|
SLACK_WEBHOOK_URL | Slackのwebhook URL |
AWS_ACCESS_KEY_ID | awsへアクセスするuser/roleのid |
AWS_SECRET_ACCESS_KEY | awsへアクセスするuser/roleのkey |
詳細(pointだけ)
AWSへ接続するための設定
- aws cliコマンドを使うために設定する
- name: Configure AWS credentials from account
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ap-northeast-1
API GatewayからSDKを取得
- キモの部分。
aws apigateway get-sdk
コマンドを使ってSDKを作成した一時ディレクトリにDLしている - コマンドの詳細はこちら(公式doc)
-
[YOUR REST ID]
は下の画像のxxxxxx
の部分
- name: API GatewayからSDKを取得
id: get_sdk
run: |
aws apigateway get-sdk --rest-api-id [YOUR REST ID] --stage-name [STAGE NAME] --sdk-type javascript ./tmp/apiGateway-js-sdk.zip
apigClient.jsを加工
- sampleとしてechoしか動かしてないけど、ここ大事だったりする
- apigClient.jsはステージを指定してDLしているため、中のロジックや定義も当たり前だがそのステージに合わせたものとなっている
- Reactのdevelopment,productionsなどenvによってAPIの接続先を切り替えたい場合などはそのままのapigClient.jsだと特定のステージにしか接続しにいかない
- よってsedコマンドなどを使って、apigiClient.jsをenvに対応するように修正が必要なケースがある
# apigClient内のコードを調整する必要があればここでsedコマンドなどを使って書き換えていく
- name: apigClient.jsの加工
run: |
echo "if Necessary, you can modify apigClient.js on this"
既存ファイルとの比較
- apigClient.jsに対しての調整が終わった後、そのファイルと既存のファイルで比較を行う
- 比較には
shasum
コマンドを使ってチェックサムレベルでの比較を行う - 後続のstepでのifで用いるため、ここではチェックサムを取るだけ
- name: apigClient.jsのchecksumを取得
run: |
echo NEW_SDK_CHECKSUM="$(shasum ./tmp/apiGateway-js-sdk/apigClient.js | awk '{ print $1 }')" >> "$GITHUB_ENV"
echo OLD_SDK_CHECKSUM="$(shasum ./[EXISTING SDK FILE PATH]/apigClient.js | awk '{ print $1 }')" >> "$GITHUB_ENV"
ファイルの上書き
- 既存ファイルと差分が出ていれば実行する
- apigClient.js以外に、一緒にDLされるlibディレクトリ配下のライブラリについてもまるごと換装している
# apigClient.jsだけでなく、同じくDLされるlibディレクトリもまとめてコピーしている
- name: ファイル上書き
if: env.NEW_SDK_CHECKSUM != env.OLD_SDK_CHECKSUM
run: |
\cp -f ./tmp/apiGateway-js-sdk/apigClient.js ./[EXISTING SDK FILE PATH]/
\cp -rf ./tmp/apiGateway-js-sdk/lib ./[EXISTING SDK FILE PATH]/
ブランチの作成・更新
- 既存ファイルと差分が出ていれば実行する
-
git switch -C
コマンドで既にブランチがあろうと無かろうと強制的にブランチを作成する -
git push -f origin $branch
コマンドで強制的に修正をブランチにpush -
-f
オプションが無いと「リモートリポジトリから先にpullして同期取ってくれ」って怒られる - SDK更新専用branchだし、毎回masterをcheckoutしてそこからbranch強制作成しているので問題ないと判断して
-f
オプションを許容する
- name: ブランチ作成/更新
id: create-sync-branch
if: env.NEW_SDK_CHECKSUM != env.OLD_SDK_CHECKSUM
run: |
git config user.email "actions@github.com"
git config user.name "Github Actions"
branch=${{ env.SYNC_BRANCH }}
git switch -C $branch
git add .
git commit -m "update AWS API Gateway SDK from Github actions"
git push -f origin $branch
PullRequestが発行済みかチェック
- 既存ファイルと差分が出ていれば実行する
-
gh pr list
コマンドでmasterブランチに対して特定の文字列を含むタイトル名でPRが発行されているかを確認する - 結果が1以上であれば発行済みと判断する
- name: PR作成済みチェック
id: check_pr
if: env.NEW_SDK_CHECKSUM != env.OLD_SDK_CHECKSUM
run: |
pr_title='API Gateway SDK new Update🚀'
base_branch='master'
echo "::set-output name=count::$(gh pr list -S "${pr_title}"' in:title' -B $base_branch | wc -l)"
echo "::set-output name=pr_title::$pr_title"
echo "::set-output name=base_branch::$base_branch"
PullRequest作成
- 既存ファイルと差分が出ている かつ PRが発行されてなければ実行する
-
gh pr create
コマンドを使ってPRを発行する - 公式によるとteam指定でreviewerをアサインできるはずなのだが、2023/08/10現在時点でgithub actionsでは指定するとエラーになる
- issue発行済み。未だopenの状態
- 個人名をreviewerとして指定するのは可能
- bodyに何も記載がないのも寂しいので、PR発行した時の日時を入れてみた
- name: PR作成
if: ${{ (env.NEW_SDK_CHECKSUM != env.OLD_SDK_CHECKSUM) && (steps.check_pr.outputs.count == 0) }}
env:
TZ: 'Asia/Tokyo' # タイムゾーン指定
run: |
CURRENT_DATETIME=$(date +'%Y-%m-%d %H:%M:%S')
gh pr create -B ${{ steps.check_pr.outputs.base_branch }} -t "${{ steps.check_pr.outputs.pr_title }}" -b "🕰️make PR date: $CURRENT_DATETIME"
# レビューアーをアサインしたい場合はオプション追加: -r [REVIEWER NAME ex: hoge,puga]
# 本当はチーム指定したいが、現状のgithub actions上だとうまく行かない -r [org]/[team]
could not request reviewer: [org]/[team] not found
Error: Process completed with exit code 1.
PR発行したことをSlackに通知
-
rtCamp/action-slack-notify
pluginを使っている - 既存ファイルと差分が出ている かつ PRが発行されてなければ実行する
- メッセージ部分と投稿部分を分けることでメッセージが自由に書ける
-
<!subteam^[YOUR SLACK GROUP ID]>
でSlackのチームに向けてメンションを送っている(画像の例では@front
) - Group Idの取り方はweb版のSlackを開いて、ユーザグループを確認したときにURLも末尾に表示される英数字がそれ
# PR作成時はこちらのステップが実行される
- name: Prepare Slack Message
if: ${{ (env.NEW_SDK_CHECKSUM != env.OLD_SDK_CHECKSUM) && (steps.check_pr.outputs.count == 0) }}
id: make-pr-slack-message-creator
run: |
SLACK_MESSAGE="
<!subteam^[YOUR SLACK GROUP ID]>
AWS API Gateway SDKの更新が確認されたため、
PRを作成しました。
Revをお願いします。"
echo "::set-output name=slack-message::${SLACK_MESSAGE//$'\n'/'%0A'}"
# PR作成時はこちらのステップが実行される
- name: Slack Notification on Failure
uses: rtCamp/action-slack-notify@v2.0.2
if: ${{ (env.NEW_SDK_CHECKSUM != env.OLD_SDK_CHECKSUM) && (steps.check_pr.outputs.count == 0) }}
env:
SLACK_USERNAME: SDK更新通知
SLACK_CHANNEL: [YOUR SLACK CHANNEL]
SLACK_TITLE: AWS API Gateway SDKが更新されたためPRを作成
SLACK_COLOR: good
SLACK_LINK_NAMES: true
SLACK_MESSAGE: '${{ steps.make-pr-slack-message-creator.outputs.slack-message }}'
まとめ
定期的にSDKの更新を見に行って、更新用のPullRequestを自動で作成してくれるようになった。
必要なオペレーションはPRのマージボタンを押すだけに。
お茶を飲む時間が増えてめでたしめでたし。
set-output
使っているところは後々 >> "$GITHUB_ENV"
に直そうかと思う。