CODEOWNERSによる自動コードレビュー
GitHub CODEOWNERS の設定方法、静かな失敗の回避、正しいパターン・権限・ブランチ保護によるレビュー強制を解説します。
CODEOWNERSファイルは、リポジトリ内のプレーンテキストファイルで、パスパターンをオーナー(GitHubユーザーまたはチーム)にマッピングし、プルリクエストが一致するパスに触れるたびに、該当オーナーへのレビューを自動的にリクエストします。このファイルは2つの役割を同時に果たします。手動でメンションすることなく適切な担当者へレビューをルーティングし、かつ誰が何を担当しているかを文書化します。ただし、CODEOWNERSはエラーを無音で失敗させます。パターンの順序が間違っていたり、メンバーのいないチームや書き込み権限のないオーナーが指定されていたりしても、エラーダイアログは表示されません。レビューリクエストは単に発火せず、意図した確認者の目を通さないままPRがマージされてしまいます。
本ガイドでは、数分でセットアップを正しく行う方法を説明したうえで、主に落とし穴に焦点を当てます。具体的には、特定のルールを静かに上書きする「最後のマッチが優先」ルール、そしてCODEOWNERSが設定済みのように見えながら何もしないという状況を引き起こす、権限・空チーム・デフォルトブランチに関する失敗パターンを取り上げます。
重要なポイント
- CODEOWNERSは最後のマッチが優先です。複数のパターンがファイルに一致する場合、最後に一致した行のみがオーナーを割り当てます。一般的なルールをファイルの先頭に、特定のオーバーライドを末尾に配置してください。
- ファイルはPRのベースブランチ上に存在し、3MB未満であり、正しい大文字・小文字を使用し、有効な構文を含んでいる必要があります。無効な行はすべて無音でスキップされます。
- CODEOWNERSだけではマージをブロックできません。ルールセットまたはブランチ保護ルールで「マージ前にプルリクエストを要求する」と「コードオーナーからのレビューを要求する」の両方を有効にする必要があります。
- 書き込みアクセス権のないオーナーは無音で無視されます。また、チームオーナーは、すべてのメンバーがすでに個別に書き込みアクセス権を持っていても、チーム自体が可視であり書き込みアクセス権を持っている必要があります。
- GitHub CODEOWNERSは
!による否定をサポートしていません。!README.mdのようなパターンは無効として拒否されます。
CODEOWNERSファイルは何をするのか?
CODEOWNERSは、リポジトリ内の特定のパスに対して責任を持つ個人またはチームを指定します。誰かが一致するパスを変更するプルリクエストを開くと、GitHubはリストされたオーナーに自動的にレビューをリクエストします。同じファイル形式がGitHub、GitLab、Bitbucketで動作しますが、本記事の例はGitHubを主軸としています。
AIエージェントが作成するPRの割合が増加している現在、auth/、**/migrations/、CI設定などの機密パスにCODEOWNERSルールを設定することで、エージェントによる変更がマージされる前に人間のオーナーが必ずレビューすることを保証できます。
Discover how at OpenReplay.com.
CODEOWNERSのセットアップと適用方法
ファイルを.github/CODEOWNERSに配置します。GitHubは.github/、次にリポジトリルート、その次にdocs/の順で検索し、最初に見つかったCODEOWNERSを使用します。そのため、単一の正規の場所に配置することで混乱を防げます。pattern @ownerの形式で1行に1ルールを記述し、デフォルトブランチにコミットします。
# .github/CODEOWNERS
# リポジトリ全体のデフォルトオーナー
* @my-org/core-team
# フロントエンドとバックエンドをエリア別に
/src/frontend/ @my-org/frontend-team
/src/backend/ @my-org/backend-team
# テストとドキュメント
**/tests/ @my-org/qa-team
*.md @my-org/docs-team
# 機密パスには専任オーナーを設定(末尾に配置)
/src/auth/ @my-org/security-team
他のファイルと同様にコミットします:
git add .github/CODEOWNERS
git commit -m "Add CODEOWNERS"
git push origin main
ファイルをコミットするだけではレビュアーのリクエストが行われるのみで、何もブロックされません。マージを実際にゲートするには、**「マージ前にプルリクエストを要求する」と「コードオーナーからのレビューを要求する」**の2つの設定を合わせて有効にする必要があります。これらは新しいルールセット(Settings → Rules → Rulesets)または従来のブランチ保護ルール(Settings → Branches)で設定できます。どちらの方法も利用可能ですが、ルールセットがより新しいインターフェースです。
知っておくべきCODEOWNERSの構文パターン
パターン言語はgitignoreのルールの大部分に従います。以下の5つでほぼすべてのケースをカバーできます:
* @core-team # グローバルデフォルト
/api/ @backend-team # ディレクトリ
*.ts @frontend-team # 任意の深さの拡張子
**/tests/ @qa-team # 任意の場所のネストされたディレクトリ
/security/ @sec-team @compliance-team # 1行に2人のオーナー
*.tsのようにアンカーされていない拡張子グロブは、リポジトリ内の任意の場所にあるその種類のファイルに一致します。これは**/*.tsと同じ動作をします。複数のオーナーを列挙する際に重要なルールが1つあります。すべてのオーナーは同じ行に記述する必要があります。複数行に分けると、パターンは最後に記述されたオーナーのみに一致します。コードオーナーからのレビューが必要な場合、リストされたオーナーのいずれか1人の承認で要件を満たします。
一つ誤解を解いておきます。.gitignoreとは異なり、GitHub CODEOWNERSは!による否定をサポートしていません。!README.mdのようなパターンは無効として拒否されます。GitHubの公式ドキュメントでも、!、[ ]文字範囲、\#エスケープはgitignoreの機能であり、ここでは動作しないと明記されています。パスを除外するには、別のオーナーを割り当てるか、どのルールにも一致しないようにルールを整理します(後の、より具体的な行でオーナー列を空にすることで、そのパスのオーナーシップを削除できます)。
最後のマッチが優先ルール(最も多い間違い)
CODEOWNERSは最後のマッチが優先です。複数のパターンがファイルに一致する場合、最後に一致した行のみがオーナーを割り当てます。 順序は最も一般的な失敗の原因です。一般的なルールを先頭に、特定のオーバーライドを末尾に配置してください。
以下は誤った順序の例です。キャッチオールが末尾にあるため、すべてを静かに引き受けてしまいます:
# 誤り — * が最後のマッチとなるため、@core-team が /src/auth/ も所有してしまう
/src/auth/ @security-team
* @core-team
*は/src/auth/app.tsに一致し、かつファイル内でより後に出現するため、@security-teamはリクエストされません。順序を入れ替えます:
# 正しい — 一般的なルールを先頭に、特定のオーバーライドを末尾に
* @core-team
/src/auth/ @security-team
これで/src/auth/以下の変更は@security-teamにリクエストされ、その他すべては@core-teamにフォールスルーします。ルールを追加するたびにパターンの順序を確認してください。
CODEOWNERSが無音で発火しない理由
「設定済みなのに何も起きない」という報告のほとんどは、以下のいずれかに起因します。CODEOWNERSはプルリクエストのベースブランチから読み込まれ、大文字・小文字を区別し、3MB未満である必要があり、無効な構文の行はスキップされます。そのため、正しく見えるファイルでもレビューが一切発火しないことがあります。
| 症状 | 原因 | 対処法 |
|---|---|---|
| レビュアーが一切リクエストされない | ファイルがPRのベースブランチに存在しない | マージ先のブランチにCODEOWNERSをコミットする |
| 特定のルールが発火しない | 最後のマッチが優先 — 後のパターンが上書きしている | 一般的なルールを上に、特定のルールを下に移動する |
| 1行だけ無視され、他は動作する | その行の構文が無効 — 無音でスキップされる | GitHubでファイルを開く。「Syntax errors」リンクが不正な行を示す |
| オーナーが列挙されているがリクエストされない | オーナーに書き込みアクセス権がない、またはユーザー・チームが存在しない | 書き込みアクセス権を付与し、ハンドル名を確認する |
| マージがブロックされ誰も承認できない | 空のチームがパスを所有している | チームに少なくとも1名のメンバーを追加する |
| パスが何にも一致しない | そのパスをカバーするルールがない | ルールを追加するか、書き込み権限を持つ任意のユーザーの承認を受け入れる |
| ドラフトPRでリクエストが発生しない | ドラフトPRはコードオーナーリクエストをトリガーしない | PRをレビュー準備完了としてマークする |
| 巨大なファイルでルールが無視される | 3MB超のCODEOWNERSは読み込まれない | ワイルドカードを使ってエントリを統合する |
2つの権限に関する詳細が、無音の失敗のほとんどを引き起こします。コードオーナーとして指定する人物は書き込み権限を持っている必要があります。書き込みアクセス権のないオーナーは無音で無視されます。また、オーナーがチームの場合、すべてのメンバーが個別に書き込みアクセス権を持っていても、チーム自体が可視であり書き込みアクセス権を持っている必要があります。存在しないユーザーやアクセス権のないユーザー・チームを指定した場合、コードオーナーは割り当てられず、PRには警告も表示されません。ただし、GitHubは不正な行を表示します。リポジトリUIでCODEOWNERSファイルを開くとエラーがハイライト表示され、REST APIからも確認できます。
静的な割り当てを超えて:チームの自動割り当てとActions
CODEOWNERSはパスをオーナーに静的にマッピングします。それだけでは不十分な場合、2つの仕組みで拡張できます。
組み込みのチーム自動割り当ては、チーム全体にメンションすることを防ぎます。Organization → Teams → チーム名 → Settings → Code reviewで自動割り当てを有効にします。チームにリクエストが来ると、チーム全体へのリクエストが削除され、代わりにメンバーのサブセットが割り当てられます。ラウンドロビン(最後にリクエストを受けた時期が最も古い順にローテーション)またはロードバランス(各メンバーの最近のリクエスト総数を均等化)から選択できます。注意点として、ブランチ保護によってコードオーナーが必須とされている場合、チームへのリクエストを削除できないため、チームへのリクエストに加えて個人へのリクエストも表示されます。
GitHub Actionsを使用するのは、割り当てがdiffやラベルに依存する必要がある場合、つまりCODEOWNERSでは表現できない場合に限ります。actions/checkout(2026年6月18日リリースの最新版v7.0.0)とレビュアー割り当てアクションを使用した、pull_requestトリガーによる最小限のワークフロー:
name: Assign Reviewers
on:
pull_request:
types: [opened, ready_for_review]
permissions:
pull-requests: write
jobs:
assign:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
with:
fetch-depth: 0 # ブランチ間のgit diffに必要
# ...変更されたパスやラベルをレビュアーにマッピングする処理をここに記述
エスカレーションの順序を選択肢のメニューとしてではなく、意思決定の指針として使用してください。静的なパス→オーナーのルールにはCODEOWNERS、チーム内の負荷分散にはチーム自動割り当て、変更またはラベルベースのロジックにはActionsを使用します。
まずデフォルトブランチに単一の.github/CODEOWNERSを作成し、一般的なルールから特定のルールへの順序で記述し、「コードオーナーからのレビューを要求する」を有効にしてから、テスト用のPRを開いて期待するオーナーがリクエストされることを確認してください。この1つの確認で、本番環境に到達する前に無音の失敗を検出できます。
よくある質問
CODEOWNERSとGitHubのチームコードレビュー自動割り当ての違いは何ですか?
CODEOWNERSはパスパターンをオーナーにマッピングする静的なファイルで、PRが一致するパスに触れるたびにレビューをリクエストします。チーム自動割り当ては組織の設定で、チームにリクエストが来ると、チーム全体へのリクエストをラウンドロビンまたはロードバランスで選ばれたメンバーのサブセットへのリクエストに置き換えます。両者は連携して動作します。CODEOWNERSがどのチームがパスを所有するかを決定し、自動割り当てがそのチームのどのメンバーに実際に通知するかを決定します。
gitignoreのような否定を使ってCODEOWNERSルールから特定のファイルを除外できますか?
いいえ。GitHub CODEOWNERSは否定をサポートしていないため、'!README.md'のようなパターンは無効として拒否され、その行は無音でスキップされます。GitHubのドキュメントでも、'!'否定、'[ ]'文字範囲、'#'エスケープはgitignoreの機能であり、ここでは動作しないと明記されています。パスを除外するには、後に続くより具体的なルールで別のオーナーを割り当てるか、その特定の行でオーナー列を空にしてオーナーシップを削除してください。
ファイルが正しく見えるのに、プルリクエストでコードオーナーがリクエストされないのはなぜですか?
最も一般的な原因は、CODEOWNERSがPRのベースブランチから読み込まれるため、フィーチャーブランチにのみ存在するファイルは発火しないことです。その他の無音の原因としては、オーナーに書き込みアクセス権がない、チームオーナーが可視でないまたは書き込みアクセス権がない、空のチーム、ドラフトPR(コードオーナーリクエストをトリガーしない)、3MB超のファイル、パスの大文字・小文字の誤り、GitHubが警告なくスキップする無効な行などが挙げられます。
CODEOWNERSだけでマージをブロックできますか、それともブランチ保護が必要ですか?
CODEOWNERSだけではレビュアーをリクエストするのみで、マージをブロックすることはありません。マージをゲートするには、「マージ前にプルリクエストを要求する」と「コードオーナーからのレビューを要求する」の2つの設定を合わせて有効にする必要があります。Settings → Rules → Rulesetsのルールセット、またはSettings → Branchesの従来のブランチ保護ルールで設定できます。どちらの方法も利用可能ですが、ルールセットがより新しい方法です。