@ijin

[Michael H. Oshita]

GitHubのeventをLambdaで処理してSlackへ通知

ちょっと前にGitHubのeventをAWS Lambdaで処理して、GitHubやSlackのAPIを叩く仕組みを作ったので、メモ。

材料

  • Github
  • SNS
  • KMS
  • Lambda
  • Slack

やりたいことはこんな感じ。

あるGitHubリポジトリのissuesに特定のコメントが書き込まれると、そのユーザはプロジェクトのteamに自動で追加されて、Slackへ通知が流れるというモノです。

SNS

まずは媒介となるSNSの作成。

1
2
3
4
$ aws sns create-topic --name github --region ap-northeast-1
{
    "TopicArn": "arn:aws:sns:ap-northeast-1:123456789012:github"
}

次にSNSに対してpublishできるIAM userを作成

IAM Policy

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": [
        "sns:Publish"
      ],
      "Sid": "Stmt0000000000000",
      "Resource": [
        "arn:aws:sns:ap-northeast-1:123456789012:github"
      ],
      "Effect": "Allow"
    }
  ]
}

GitHub

  • organization: my_org
  • repository: test
  • team: reader

SNS連携

Webhooks & Services からAmazonSNSと連携

AWS KEYには先ほど作成したIAMユーザのを利用。SNS topicのarnにはregionが書かれているにも関わらず、GitHubの方では明示的に指定が必要。

通知するeventの有効化

さて、GitHubではSNSの場合、hooksのjsonにある通り、push時のeventにしか対応していないので、

hooks link
1
2
3
4
"name": "amazonsns",
"events": [
  "push"
],

GitHubのWebhook APIに従ってissue_commentを追加してやります。

先ほどのSNS連携のhook idを取得するにはGET /orgs/:org/hooks

1
2
export HOOK_ID=$(curl -H 'Uer-Agent: ijin' -X GET -s -H "Authorization: token xxxxxxxxxx" \
https://api.github.com/repos/my_org/test/hooks | jq '.[].id')

編集するにはPATCH /orgs/:org/hooks/:id

1
2
curl -H 'Uer-Agent: ijin' -X PATCH -s -H "Authorization: token xxxxxxxxxx" \
https://api.github.com/repos/my_org/test/hooks/$HOOK_ID -d '{"add_events": ["issue_comment"]}' | jq .

Web UIからは分からないので、ついでにreader teamのIDも取得しておく

1
2
curl -H 'Uer-Agent: ijin' -X GET -s -H "Authorization: token xxxxxxxxxx" \
https://api.github.com/orgs/my_org/teams | jq '.[] | select(.name=="reader")'

(※)User-Agentは必須

これで、誰かがコメントをした時にもSNSが飛びます。

KMS

lambdaでは以下の認証情報を使うので、予めKMSで暗号化しておく。

  • GitHub token
  • Slack webhook

rubyで暗号化する場合

1
2
3
4
require 'aws-sdk'
require 'base64'
kms = Aws::KMS::Client.new(region: 'us-east-1')
Base64.encode64 kms.encrypt(key_id: "alias/ijin", plaintext: 'my plain text code').ciphertext_blob

javascriptの場合

1
2
3
4
5
6
7
var aws = require('aws-sdk');
var kms = new aws.KMS({ region: 'us-east-1' });
var text = 'my plain text code';
kms.encrypt({KeyId: 'alias/ijin', Plaintext: text}, function(err, data) {
  if (err) console.log(err, err.stack);
  else console.log(data.CiphertextBlob.toString('base64'));
});

こうしておくと、SCMに入れても安心。

また、KMSキーの実行権限もlambdaのroleに紐づけておく。

Lambda

Lambda function作成時にはSNSをevent sourceとして指定。

(※) KMSは現在us-eastにしかないので、そこ以外のregionでlambdaを実行する場合は、timeoutは若干長めに指定して置くと良さげ

コードはこんな感じ。

  1. GitHubからSNS hookを受け取って
  2. コメントした内容がjoinの場合
  3. KMSによってGitHubのtokenを復号化し
  4. そのユーザをteamへ追加する
  5. その後、別lambda functionでslackへ通知する

nodeのlibraryを使うともっとスッキリ書けたけど、1 function 1 fileで纏めたかったのでやや冗長なコードになっちゃいました。。

実行

GitHubでコメント

Lambda発動

1
2
3
4
5
6
7
8
9
10
11
2015-08-11T15:42:53.108Z   a09e3c80-403f-1e15-bbb5-55b693433c0e    user: ijin2
2015-08-11T15:42:53.108Z  a09e3c80-403f-1e15-bbb5-55b693433c0e    comment: join
2015-08-11T15:42:55.417Z  a09e3c80-403f-1e15-bbb5-55b693433c0e    status code: 200
2015-08-11T15:42:55.418Z  a09e3c80-403f-1e15-bbb5-55b693433c0e    response:
{
    "state": "pending",
    "url": "https://api.github.com/teams/1234567/memberships/ijin2"
}
2015-08-11T15:42:55.418Z  a09e3c80-403f-1e15-bbb5-55b693433c0e    Added to the team
END RequestId: a09e3c80-403f-1e15-bbb5-55b693433c0e
REPORT RequestId: a09e3c80-403f-1e15-bbb5-55b693433c0e Duration: 5211.48 ms Billed Duration: 5300 ms Memory Size: 128 MB  Max Memory Used: 14 MB   
1
2
3
4
5
6
7
8
9
10
11
2015-08-11T15:42:56.058Z   a25c66fc-403f-11e5-b291-25d4ee441689    Received event:
{
    "username": "ijin2",
    "icon_url": "https://avatars.githubusercontent.com/u/12809425?v=3",
    "text": "Added to the team"
}
2015-08-11T15:42:58.310Z  a25c66fc-403f-1e15-b291-25d4ee441689    200
2015-08-11T15:42:58.311Z  a25c66fc-403f-1e15-b291-25d4ee441689    ok
2015-08-11T15:42:58.311Z  a25c66fc-403f-1e15-b291-25d4ee441689    Successfully posted to Slack!
END RequestId: a25c66fc-403f-11e5-b291-25d4ee441689
REPORT RequestId: a25c66fc-403f-1e15-b291-25d4ee441689 Duration: 2268.06 ms Billed Duration: 2300 ms Memory Size: 128 MB  Max Memory Used: 14 MB   

teamへの追加(招待)

Slackへ通知

結論

簡単にできますね。実はこんな風に作っちゃった2週間後ぐらいにAWSの人も似たようなブログを書いていた事を発見しましたが。まあ、こっちはKMSとSlack使ってるので。。

後、KMSのアイコンが欲しい!

Comments