CodeBuildのビルド結果をSlackで通知する

[2017-06-22のアップデート](https://aws.amazon.com/jp/about-aws/whats-new/2017/06/aws-codebuild-now-provides-build-notifications/)でCodeBuildのビルド結果をCloudWatchEventsで捕捉できるようになりました。
そのままSNSで通知してもいいのですが、LambdaからIncoming Webhookを使ってSlackに通知してみようと思います。

# 事前準備

以下のことはすでに済んでいる前提で話を進めます。

- CodeBuildのプロジェクト作成
- SlackのIncoming Webhookの設定(URL発行)

# 実装

さくっと済ませたいので[Serverlessフレームワーク](https://serverless.com)を使います。

## serverless.yml設定

まあこいつを見てもらおうか

```yaml:serverless.yml
service: notify-build-result
provider:
  name: aws
  runtime: nodejs6.10
  timeout: 180
  stage: ${opt:stage, self:custom.defaultStage}
  #profileオプション追加
  profile: ${opt:profile, self:custom.defaultProfile}
  #regionオプション追加
  region: ${opt:region, self:custom.defaultRegion}
custom:
  defaultStage: dev
  #何も指定がなければ default profile
  defaultProfile: default
  #何も指定がなければ 北バージニア
  defaultRegion: us-east-1

functions:
  notify:
    handler: handler.notify
    memorySize: 128
    events:
      - cloudwatchEvent:
          event:
            source:
              - "aws.codebuild"
            detail-type:
              - "CodeBuild Build State Change"
            detail:
              build-status:
                - SUCCEEDED
                - FAILED
                - STOPPED
              project-name:
                - my-project-name
          enabled: true
```

### イベントの設定

今回キモになるeventsの設定項目は詳しめに説明します。
cloudwatchEventを指定することで、cloudwatchのイベントをトリガーにできます。
cloudwatchEvent.event 以下の項目は

|項目名|解説|
|:---|:--|
|source|cloudwatchで見る対象 今回はCodeBuildが対象なのでaws.codebuildでOK|
|detail-type|検知するイベントの種類 "CodeBuild Build State Change"を指定するとCodeBuildのステートが変化したタイミングでイベントが発火します。|
|detail|detail以下はCodeBuild特有の設定|
|detail.build-status|CodeBuildのステートがここで設定したものになるとイベントが発火します。用意されているのはSUCCEEDED(成功), FAILED(失敗), STOPPED(中断), PROGRESS(進行中)がありますが、今回は結果を教えてほしいのでPROGRESSは外してます。|
|detail.project-name|監視対象のプロジェクト 配列の形で複数指定することもできます|
|enabled|トリガーの有効/無効|

## Lambdaファンクション

### payload確認

Lambdaファンクションの実装前に、cloudwatchEventからLambdaをコールしたときにevent引数に何が渡されるか確認しておきます。
いや、まじでトリガーによって引数全然違うから確認しとかないと死ぬ。

イベントの種類によってはLambdaのコンソールのテストイベントの設定からこんな感じで見れますが、
![test_event.PNG](https://qiita-image-store.s3.amazonaws.com/0/116434/9fb04623-4455-8a3c-7728-322ad7f13c43.png)

残念ながらCodeBuildのcloudwatchEventはこの方法では見られなかったので、Lambdaのコード内で`console.log(event)`とかやって調べます。

その結果がこちら

```json:event.json
{
  "version": "0",
  "id": "50639ce0-1497-4911-baa0-79d9ca880a21",
  "detail-type": "CodeBuild Build State Change",
  "source": "aws.codebuild",
  "account": "account-number",
  "time": "2017-06-26T07:10:43Z",
  "region": "us-east-1",
  "resources": [
     "arn:aws:codebuild:us-east-1:account-number:build/my-project-name:9eb34205-9cba-40f2-a5d0-220497a9643a"
  ],
  "detail": {
    "build-status": "SUCCEEDED",
    "project-name": "my-project-name",
    "build-id": "arn:aws:codebuild:us-east-1:account-number:build/my-project-name:9eb34205-9cba-40f2-a5d0-220497a9643a",
    "current-phase": "COMPLETED",
    "current-phase-context": "[]",
    "version": "1"
  }
}

```

event.detail以下を見れば何とかなりそうですね。

## ファンクション本体

それでは本体を実装していきましょう。
方針としては

1. event引数からプロジェクト名とビルド結果を取得
2. 予め用意したSlackのwebhook URLに送信

これだけです。
requestモジュールでPOSTするだけの簡単なお仕事ですね。

```js:handler.js
"use strict";
const request = require('request');

function sendRequest(url, message, callback){

    const post = {
        text: message
    };
    //ヘッダーを定義
    const headers = {
        "Content-Type":"application/json"
    };

    const options = {
        url: url,
        method: "POST",
        headers: headers,
        json: true,
        body: post
    };
    request(options,  (error, response, body) => {
        console.log(body);
        return callback();
    })
}

module.exports.notify = (event, context, callback) => {
  const url = "your-webhook-url";
  const buildDetail = event.detail;
  const message = `${buildDetail["project-name"]}のビルドが${buildDetail["build-status"]}デース!`;
  sendRequest(url, message, () => {
    callback(null);
  });
};

```

# 結果確認

## 成功

![pegasus_succeeded.PNG](https://qiita-image-store.s3.amazonaws.com/0/116434/4515247d-18bf-fe74-7b52-87b07a170c14.png)

## 失敗

![pegasus_failed.PNG](https://qiita-image-store.s3.amazonaws.com/0/116434/695437ed-6e73-04f1-864f-5af2d49b3bb7.png)

## なんでペガサスなの?

1. SUCCEEDEDとかFAILEDとかわざわざ日本語に直すのがめんどくさかった
2. かといって全部英文にして「英語で言われてもわかんねーよ」とか怒られるのも嫌だった
3. せや、ペガサスだったら英語と日本語が混ざってても違和感ない!いけるやん!

# 実装してから気づいたこと、改善案

1. cloudwatchEventの発火がめっちゃ早い
    - CodePipeline内に設置したCodeBuildに仕込んでたところ、ペガサスの通知が来てから数十秒間はCodePipelineのコンソールではビルド中と表示されていた。
2. メンション欲しいかも
    - CodePipelineの手動承認とかと組み合わせるなら、担当者へのメンションが欲しい