毎週のミーティング準備が面倒
うちのチームでは毎週水曜日に定例ミーティングがありまして。
定例ミーティングが行われる度にGitHubでIssueを一つ作成し、そこに各チームメンバーがトピックや進捗を書き込んでおくスタイルでやってます。

上の図のように、ミーティング前日にIssueを作成しチームメンバーに共有(図の1.2部分)というのを、毎回温かみのある手作業で行ってたんですね。
手を付ければ5分もかからないのに、面倒でつい後回しにしてしまう…
後回しにしてしまうとチームメンバーがIssueを更新する時間が減ってしまうので、「よし自動化だ!」という流れに。

処理の流れを考える

Google Apps Script(以下GAS)で、
- GitHubのIssueを作成
- 作成したIssueにコメント
- Slackに通知
という流れで実装していくことにしたので、やっていきましょう。
ちなみに作成するIssueの雛形となる情報(Issueのタイトルや投稿する定型コメント)は、内容を定義したスプレッドシートから取得してます。
GAS内に直接記述してもよかったのだけど、実際に作成するIssueの内容には画像や表を使ってたりするので、管理しやすさを優先してスプレッドシートで管理することにしました(スプレッドシートのサンプルは後ほど紹介)。
ひとまずゴールイメージを確認
自動化に成功すると、このような通知が指定したSlackのチャンネルに届きます。便利。

早速GASを書く…前に準備しておくものがあるので、まずはそこからやっていきましょう。
事前準備(スプレッドシート、GitHub、Slack)
【Googleスプレッドシート】Issueの雛形となるシートの準備

Issueに投稿する内容などはスプレッドシートに設定しています。スプレッドシートには3つのシートがあり、以下のような構成です。
- mainシート・・・Issueのタイトルや本文、ラベルの内容
- commentシート・・・作成したIssueに投稿するコメントの内容
- confシート・・・Issueを作成するために必要な情報
以下にサンプルのスプレッドシートを用意したので、これをコピーして、色付きのセル部分を書き換えて使ってください。
【GitHub】tokenの設定

GiHubのIssueを作るために必要なtokenを発行しておきます。
Scopeはrepo
にチェックを入れました。
発行したtokenやissueを作るリポジトリの情報などを、先程のスプレッドシートの[conf]シートに書き込んでおきます。

【Slack】incoming webhooksの設定
処理が完了した後にSlackに通知したいので、incoming webhooksを設定しときましょう。Slackの設定画面でWebhook URLを取得したらメモっておきます。

これで下準備は完了です。
いざGAS
スクリプトエディタを開き、以下のコードをコピペして、一部を書き換えます。
function myFunction() {
//Issueを作成し、URL、タイトル、issue番号を受け取る
var issueData = createIssue();
var issueUrl = issueData.url;
var issueTitle = issueData.title;
var issueNumber = issueData.number;
//作成したIssueにコメントする
commentIssue(issueNumber);
//Slackに通知
var notificationTarget = "<@moge> <@hoge>"; // 通知したいslackのユーザー名を入力
var message = notificationTarget + " 定例ミーティングのIssueを用意しました!n" + issueTitle + "n" + issueUrl;
postSlack(message);
}
function openSheet(sheetName) {
var spreadsheet = SpreadsheetApp.openById('***************'); //雛形のスプレッドシートのIDを指定
return spreadsheet.getSheetByName(sheetName);
}
function createIssue() {
// POSTするURLを作成
var sheetConf = openSheet('conf');
var username = sheetConf.getRange(1,2).getValue();
var repository = sheetConf.getRange(2,2).getValue();
var accesstoken = sheetConf.getRange(3,2).getValue();
var url = "https://api.github.com/repos/"+username+"/"+repository+"/issues?access_token="+accesstoken;
//issueに投稿する内容をシートから取得
var sheetMain = openSheet('main');
sheetMain.getRange('E1').setValue('foo'); //シート内の適当なセルを更新して タイトルで使っているnow()を更新する
var title = sheetMain.getRange(2,1).getValue();
var body = sheetMain.getRange(2,2).getValue();
var label = sheetMain.getRange(2,3).getValue().split(',');
var issueBody = {title:title,body:body,labels:label};
var options =
{
"method" : "post",
"payload" : JSON.stringify(issueBody),
"muteHttpExceptions" : true
};
//issue作成
var response = UrlFetchApp.fetch(url, options);
//コメントと通知に必要な情報を返す
var json = JSON.parse(response.getContentText());
var issueData = {url: json.html_url,title: json.title,number: json.number};
return issueData;
}
function commentIssue(issueNumber) {
// POSTするURLを作成
var sheetConf = openSheet('conf');
var username = sheetConf.getRange(1,2).getValue();
var repository = sheetConf.getRange(2,2).getValue();
var accesstoken = sheetConf.getRange(3,2).getValue();
var url = "https://api.github.com/repos/"+username+"/"+repository+"/issues/" + issueNumber + "/comments?access_token="+accesstoken;
// コメント内容をシートから取得
var sheetComment = openSheet('comment');
var data = sheetComment.getRange(2, 1, sheetComment.getLastRow() - 1).getValues();
//二次元配列から配列に変換する
var commentBody = [];
for (var i=0; i<data.length; i++){
commentBody.push(data[i][0]);
}
//コメントを投稿
commentBody.forEach(function(body,index,array) {
var payload = {"body": body};
payload = JSON.stringify(payload);
var options =
{
"method" : "post",
"payload" : payload,
"muteHttpExceptions" : true
};
UrlFetchApp.fetch(url, options);
});
}
function postSlack(message) {
var url = "https://hooks.slack.com/services/****/************";
var payload = { "text": message };
var options = {
"method": "post",
"payload": JSON.stringify(payload)
};
UrlFetchApp.fetch(url, options);
}
最低限、書き換えが必要な箇所は、
var spreadsheet = SpreadsheetApp.openById('***************');
で、用意したスプレッドシートのIDを指定するところと、
var url = "https://hooks.slack.com/services/****/************";
で、slackのIncoming webhooksのURLを設定するところです。
※GitHub Enterpriseを利用している場合は、githubのAPIのURLを適宜書き換えてください。
試しに実行してみる
ここまでやったら試しにIssueが作成できるか確認してみましょう。

うまくいきましたか?うまくいかない場合はスクリプトにLogger.log()を仕込んで、どこで躓いているか調べてみてください。
仕上げにトリガーの設定
毎回スクリプトを手動で実行してては本末面倒なので、定期実行されるようにします。
今回はGASのトリガーを使って、ミーティング前日の11〜12時に実行するようにセットしてみました。

GASブームが到来
GAS、何が良いってブラウザですぐに書けるから開発環境を用意したりメンテナンスをしなくていいのが楽で良いですね。おかげで書いたスクリプトを非エンジニアにも共有しやすい。
そんなお手軽さが気に入って、マイブーム到来中。