LCL Engineers' Blog

バス比較なび・格安移動・バスとりっぷを運営する LCLの開発者ブログです。

Dangerの指摘をChatWorkに流してリリースのミスを防ぐ

モバイルアプリエンジニアの山下です。

LCLでは作業中のPull Reqeustが誤マージされるのを防ぐため、Pull Requestのステータスをラベルで管理しています。
ラベルは「WIP」と「ready for release」の2つあり、マージするためには「ready for release」を付ける必要があります。
「ready for release」を付けられたPull RequestはChatWorkに通知され、開発部のメンバーへ周知されるようになっています。

f:id:lcl-engineer:20180207134621p:plain

今回はこの通知に先日導入した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を呼ぶことで通知されます。

f:id:lcl-engineer:20180207134638p:plain

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説明の項目漏れや名前の決まりなど)も直そうという意識が持たざるを得なくなった

まとめ

既に取り入れている仕組みを組み合わせるだけで開発効率や安全性の向上に繋げることができました。
最初はコメントの解析を正規表現でやろうとしていましたが、自分の分報へ流した際に他のメンバーから助言をもらい簡潔に処理することができました。

f:id:lcl-engineer:20180207134653p:plain

LCLでは社内のモブプロ勉強会でもこのような改善系ツールの開発を題材にして、各メンバーが環境づくりをしやすくしています。 チームでの活動や使っているツールを組み合わせることで日々環境が改善されていくのは面白いです。