SmartHR Tech Blog

SmartHR 開発者ブログ

DangerとGitHub Actionsで始めるレビュー運用をスムーズにする方法

こんにちは。プロダクトエンジニアのnakanotです。 普段は組織図・分析レポート・従業員サーベイの開発を担当しています。 今回は、DangerとGitHub Actionsを使ってPull Request作成時のレビュワーの設定や一部のコードレビューを自動化することで、レビュー周りの運用をスムーズにする方法を紹介します。

Dangerとは

Dangerとは、CIプロセス中に実行され、コードレビューの作業を自動化するツールです。チームのPull Requestの書き方や運用のルールを定義することで、Dangerがよしなに設定を行ってくれたり、ルールに沿っていないコードの変更を見つけたら警告をコメントしてくれたりします。

私たちのチームではRubyのGemとして提供されているDangerを利用していますが、Ruby以外にPython、JavaScriptなど複数の言語でも提供されています。

導入背景

現在、SmartHRの各プロダクトはほぼ全てのデータをBigQueryへ連携し、様々な分析に利用しています。 プロダクト側でデータベースのカラムが変更されたり削除されたりすると、BigQueryへのデータの同期が失敗することがあります。 これを防ぐためには、事前にデータチームへカラム変更の連絡を行う必要がありますが、この作業は頻繁に発生しないため、うっかり忘れてしまうことがありました。

この課題を解決するために、Dangerを利用することにしました。 Dangerを使うことで、データベースのスキーマ変更が検知された場合に自動的にデータチームへの連携が必要なことを通知する仕組みを構築し、忘れがちな連携作業を確実に行えるようにしました。

設定方法

大まかには以下の手順で設定しました。

  1. Getting Set Upを参考にDangerをインストールし、Dangerfileを作成する
  2. Dangerfileにルールを定義する
  3. 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 を使うことで、警告のコメントを表示できます。 実際には以下のような感じでコメントが表示されます。

github-actions のbotのコメント例

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 を作りあげていく仲間を募集中です!

少しでも興味を持っていただけたら、カジュアル面談でざっくばらんにお話ししましょう!

hello-world.smarthr.co.jp