CircleCIのビルド結果をSlackのダイレクトメッセージに送信する

こんにちは。ニュースチームのエンジニアのTです。

ニュースチームでは、チャットツールとしてSlackを使用しています。CIツールとして新たにCircleCIを導入することを検討していた際に、Slackとの連携について調査しました。


今回やりたかったのは、git push時にCircleCIのビルドを行い、Slackのchannelにビルド結果を送信し、ビルドが失敗していたときのみpushした人のslackbotにダイレクトメッセージを送信することです。

ビルド失敗通知の取得やダイレクトメッセージの送信はSlackAppのOutgoing WebHooksIncoming WebHooksを使って、Google Apps Script(GAS)で実装します。


処理の流れ

①git pushを行い、CircleCIのビルドが走る
②CircleCIのビルド結果がSlackのchannelに通知される
③Outgoing WebHooksが通知を感知してGASが実行される(doPostメソッドが走る)
④GASでSlackの通知内容を確認する
⑤ビルド失敗の通知であれば、Incoming WebHooksでダイレクトメッセージを送信する


特定のchannelにビルド結果の通知を送るのは、CircleCIとSlackのWebの設定ですぐできるかと思うので割愛します。以下のようなビルド結果がchannelに送られてきます。

ビルド失敗時
CircleCIのビルド結果をSlackのダイレクトメッセージに送信する_f0364156_19051175.png
ビルド成功時
CircleCIのビルド結果をSlackのダイレクトメッセージに送信する_f0364156_19102665.png

画像のようにCircleCIのビルド結果はfallback形式で送信されているため、Outgoing WebHooksではTrigger Word(s)が取得できません。


そこで、Trigger Word(s)を空にしてビルド結果が送られるchannelに何か通知される度にOutgoing WebHooksからGASのdoPostメソッドが走り、channelの過去ログ1件を取得、ビルド失敗通知であれば対象者のslackbotに送信するようにしました。


Outgoing WebHooksの設定
URLはGASの匿名での実行を公開しているリンクを入力
CircleCIのビルド結果をSlackのダイレクトメッセージに送信する_f0364156_10130008.png

過去ログ1件を取得するコード
var API_TOKEN = PropertiesService.getScriptProperties().getProperty('SLACK_ACCESS_TOKEN');

function doPost(e) {
// Outgoing WebHooksのToken
var verify_token = "xxxxxxxxxxxxxxxx";

if (verify_token != e.parameter.token) {
throw new Error("invalid token.");
}

// slackのログを取得するchannelのID
var chId = e.parameter.channel_id;
var message = getSlackChannelHistory(chId);
}

function getSlackChannelHistory(chId) {
var params = [];
params['channel'] = chId;
params['count'] = 1;

var data = requestSlackApi('channels.history', params);
return data.messages[0];
}

function requestSlackApi(path, params) {
var queryParams = ['token=' + encodeURIComponent(API_TOKEN)];
for (var key in params) {
queryParams.push(encodeURIComponent(key) + "=" + encodeURIComponent(params[key]));
}
var url = 'https://slack.com/api/' + path + '?' + queryParams.join('&');

var res = UrlFetchApp.fetch(url);
var data = JSON.parse(res.getContentText());
if (data.error) {
throw "GET " + path + ": " + data.error;
}

return data;
}


また、対象者のslack名がわからないとダイレクトメッセージを送信できないので、git pushした名前とSlackの名前を紐付けるスプレッドシートを用意します。


スプレッドシート(シート1)
CircleCIのビルド結果をSlackのダイレクトメッセージに送信する_f0364156_12034879.png

スプレッドシートのデータを取得するコード
function myFunction() {
var name = 'test_user';
var user = getSlackName(name);
Logger.log(user);
}

function getSlackName(name) {
var sheetUrl = "https://docs.google.com/spreadsheets/d/1GxZxxxxxxxxxxxxxE/edit#gid=0";
var sheetName = "シート1";
var book = SpreadsheetApp.openByUrl(sheetUrl);
var sheet = book.getSheetByName(sheetName);

var lastRow = sheet.getLastRow();
var user = '';
for (var rowIndex=2; rowIndex<=lastRow; rowIndex++) {
var gitName = sheet.getRange(rowIndex, 1).getValue();
var slackName = sheet.getRange(rowIndex, 2).getValue();

if (gitName == name) {
user = slackName;
break;
}
}

return user;
}


一致する紐付けがあったとき対象者宛にダイレクトメッセージを送信します。今回は一致しなければ特定のchannelへ通知するようにしました。


slackbotへの通知
CircleCIのビルド結果をSlackのダイレクトメッセージに送信する_f0364156_12260851.png

slackbotにビルド失敗を通知するコード
var reg = /^Failed:[\s|\S]*\(.*?\sby\s(.*)\)/;

function doPost(e) {

・・・

// スプレッドシートに名前がなかったときに通知するchannel名
var chName = "#xxxxx";
postMessage(message, chName);
}

function postMessage(message, chName) {
var bot_name = "circle ci bot";
var bot_icon = "https://a.slack-edge.com/bda7/plugins/circleci/assets/service_512.png";

var channel = chName;

//Incomig WebHooks の webhook url
var url = "https://hooks.slack.com/services/Txxxxxxxxx5/BxxxxxxB/aDxxxxxxxxxMY";
var method = "POST";

if (obj = message.attachments) {
var matches = obj[0].fallback.match(reg);
if (!matches) {
return;
}
var user = matches[1];
name = getSlackName(user);
if (name) {
user = name;
channel = user;
}

var attachments = obj;
var payload = {
"text" : user + " さん、buildに失敗しました。",
"channel" : channel,
"attachments": attachments,
"username" : bot_name,
"icon_url" : bot_icon,
"link_names" : 1
};

var option = {
"method" : method,
"payload" : JSON.stringify(payload)
};

UrlFetchApp.fetch(url, option);
}
}


以上です。
他にもGASとスプレッドシートを使用したら色々できそうなので、ぜひ試してみてください。


エンジニア募集

エキサイトではエンジニアとして一緒に働いてくださる方を新卒採用と中途採用で募集しています。
詳しくは、こちらの採用情報ページをご覧ください。

by ex-engineer | 2016-10-21 10:00