モバイルアプリエンジニアの山下です。
LCLでは作業中のPull Reqeustが誤マージされるのを防ぐため、Pull Requestのステータスをラベルで管理しています。
ラベルは「WIP」と「ready for release」の2つあり、マージするためには「ready for release」を付ける必要があります。
「ready for release」を付けられたPull RequestはChatWorkに通知され、開発部のメンバーへ周知されるようになっています。
今回はこの通知に先日導入したDangerの指摘を一緒に流し、指摘事項を全員が確認できるようにしてみました。
それでは実装例とGitHub Webhookの効率的なデバッグ方法を紹介していきます。
実装
GitHub Webhookで送られたJSONからDangerのコメントを取得して特定のルームに通知します。
今回は以下のGemを使っています。 ※これらの詳細な使い方については当記事では取り扱いません
- octokit
- nokogiri
- chatwork
Dangerのコメントを取得
Dangerのコメントには「generated_by_danger」の文字列が含まれるため、この条件を利用します。
class GithubController < ApplicationController def fetch_danger_comments client.issue_comments(repo_full_name, pr_number).each do |comment| return comment['body'].gsub(/[\r\n]/, '') if comment['body'].include?('generated_by_danger') end end def client @client ||= Github::Base.new.client end def pr @pr ||= params['pull_request'] end def repo_full_name pr['base']['repo']['full_name'] end def pr_number pr['number'] end end
Dangerのコメントを解析
DangerのコメントはHTMLで構成されているため、nokogiriを使って要素を取得します。
今回はテーブルで表現される Error、Warnin、Message に対応しました。
class GithubController < ApplicationController ... def parse_danger_comments(comments) doc = Nokogiri::HTML(comments) contents = doc.search('//th[@data-kind]') notice = "\n" contents.each_with_index do |content, index| title_text = content.inner_text.gsub(/[\r\n]|\s{2,}/, '') notice += "\n" if index > 0 notice += "[title]#{title_text}[/title]\n" doc.search("//table[#{index + 1}]//tr/td[2]").each_with_index do |item| item_text = item.inner_text notice += "・#{item_text}\n" end end notice += "\n" end end
ChatWorkへメッセージを送る
asonas/chatwork-ruby を使うことで簡単にChatWorkへメッセージを送信することができます。
require 'chatwork' class GithubController < ApplicationController ... def post_on_chatwork body = "[info]#{repo_name} にPull Requestされました。\n" body += "[hr]#{title}\n" body += html_url danger_comments = fetch_danger_comments body += parse_danger_comments(danger_comments) if danger_comments.present? body += '[/info]' ChatWork.api_key = "API_KEY" ChatWork::Message.create(room_id: room_id, body: messages) end ... def title pr['title'] end def html_url pr['html_url'] end def repo_name params['repository']['name'] end end
以上でWebhookからpost_on_chatwork
を呼ぶことで通知されます。
GitHub Webhookの効率的なデバッグ方法
Webhookのデバッグでよくやりがちなのは、本番と同じ操作を繰り返して動作確認をすることです。
今回の例で言えば、Pull Requestのラベルを何度も切り替えて確認を繰り返してしまうことです。
ラベルの切り替えのみならまだラクですが、確認したいイベントがコメント追加やPull Request作成、Pushなどの場合、それらを実際に何度も操作するのは手間がかかります。
さらに、対象のリポジトリが組織の管理するもので不要な操作をし辛かったり、アクセス権限がありwebhookやリポジトリの設定画面を見れる環境でない場合、Webhookテスト用のリポジトリを自前で用意する必要があります。
実装する前から用意することを考えるとやる気もなくなります。
そこで、ローカルからcurl
コマンドを打ってWebhookを再現するようにしたいと思います。
手順
curl
で送るためのテスト用JSONデータを用意します。
実際のデータとダミーのデータを用意する2つの方法があります。
リポジトリの設定画面へのアクセス可能な場合は、実際のJSONデータを使うといいと思います。
実際のJSONデータを用意
リポジトリの設定画面のWebhooks & services > Recent Deliveries
から過去の送信履歴を確認することができます。
履歴の一覧からテストしたいPayloadをコピーすることで実際のデータで実装を進められます。
ダミーのJSONデータを用意
dummy.payload.json
というJSONファイルを作成し、以下から再現したい対象のWebhook payload exampleをコピペします。
Event Types & Payloads | GitHub Developer Guide
ローカルからWebhookを再現
ターミナルから以下のコマンドを叩いてPOSTします。
URLやX-GitHub-Event
はそれぞれ適当な値に変更してください。
$ cat dummy.payload.json | curl http://localhost:8080/XXXX -H 'Content-Type:application/json' -H "X-GitHub-Event: pull_request" -d @-`
以上で実際の流れを何度も操作する必要なく、ターミナルから直ぐに何回でも処理を動かすことができます。
効果
今回の取り組みで以下のメリットがありました。
- Dangerの指摘の確認漏れが減った
- フロントエンド・バックエンドなど担当分野の違う警告が発生した際に別途確認する手間が無くなった
- 明るみに出ることによってリーダーや互いの不安が減り、精神衛生的によい環境になった
- コードに関係ない警告(PR説明の項目漏れや名前の決まりなど)も直そうという意識が持たざるを得なくなった
まとめ
既に取り入れている仕組みを組み合わせるだけで開発効率や安全性の向上に繋げることができました。
最初はコメントの解析を正規表現でやろうとしていましたが、自分の分報へ流した際に他のメンバーから助言をもらい簡潔に処理することができました。
LCLでは社内のモブプロ勉強会でもこのような改善系ツールの開発を題材にして、各メンバーが環境づくりをしやすくしています。 チームでの活動や使っているツールを組み合わせることで日々環境が改善されていくのは面白いです。