メドピア開発者ブログ

集合知により医療を再発明しようと邁進しているヘルステックカンパニーのエンジニアブログです。読者に有用な情報発信ができるよう心がけたいので応援のほどよろしくお願いします。

GitHub メンションを Slack DM する機能を Serverless Framework で作った話

メドピアマッスル部見習い kenzo0107 です。

今回は GitHub のコミュニケーションを円滑にすべく導入した GitHub 通知の Slack DM 機能になります。

github.com

導入経緯

GitHub.com でイシューコメントやプルリクエスト等でコメントをした、してもらった、時に気づかず放置されてしまうことが度々ありました。

Slack 上で
「あれ、どうなりました?」と聞くと、
「コメントくれてたんですね、すいません、気づきませんでした」と。

この防止策の一環として導入したのが、 GitHub メンションを Slack DM に通知する機能になります。

Slack には既に GitHub アプリあるけど?

Slack には既に GitHub アプリがありますが、個人宛てに DM 通知するには個々が認証する等、一手間あります。

その為、認証等せずとも、GitHub.com 通知を個人宛のメンションとして Slack DM する仕組みが必要と考えました。

新入社員さんに一手間かけてもらうことなく、スムーズに GitHub + Slack のコミュニケーションを体験してもらえるようにしました。

システム構成

f:id:kenzo0107:20190228125632p:plain

  1. mogezo 君が GitHub イシュー or プルリク コメントで hogeko さんへメンション
  2. GitHub Webhook を AWS API Gateway で受け、
  3. Lambda を実行し、
  4. hogeko さんへ Slack DM 通知する

図の点線で囲んだ AWS 内の処理を Serverless Framework で構築しました。

導入手順

以下導入手順の大まかな流れです。

  1. GitHub Webhook 設定
  2. Slack Incoming Webhook URL 発行
  3. 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

f:id:kenzo0107:20190215182516p:plain

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 のプルリクエストでメンションします。

f:id:kenzo0107:20190227143042p:plain

するとすぐに Slack DM 通知がきます。

f:id:kenzo0107:20190227123309p:plain

通知がこないな、という時に

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 に設定してください。

*2:nodenv でセットアップしています。

*3:特段これでなくてはいけないということではないので、適宜設定してください。

*4:https:// を含めるとエラーになるかと思います。

*5:serverless-secrets-plugin という暗号化する npm 管理可能な package もあるのですが、暗号化時にパスワードを設定する必要があります。そのパスワードの管理をAWS パラメータストアにすると、結局、KMS が必要となり、秘密情報を取得する手間がさらに増えてしまう為、導入経験がある yaml_vault にしました。その他にもあろうかと思いますが、もし良いのあるよ!という場合は是非教えてください!

*6:命名規則がない場合はこの辺りを DB に持たせても良いかもしれません。