こんにちは。プロダクトエンジニアのnakanotです。 普段は組織図・分析レポート・従業員サーベイの開発を担当しています。 今回は、DangerとGitHub Actionsを使ってPull Request作成時のレビュワーの設定や一部のコードレビューを自動化することで、レビュー周りの運用をスムーズにする方法を紹介します。
Dangerとは
Dangerとは、CIプロセス中に実行され、コードレビューの作業を自動化するツールです。チームのPull Requestの書き方や運用のルールを定義することで、Dangerがよしなに設定を行ってくれたり、ルールに沿っていないコードの変更を見つけたら警告をコメントしてくれたりします。
私たちのチームではRubyのGemとして提供されているDangerを利用していますが、Ruby以外にPython、JavaScriptなど複数の言語でも提供されています。
導入背景
現在、SmartHRの各プロダクトはほぼ全てのデータをBigQueryへ連携し、様々な分析に利用しています。 プロダクト側でデータベースのカラムが変更されたり削除されたりすると、BigQueryへのデータの同期が失敗することがあります。 これを防ぐためには、事前にデータチームへカラム変更の連絡を行う必要がありますが、この作業は頻繁に発生しないため、うっかり忘れてしまうことがありました。
この課題を解決するために、Dangerを利用することにしました。 Dangerを使うことで、データベースのスキーマ変更が検知された場合に自動的にデータチームへの連携が必要なことを通知する仕組みを構築し、忘れがちな連携作業を確実に行えるようにしました。
設定方法
大まかには以下の手順で設定しました。
- Getting Set Upを参考にDangerをインストールし、
Dangerfile
を作成する Dangerfile
にルールを定義する- GitHub ActionsでDangerを実行する
手順1と2はドキュメントを見てもらえば分かると思うので、説明を省略します。
GitHub ActionsでDangerを実行する
手順3では、以下のようなWorkflowを追加しました。 Dangerを実行するだけのシンプルなWorkflowになっています。 ドキュメントには、GitHubではPull Requestにコメントできる権限を持ったBotを作成する必要があると記載があるのですが、GitHub Actions上で実行する場合はGitHub Actions側のBotがよしなにコメントを付けてくれるため、別途Botの用意は不要になっています。
# .github/workflows/danger.yml name: Danger CI on: [pull_request] jobs: job: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - uses: ruby/setup-ruby@v1 with: bundler-cache: true - name: Run danger env: DANGER_GITHUB_API_TOKEN: '${{ secrets.GITHUB_TOKEN }}' run: | bundle exec danger --fail-on-errors=true
Dangerの実行時に渡している--fail-on-errors=true
は、Dangerがエラーを検出した場合にCIプロセスを失敗として終了させるためのものです。
なお、手元のDangerfileの内容を実行してローカルで動作確認したい場合は、Setting up an Access Tokenからアクセストークンを発行し、以下のようなコマンドを実行することで行えます。
$ DANGER_GITHUB_API_TOKEN=xxxxx bundle exec danger pr {対象のPRのURL}
活用事例
ここからは、私たちのチームでのDangerの活用事例を紹介します。
db/schema.rb に変更がある場合は、データチームへの連携を通知する
まずは、「導入背景」で説明した、カラムの変更時にデータチームへ通知する仕組みです。
以下のように設定することで、schema.rb
の変更をチェックし、変更がある場合はPull Request上でmessage
の内容をコメントしてくれます。
# Dangerfile: require "active_support/all" # 指定されたパスを含むファイルが変更されているか def changed_files(*path_prefixes) paths = (git.added_files + git.modified_files) paths.select do |path| path_prefixes.any? { path.start_with?(_1) } end end schema = changed_files("db/schema.rb") if schema.present? message = <<~MESSAGE db/schema.rb に変更がありました。カラムの削除や変更がある場合はデータチームへの連携が必要です。 マージ前に data_request チャンネルでデータチームへ連絡してください。 また、連携後はスプシの内容も更新してください。 [データベース mask 一覧](example.com) MESSAGE warn(message) end
warn
を使うことで、警告のコメントを表示できます。
実際には以下のような感じでコメントが表示されます。
SidekiqのWorkerのインターフェイスやWorker名に変更がある場合は、リリース前のオペレーションを通知する
以前、Workerのインターフェイスに変更を加えてリリースした際に、リリース前に積まれたジョブのことを考慮できておらず、障害を起こしてしまったことがありました。 再発防止のため、Workerのインターフェイスを変更した場合など通常のリリースフローとは別のオペレーションが発生する場合は、警告のコメントを表示する対応をしています。
# Dangerfile: # 指定されたファイルの差分に、patternで渡した文字列が含まれているか def changed_files_with_pattern(*paths, pattern) paths.select do |path| # 差分を抽出 diff = git.diff_for_file(path) # 追加された行のみを抽出 added_lines = diff.patch.split("\n").select { |line| line.start_with?("+") } # 削除された行のみを抽出 deleted_lines = diff.patch.split("\n").select { |line| line.start_with?("-") } [*added_lines, *deleted_lines].any? do |line| pattern.match?(line) end end end modified_files = git.modified_files deleted_files = git.deleted_files target_files = (modified_files + deleted_files).select { |file| file.include?("app/workers") } # performのインターフェイスに変更があるかチェックする perform_changed_files = changed_files_with_pattern(*target_files, /perform/) if perform_changed_files.present? message = <<~MESSAGE #{perform_changed_files.join(',')} の `perform` メソッドのインターフェイスに変更がありました。 リリース前にジョブの払い出しを忘れないようにしてください。 MESSAGE warn(message) end # worker名に変更があるかチェックする worker_name_changed_files = changed_files_with_pattern(*target_files, /class .*Worker/) if worker_name_changed_files.present? message = <<~MESSAGE #{worker_name_changed_files.join(',')} の Worker 名に変更がありました。 リリース前にジョブを払い出すか、[安全なリネーム対応](https://github.com/sidekiq/sidekiq/wiki/FAQ#how-do-i-safely-rename-a-worker) を行ってください。 MESSAGE warn(message) end
Stagingブランチ以外で特定のファイルに変更がある場合は、決められたチームをレビュワーに追加する
従業員サーベイでは、特定のファイルに変更があった場合、別チームにコードレビューをしてもらうルールがあります。もともとはGitHubのCODEOWNERSを使ってレビュワーを自動でアサインしていました。 しかし、CODEOWNERSはどのブランチでもファイルに変更があればレビュワーに設定してしまうため、本番環境へのリリースPRなどでレビューが不要なPRを作成した時でも別チームへレビュー依頼が飛んでしまい、ノイズとなっていました。 この問題もDangerを使って解決しています!
Dangerは、内部でOctokitと呼ばれるGitHub APIのクライアントを使っています。 Octokitoが提供しているAPIを利用することで、Pull Requestのレビュワーを追加することができます。
# Dangerfile: # branch_for_head で作成中のPRのブランチ名を取得する if github.branch_for_head != "staging" any_files = changed_files("config/any/") if any_files.present? # group-any というGitHub Groupをレビュワーに追加する github.api.request_pull_request_review(github.pr_json.base.repo.full_name, github.pr_json.number, team_reviewers: ["group-any"]) end end
まとめ
本記事では、Dangerを使ってレビュー運用を楽にする方法を紹介しました。 この記事で紹介した内容以外に、
- PRの作成者が所属するチームメンバーをレビュワーにアサインする
- PRの作成者に応じてPRにラベルを付与する
- JIRAのチケットのURLをPRのdescriptionに反映する
などを行っているチームもあります。 ぜひ皆さんもDangerを活用して、PRのレビュー作業を自動化しましょう!
We Are Hiring!
SmartHR では一緒に SmartHR を作りあげていく仲間を募集中です!
少しでも興味を持っていただけたら、カジュアル面談でざっくばらんにお話ししましょう!