メドピアマッスル部見習い kenzo0107 です。
今回は GitHub のコミュニケーションを円滑にすべく導入した GitHub 通知の Slack DM 機能になります。
導入経緯
GitHub.com でイシューコメントやプルリクエスト等でコメントをした、してもらった、時に気づかず放置されてしまうことが度々ありました。
Slack 上で
「あれ、どうなりました?」と聞くと、
「コメントくれてたんですね、すいません、気づきませんでした」と。
この防止策の一環として導入したのが、 GitHub メンションを Slack DM に通知する機能になります。
Slack には既に GitHub アプリあるけど?
Slack には既に GitHub アプリがありますが、個人宛てに DM 通知するには個々が認証する等、一手間あります。
その為、認証等せずとも、GitHub.com 通知を個人宛のメンションとして Slack DM する仕組みが必要と考えました。
新入社員さんに一手間かけてもらうことなく、スムーズに GitHub + Slack のコミュニケーションを体験してもらえるようにしました。
システム構成
- mogezo 君が GitHub イシュー or プルリク コメントで hogeko さんへメンション
- GitHub Webhook を AWS API Gateway で受け、
- Lambda を実行し、
- hogeko さんへ Slack DM 通知する
図の点線で囲んだ AWS 内の処理を Serverless Framework で構築しました。
導入手順
以下導入手順の大まかな流れです。
- GitHub Webhook 設定
- Slack Incoming Webhook URL 発行
- serverless framework で API Gateway + Lambda + CloudWatch Logs 構築
GitHub Webhook 設定
Organization の Webhook を作成します。*1
設定項目 | 内容 |
---|---|
Playload URL | 仮設定で良いです (ex. https://hoge )。 後ほど設定します。 |
Content type | application/json |
Secret | 認証の際に必要なパスワード情報です。 適当なランダム値を設定してください。 |
対象イベントは以下としました。
- Commit comments
- Issue comments
- Pull requests
- Pull request reviews
- Pull request review comments
Slack Incoming Webhook 作成
以下リンクから Slack Incoming Webhook URL 作成します。
https://slack.com/services/new/incoming-webhook
Serverless framework で API Gateway + Lambda 構築
事前準備です。
serverless framework プロジェクトを clone します。
git clone https://github.com/medpeer-inc/githubcom2slack cd githubcom2slack
Nodejs は本プロジェクトでは AWS Lambda 最新の 8.10.0 としています。*2
node -v v8.10.0
Serverless Framework インストールします。
npm install -g serverless
関連ライブラリをインストールします。
npm install bundle install -j4 --path vendor/bundle
AWS Access Key ID
, AWS Secret Access Key
を環境変数設定します。
現状弊社では direnv を利用し設定しています。*3
// direnv インストール brew install direnv // 環境変数設定 echo -n 'export AWS_ACCESS_KEY_ID=HOGEFUGA' >> .envrc echo -n 'export AWS_SECRET_ACCESS_KEY=BUDOUBAR' >> .envrc direnv allow .
AWS KMS Key 作成
aws kms create-key { "KeyMetadata": { "AWSAccountId": "xxxxxxxxxxx", "KeyId": "yyyyyyyyyyyyyyyyyyyyyyyy", "Arn": "arn:aws:kms:ap-northeast-1:xxxxxxxxxxx:key/yyyyyyyyyyyyyyyyyyyyyyyy", "CreationDate": 1549615619.872, "Enabled": true, "Description": "", "KeyUsage": "ENCRYPT_DECRYPT", "KeyState": "Enabled", "Origin": "AWS_KMS", "KeyManager": "CUSTOMER" } }
キーのエイリアス作成
aws kms create-alias \ --alias-name alias/githubcom2slack \ --target-key-id yyyyyyyyyyyyyyyyyyyyyyyy
秘密情報の暗号化
今回、秘密情報として扱うのは、以下2 点です。
- GitHub Webhook Secret
- Slack Incoming Webhook URL
AWS Lambda コンソール上で暗号化ボタンをポチッと押すのでなく、暗号化した文字列を環境変数として設定します。
GitHub Webhook Secret 暗号化
aws kms encrypt \ --key-id arn:aws:kms:ap-northeast-1:xxxxxxxxxxx:key/yyyyyyyyyyyyyyyyyyyyyyyy \ --plaintext "<GITHUB_WEBHOOK_SECRET>" \ --query 'CiphertextBlob' \ --output text
Slack Incoming Webhook URL
aws kms encrypt \ --key-id arn:aws:kms:ap-northeast-1:xxxxxxxxxxx:key/yyyyyyyyyyyyyyyyyyyyyyyy \ --plaintext "<SLACK_INCONMING_WEBHOOK_URL>" \ --query 'CiphertextBlob' \ --output text
<SLACK_INCONMING_WEBHOOK_URL>
は https://
の後の ドメインからの文字列を指定してください。*4
上記 2 つ生成された値を secrets.yml に設定します。
- secrets.yml は以下の様になります。
GITHUB_WEBHOOK_SECRET_ENCRYPTED: ***************************** SLACK_INCONMING_WEBHOOK_URL_ENCRYPTED: ********************************* AWS_KMS_KEY_ARN: arn:aws:kms:ap-northeast-1:xxxxxxxxxxx:key/yyyyyyyyyyyyyyyyyyyyyyyy
秘密情報の暗号化
yaml_vault で secrets.yml ファイルを暗号化した secrets.yml.enc をリポジトリで管理します。*5
env \ AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID} \ AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY} \ bundle exec yaml_vault encrypt \ --cryptor=aws-kms \ --aws-region=ap-northeast-1 \ --aws-kms-key-id=alias/githubcom2slack \ secrets.yml -o secrets.yml.enc
KMS key を利用した暗号化・復号権限を特定 IAM Group に絞ることで、そのグループに属するユーザのみ暗号化・復号が可能になります。
GitHub.com ユーザと Slack ユーザを紐付け
以下 2 つを紐づける為に git2slackNames という hash 値を設定します。
- GitHub.com ユーザ名
- Slack ユーザ名
// github username : slack username const git2slackNames = { 'kenzo0107': 'kenzo.tanaka', }
上記の場合、新たにユーザ追加する度にコード修正が必要となります。
もし GitHub.com ユーザ名 と Slack ユーザ名 に命名規則がある場合、以下メソッドで命名規則に沿った変換を実装すると追加の手間がありません。
弊社では命名規則があるので、変換処理をするようにしています。*6
function extractUsernameFromMessage (message) { let usernames = [] let usernameCandidates = message.match(/@[a-zA-Z0-9-]+/g) if (!usernameCandidates) { return usernames } for (let i in usernameCandidates) { var u = usernameCandidates[i].replace('@', '') if (denySlackDmUsers.indexOf(u) > -1) { continue } if (git2slackNames[u]) { usernames.push(`@${git2slackNames[u]}`) } // github username と slack username に命名規則があれば、 // ここで変換 git --> slack username へ変更するも良し } return usernames }
例)命名規則がある場合
- GitHub.com のアカウント名:
medpeer-<firstname>-<lastname>
- Slack のアカウント名:
<firstname>.<lastname>
以下のような変換が可能です。
for (let i in usernameCandidates) { ... ... if (u.match(/^medpeer-/)) { usernames.push(`@${u.replace(/medpeer-/, '').replace(/-/, '.')}`) }
いざデプロイ
sls deploy -v
GitHub Webhook URL 設定
GitHub Webhook URL が仮設定状態だったので、生成されたエンドポイントを設定します。
sls info Service Information service: githubwebhook2slack stage: production region: ap-northeast-1 api keys: None endpoints: POST - https://xxxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/production/webhook functions: githubWebhookListener: githubwebhook2slack-production-githubWebhookListener
上記の例では、 https://xxxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/production/webhook
がエンドポイントになります。
こちらを仮設定だった Github Webhook URL
に設定します。
では、早速試してみましょう!
GitHub.com のプルリクエストでメンションします。
するとすぐに Slack DM 通知がきます。
通知がこないな、という時に
Lambda のログが CloudWatch Logs に流れてますので、ログを確認してください。
そもそもログが出ていないのであれば、 Webhook の設定忘れや URL に誤りがある等チェックしてみてください。
まとめ
弊社では、非エンジニアも GitHub でコミュニケーションしています。
今回の実装によって、コミュニケーションが円滑になったことに加えて、 GitHub 上で明示的にメンションすることが増えた様に思います。
また、 GitHub から API Gateway + Lambda の認証が確立できたことで、 GitHub イベントをトリガーとした AWS リソースの変更に応用する様にもなりました。
こちらについては今後執筆できればと思います。
以上です。 参考になれば幸いです。
(☝︎ ՞ਊ ՞)☝︎是非読者になってください
メドピアでは一緒に働く仲間を募集しています。 ご応募をお待ちしております!
■募集ポジションはこちら
https://medpeer.co.jp/recruit/entry/
■開発環境はこちら
https://medpeer.co.jp/recruit/workplace/development.html
*1:リポジトリを絞りたい場合は、個々のリポジトリの Webhook に設定してください。
*3:特段これでなくてはいけないということではないので、適宜設定してください。
*4:https:// を含めるとエラーになるかと思います。
*5:serverless-secrets-plugin という暗号化する npm 管理可能な package もあるのですが、暗号化時にパスワードを設定する必要があります。そのパスワードの管理をAWS パラメータストアにすると、結局、KMS が必要となり、秘密情報を取得する手間がさらに増えてしまう為、導入経験がある yaml_vault にしました。その他にもあろうかと思いますが、もし良いのあるよ!という場合は是非教えてください!
*6:命名規則がない場合はこの辺りを DB に持たせても良いかもしれません。