⚠️ 2019/07 の段階でOutgoing WebhooksはLegacy扱いとなり、
SlackAppへの移行が推奨されています。
AWS Amplifyは通常コミット時に自動的にデプロイのスクリプトが走りますが、 Gitで管理していないデータを取り込んだりする際には、 以下のように amplify-cli のコマンドをローカルから叩いて実行する必要があります。
$ export AWS_PROFILE=deploy-profile
$ amplify push
シンプルにはこれでよいのですが、
上記のような問題から以下のフローのようにSlackからdeployを実行できるようにしたいと思います。
AWS AmplifyにはZapierやJenkinsなど外部のサービス・ツールからでもビルドをトリガするためのwebhookが用意されています。 documentはこちらIncoming Webhooks
AWS Amplifyのコンソールから_ビルドの設定_の下部のIncoming webhooksを新規に作成します。 生成されたCommandをを実行するとビルドを実行できることがわかります。
$ curl -X POST -d {} \\
"<https://webhooks.amplify.ap-northeast-1.amazonaws.com/prod/webhooks?id=xxxx&token=xxxxxx&operation=startbuild>"\\
-H "Content-Type:application/json"
> {"SendMessageResponse":{"ResponseMetadata":{"RequestId":"...
SlackのOutgoing WebHooksでこのURLを呼び出せるといいのですが、
Outgoing WebHooksからPOSTするとコンテンツの形式がJSON
でなくform-urlencoded
で送られてしまうので、うまく送信できないようです 😓
なので、上記のコマンドをLambdaで起動できるようにスクリプトを作成します。
AWS Lambdaを開き、以下のようにnode.jsの関数を作成します。(POSTのリクエストができればいいので、正直言語は何でもいいです)
以下のような、シンプルなPOSTリクエストの関数を作成します。
const https = require('https');
// AWS Amplifyから生成されたもの
const amplifyId = 'xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx';
const token = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
const options = {
hostname: 'webhooks.amplify.ap-northeast-1.amazonaws.com',
port: 443,
path: `/prod/webhooks?id=${amplifyId}&token=${token}&operation=startbuild`,
method: 'POST',
headers: {
'Content-Type': 'application/json'
}
};
exports.handler = (event) => {
return new Promise((resolve, reject) => {
const req = https.request(options, (res) => {
console.log(`statusCode: ${res.statusCode}`);
res.on('data', (d) => {
// レスポンスに`text`を返すことでそれをslackに表示できる
const response = {
text: "[AWS Amplify] build starting..."
};
resolve(response);
});
});
req.on('error', (error) => {
console.error(error);
reject(error);
});
req.end();
});
};
AWS Lambda上で「テスト」を実行し、成功したらOKです。
Slackへのメッセージのカスタマイズは Formatting messages | Slack を参照ください。
AWS API GatewayからLambdaを起動するようにします。
まずはAPI Gatewayで新しいRESTのAPIを作成します。
POSTアクションを作成し、上記で作成したLambda関数を指定します。
クライアントの「テスト」を実行し、正常に動作していることを確認します。(現状引数はなくてもよいので指定する必要はないです)
SlackのOutgoing WebHooksから送られてくるPOSTリクエストは本文のコンテンツの形式(Content-Type)がapplication/x-www-form-urlencoded
になっているので、それをJSONとして扱えるように変換する必要があります。
リクエストのマッピングテンプレートに
application/x-www-form-urlencoded
を追加し、 マッピングテンプレートは以下のようにします。
## POSTで送られてきたデータ変数に入れる
#if ($context.httpMethod == "POST")
#set($rawAPIData = $input.path('$'))
#else
#set($rawAPIData = "")
#end
## &で区切られたKey-Valueをセットにする
#set($tokenisedAmpersand = $rawAPIData.split("&"))
#set($tokenisedEquals = [])
#foreach( $kvPair in $tokenisedAmpersand )
#set($countEquals = $kvPair.length() - $kvPair.replace("=", "").length())
#if ($countEquals == 1)
#set($kvTokenised = $kvPair.split("="))
#if ($kvTokenised[0].length() > 0)
## we found a valid key value pair. add it to the list.
#set($devNull = $tokenisedEquals.add($kvPair))
#end
#end
#end
## "{" と "}"で括り、出力する
{
#foreach( $kvPair in $tokenisedEquals )
#set($kvTokenised = $kvPair.split("="))
"$util.urlDecode($kvTokenised[0])" : #if($kvTokenised[1].length() > 0)"$util.urlDecode($kvTokenised[1])"#{else}""#end#if( $foreach.hasNext ),#end
#end
}
次に外側から呼び出すために「ステージ」が必要なので、 アクションから APIのデプロイ を選択し、新しいステージを作成します。
ステージにデプロイされたPOSTリクエストを確認します。
$ curl -X POST \\
<https://xxxx.execute-api.ap-northeast-1.amazonaws.com/prod/> \\
-H "Content-Type:application/x-www-form-urlencoded"
> {"text": "..."}
Lambda関数で定義した戻り値が表示されていることを確認できます。
slackのapps設定ページ(https://[YOUR_ORG].slack.com/apps)からoutgoing webhookを追加し、 "Add Configuration"から新規のwebhookを追加する。
例として、以下のように設定する。
amplify-build
Descriptive Label、Customize Name、IconはなんでもOKです。
slackでamplify-build
と発言するとLambdaが動作し、amplifyのビルドが実行されていることが確認できました。
引数等も調整すればいろんなブランチへのデプロイも指示できそうですね。