3
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

CircleCIでGitHub上のPRを自動コードレビュー(master, develop以外任意の統合先ブランチにも対応)

Last updated at Posted at 2018-03-18

GitHubにホストしているプロジェクトのPRに対して、正しい統合先ブランチを判定し、CircleCIからProntoを使って自動的にコードレビューできるようにした。

具体的にPRのどこが悪い?

とあるRoRプロジェクトで、Rubocopの違反数増をCircleCIでガードしてGitHubでマージさせないようにしていた。これで確かにある程度はブロックできている。しかし、開発者としては具体的に、自分が変更したコードのどこに問題があるのかPR画面ですぐ分からないため対応しようとしても面倒だ。できたら、Rubocopの指摘をGitHubのPRのコメントを載せて上げられないものかと考えた。

自動コードレビューのツール

調べてみたら、SaddlerとProntoというgemsがこの用途にあると分かり、先にSaddlerを試してみたがローカルでも実行が上手くいかなかったり、使い勝手が悪いと感じて断念した。Prontoにしたら、ローカルでは比較的スムーズに実行できた。Prontoの必要なチェッカーをGemfileに指定してインストールしたら、pronto-run.shのように、コマンドラインで比較対象ブランチ(master、releaseやdevelopなど)を指定すると、現在のブランチ(マージしたいブランチ)によって持たされる差分に対して、Rubocopチェックを実施できた。

Gemfile
# ProntoのチェッカーをGemfileに指定してインストール
group :development, :test do
  # Pronto: auto code review
  gem 'pronto-rubocop', require: false
  gem 'pronto-reek', require: false
  gem 'pronto-flay', require: false
end
pronto-run.sh
# コマンドラインで現在のブランチとマージ先のブランチの差分に対してコードチェック
# Check the diff between current branch and the branch specified
# PR `TO_BRANCH` ← `FROM_BRANCH`における変更箇所のチェックを行うには

git checkout FROM_BRANCH
bin/pronto run -c origin/TO_BRANCH

統合先ブランチ多数の難

CIで実行する場合、比較対象ブランチ(統合先のブランチ)をどう取得できるのか。前述のRoRプロジェクトでは、統合ブランチがmaster一本だけではなく複数存在する。だから、PRもmasterに対するとは限らず、release、developや親featureブランチに対するPRが大多数。いくつか参考させていただいたところ、統合先ブランチが複数存在する場合の対応方法は見当たらなかったので、模索と工夫が必要だった。

CircleCIで工夫して統合先ブランチ名を特定

CircleCIなら、PRのURLsは環境変数CI_PULL_REQUESTSにあり、URLからPRのIDを取り出し、GitHub API (GraphQL)を通してPRの情報が取得でき、その中には欲しいbase branch (統合先ブランチ)情報が含まれている。GraphQLのQueryにはレポジトリの owner とnameを指定する必要もあるが、それらもCircleCIの環境変数にある。一連の作業があるので、pr.rakeに記述し、CircleCIではcode-review.shスクリプトで環境変数を受け取り実行する。

CI_PULL_REQUESTSから関連PRのIDを(複数)取り出す
# CI_PULL_REQUESTSから関連PRのIDを(複数)取り出す
def pr_ids(pr_id = nil)
  ids = ENV.fetch('CI_PULL_REQUESTS', '').split(',').map { |url| pr_id url }
  ids.push pr_id if pr_id
  ids
end

# PRのURLから、最後の数値列を取り出しPRのIDとする
def pr_id(pr_url)
  pr_url[/\d+$/].to_i
end
GraphQLでPRのIDから統合先ブランチ名を特定
query {
  repository(owner: "#{owner}", name: "#{name}") {
    pullRequest(number: #{pr_id}) {
      title
      url
      baseRefName
      headRefName
    }
  }
}
code-review.sh
# !/usr/bin/env bash
# CircleCIから環境変数を受け取り、コードレビューのrakeタスクに渡す
set -eu

export GITHUB_OWNER=$CIRCLE_PROJECT_USERNAME
export GITHUB_REPO=$CIRCLE_PROJECT_REPONAME
export PRONTO_GITHUB_ACCESS_TOKEN=$GITHUB_ACCESS_TOKEN
set -x

bin/rake pr:code_review

echo DONE

まとめ

  • CirleCIの環境変数からPRのIDを取得
  • GitHub GraphQLでPRのIDから統合先ブランチ名を取得
  • Prontoで統合先ブランチとの差分をチェックしPRにコメントを挿入

その他

  • コード自動レビューツールのProntoはローカルでの利用やGitHub連携以外にGitLabやBitbucketにも対応している。
  • CircleCI以外、JenkinsやTravisCIなどでもGitHubと連携していたらPRのIDや統合先ブランチ名を取得できるはずだ。
  • Rubocopで自動修正できる部分は自動修正していますが、Rubocopで自動修正できない部分やReekやFlayの指摘点はやはり開発者が確認する必要があります。

参考

3
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?