Google Apps Script はじめました
「GitHub Enterprise のリポジトリの、特定のラベルがついた issue にコメントがあった時に Slack に通知させたい」という目的で Google Apps Script をはじめたら思いの外楽しい。
上記を実現しようとしたとき GitHub integration for Slack であれば /github
の Slash コマンドで十分ではある。
subscribe で ave-hikari/tips
というリポジトリの issue のコメント(アクティビティ)を通知させる
またこの通知を bug
のラベルがついた場合のみにフィルタする
コメント時以下のように通知される
(通知解除時は /github unsubscribe
)
特定のリポジトリの全 issue に対してでは量が多すぎるので特定のラベルがついてるやつのみ通知させたい!というカスタムがしたいとき、
Google Apps Script でも GitHub API を使って、かつ通知を見た時テンションも上がるように Slack に通知させてみよ〜という感じに思い至った。
スクリプトは基本的な JavaScript + GAS 独自の関数、という感じなので新規のアプリ作成&導入手順やGCPでのログ参照周りを細かく残す ✍️
- 1. GAS で実行するWebアプリケーションを新規追加
- 2. アプリケーションをデプロイしてGETリクエストしてみる
- 3. Webhook の設定
- 4. POSTリクエストを受け付ける
- 5. GCP上でコンソールログを出力確認できるようにする
- 6. Slack の Webhook 設定
- 7. GAS から Slack Webhook にリクエストを送る
- 8. 特定の issue に対してリクエストする
1. GAS で実行するWebアプリケーションを新規追加
GAS の「新しいアプリケーション」より新規のアプリを作成(初めて作成する場合は事前に Google ドライブなりから Google Apps Script のアプリを追加する必要あり)
このようになっている
2. アプリケーションをデプロイしてGETリクエストしてみる
作成したアプリにHTTPリクエストを送るとGASでは
- doGet
- doPost
がトリガとなり実行される。
🔗 Simple Triggers
ということでwebアプリとしてデプロイして、GETの挙動を見てみる。
まず新規作成時 myFunction()
になっているコードを以下に修正して、メニューバーの「関数を選択」を doGet
を設定。
function doGet(e) { Logger.log(e) }
プロジェクト名を識別しやすいものに変更して、公開 > ウェブアプリケーションとして導入
を選択すると以下のように表示される
ここで execute the app as
(実行ユーザ) を自身のアカウントに、Who has access to the app
(アプリにアクセス可能なユーザ)を Anyone even anonymous
(匿名ユーザを含む全員)に設定。
ここでの匿名ユーザとはGoogleアカウントを持っていない、アプリにアクセスする Slack のことと解釈している
そして Deploy
を押すとデプロイしたアプリケーションのURLが表示されるので、
このURLの末尾に ?hoge=fuga
など適当なパラメータをつけてアクセスしてみるとこうなる。
Logger でログを出力しているので、プロジェクトのダッシュボード>実行数を見るとログが出力されているのが確認できる。
まずは作成したアプリケーションでGETリクエストをちゃんと受け付けられているという最小限の動作はOK 👌
Logger.log()
をログ出力に使っているけど馴染み深い console.log()
も同様に出力&ダッシュボードからの確認は可能。
今回は GitHub にコメントされたときの処理なので以降は doPost
を使う。
3. Webhook の設定
リポジトリの Settings > Webhooks > Add Webhook
より以下のように設定。
- Payload URL
- 作成したアプリケーションのURL
- Content type
- application/json
- トリガするイベントは
Let me select individual events
より、Issues
Issue comments
を選択
4. POSTリクエストを受け付ける
doPost
がトリガされるようアプリのコードを関数名だけ、以下のように修正。
function doPost(e) { Logger.log(e) }
最初ハマったのだがスクリプトを書き換えたら保存だけでなく 公開 > ウェブアプリケーションとして導入
より都度デプロイし直さなければ反映されないので注意(当然)
その際 Project version
は New
にしておく。バージョン更新時にコメント残せるので切り戻しも便利!
そしてデプロイの都度 Current web app URL
とアプリケーションのURLを教えてくれるがこれは変更されないので、Webhookの設定更新は不要。
doPost に書き換えたので、GASがこのスクリプトを実行してPOSTリクエストを受け付けるようトリガの設定をする。
プロジェクトの ダッシュボード>トリガ
より以下のようにトリガを追加
- 実行する関数を選択
- doPost
- デプロイ時に実行
- Head(最新バージョン)
- イベントのソースを選択
- 時間主導型
- 時間ベースのトリガーのタイプ
- 分ベース
- 時間の間隔を選択(分)
- 1分おき
先ほどと同じくプロジェクトのダッシュボード>実行数を見るとログが出力されている。
ここでトリガから実行される doPost の中身をデバッグしながら書いていくのだけど、デバッグ目的に Logger.log()
が使いづらい(POSTリクエストの詳細が出力されない)ので console.log()
を使用することにする。
console.log()
での出力は Google Cloud Platform(GCP)の Stackdriver Logging
を使用する。
5. GCP上でコンソールログを出力確認できるようにする
5-1. Google Apps Script用のプロジェクトをGCPで作成
(Googleアカウントがあれば利用できる)GCPのTOPより、新しいプロジェクトを作成
作成したプロジェクトを選択しダッシュボードを表示すると、プロジェクト情報に「プロジェクト番号」があるのでこれをコピー
GASに戻って、リソース>Cloud Platform プロジェクト
を選択。コピーしたプロジェクト番号をフォームに入力し設定ボタンを押すと、
以下のように OAuth の設定を求められるので文中のリンクから設定画面に遷移。
UserType で「外部」を選択
次の画面でアプリケーション名だけ任意のものを入力し(GASのプロジェクト名と合わせた)保存。
GASに戻って Cloud Platform プロジェクト
の「プロジェクトの設定」を押すと、GCPのプロジェクトとGASの紐付けがされる。
5-2. GCPのログビューアからコンソールログを参照
作成したGCPプロジェクトの メニュー>ロギング>ログビューア
を開き、出力するログの種類をセレクトボックスより「Apps Script 関数」を選択しておく
GASの Logger.log(e)
の1行を console.log()
に変更し、GitHubから新規に issue を作成してみる
function doPost(e) { console.log('ログ出力:', JSON.parse(e.postData.getDataAsString())); return; }
POSTリクエストの詳細をパースして出力しているため以下のようにGCPのログビューアより確認できる。
6. Slack の Webhook 設定
Slack のアプリ追加から Incoming Webhook を新規に作成し、Webhook URL をコピーする
ここで、GASのスクリプトプロパティを使って Slack Webhook URL を設定しておく
ファイル>プロジェクトのプロパティ>スクリプトプロパティ
にkey, value(URL)を保存
スクリプトプロパティに保存した key, value は PropertiesService で以下のように取得可能
PropertiesService.getScriptProperties().getProperty('slack_webhook_url');
7. GAS から Slack Webhook にリクエストを送る
デバッグしつつ以下のように POSTリクエストを受け取り、Slackの webhook にリクエストを送信する処理を書く。
const SLACK_WEBHOOK_URL = PropertiesService.getScriptProperties().getProperty('slack_webhook_url'); function doPost(e) { if (!e.postData) { return; } var data = JSON.parse(e.postData.getDataAsString()); // commentがない場合は通知しない if (!data.comment) { return; } callSlackWebhook(data.comment); } // Slackの webhook にリクエストを送る function callSlackWebhook(comment) { var params = { method: 'post', contentType: 'application/json', payload: JSON.stringify({ channel: '#general', link_names: 1, text: 'コメントされました:eyes:', attachments: [{ color: '#ffd700', text: comment.body }], }) }; var response = UrlFetchApp.fetch(SLACK_WEBHOOK_URL, params); return response; }
- UrlFetchApp.fetch() で webhook にPOSTする
- payload で投稿内容をカスタム
この内容でデプロイし GitHub の issue に テスト用issueにコメントしました
とコメントすると以下のように通知される
なお UrlFetchApp.fetch()
で slack へのリクエストを記述してデプロイする初回は、アクセス許可を確認されるので表示に従う。
8. 特定の issue に対してリクエストする
ここまでで issue にコメントがあった際Slackのチャンネルに通知する
まではできたので発端の 「特定のラベルがついた issue にコメントがあった時に」対して行えるようにする
doPost で POSTリクエスト時のパラメータを e.postData.getDataAsString()
で参照しており、
- action
- issue
- user
- labels
- comment ...
といった内容が確認できるがPOST時にラベルの詳細(名前など)が確認できるのは「ラベルが issue に設定された時」( action: labeled
)のみだったので、コメントされた時対象の issue のラベルが何か
は issue に対して UrlFetchApp.fetch()
でGETし確認することにした。
Slackの webhook にリクエストする callSlackWebhook()
をコールする前後を以下のように書き換え、コメントされた issue が特定のラベルが付与されている場合にのみ発火させる
var issue_info = UrlFetchApp.fetch(data.issue.url); var issue_parse = JSON.parse(issue_info.getContentText()); issue_parse.labels.forEach( function(e) { if (e.name == 'bug') { callSlackWebhook(data.comment); } return; } )
また Authorization ヘッダを使用してアクセストークンでAPIにリクエストする場合は以下のように UrlFetchApp.fetch
の引数にオブジェクトを渡す
var github_token = PropertiesService.getScriptProperties().getProperty('github_token'); var options = { 'method': 'GET', 'muteHttpExceptions': true, 'Content-Type': 'application/json', 'headers': { 'Authorization': ' token '+ github_token, } }; var issue_info = UrlFetchApp.fetch(data.issue.url, options); ...
issue のリンクもつけたりと payload の中身もカスタムして最終的には以下のようになった 👌
Webエンジニアをしているものの、仕事でJS書くの月に10hもないのではくらいの感じなので GAS 書くの純粋に楽しいデバッグも(デバッグできるとこまでくると)楽しい。
サービス連携や業務の自動化にも可能性が無限なのでがしがし書くぞ〜
参考