# 機能設計書 99-メールオンプッシュ

## 概要

本ドキュメントは、GitLabにおけるメールオンプッシュ機能の設計仕様を記載する。本機能は、プッシュイベント発生時に指定されたメールアドレスにコミット情報とdiffを送信する通知機能を提供する。

### 本機能の処理概要

メールオンプッシュ機能は、リポジトリへのプッシュまたはタグプッシュイベントが発生した際に、設定されたメールアドレスリストに対してコミット内容とdiff情報をメール送信する。ブランチフィルタにより通知対象を制限可能。

**業務上の目的・背景**：コードレビューやチーム内の情報共有において、リポジトリへの変更を即座にメールで通知することで、変更内容の把握と対応を迅速化する。

**機能の利用シーン**：本機能は以下のシーンで利用される。
- 重要なブランチへのプッシュをチームメンバーに通知
- コミット内容をメールでアーカイブ
- コードレビュー依頼の自動通知
- 外部関係者へのコード変更通知

**主要な処理内容**：
1. 受信者メールアドレスの設定
2. ブランチフィルタの設定
3. プッシュイベントの検出
4. コミット情報とdiffの取得
5. メール生成と送信

**関連システム・外部連携**：メールサーバー（SMTP）

**権限による制御**：プロジェクトのMaintainer以上が設定可能。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 142 | インテグレーション設定 | 主機能 | メールオンプッシュの設定 |

## 機能種別

インテグレーション / 通知

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| recipients | String | Yes | 受信者メールアドレス（スペース区切り） | メール形式、最大750件 |
| send_from_committer_email | Boolean | No | コミッターのメールアドレスから送信 | - |
| disable_diffs | Boolean | No | diffを含めない | - |
| branches_to_be_notified | String | No | 通知対象ブランチ（all/default/protected/default_and_protected） | デフォルト: all |

### 入力データソース

- 画面入力（プロジェクト設定 > インテグレーション > Emails on push）
- REST API
- プッシュイベントデータ

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| メール | Email | プッシュ内容を含むメール |
| 処理結果 | Boolean | 送信成功可否 |

### 出力先

- SMTPメールサーバー

## 処理フロー

### 処理シーケンス

```
1. プッシュイベント発生
   └─ push または tag_push

2. イベント判定
   └─ supported_eventsに含まれるかチェック

3. ブランチフィルタ
   └─ notify_for_ref?で通知対象か判定

4. ワーカーキュー
   └─ EmailsOnPushWorker.perform_async

5. 比較処理
   └─ CompareService.executeでdiff取得

6. メール生成
   └─ Notify.repository_push_email

7. メール送信
   └─ 各受信者に個別送信
```

### フローチャート

```mermaid
flowchart TD
    A[プッシュイベント] --> B{emails_disabled?}
    B -->|Yes| C[終了]
    B -->|No| D{notify_for_ref?}
    D -->|No| C
    D -->|Yes| E[EmailsOnPushWorker]
    E --> F{アクション判定}
    F -->|create| G[新規ブランチ]
    F -->|delete| H[ブランチ削除]
    F -->|push| I[CompareService]
    I --> J{コミットあり?}
    J -->|No| C
    J -->|Yes| K[diff取得]
    K --> L[メール生成]
    G --> L
    H --> L
    L --> M[各受信者に送信]
    M --> N{SMTPエラー?}
    N -->|Yes| O[ログ出力]
    N -->|No| C
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-99-01 | 受信者上限 | 最大750メールアドレスまで | 設定時 |
| BR-99-02 | メール形式検証 | Devise.email_regexpで検証 | 受信者解析時 |
| BR-99-03 | 重複排除 | 大文字小文字を無視して重複排除 | 受信者解析時 |
| BR-99-04 | タグプッシュ許可 | tag_pushは常に通知対象 | notify_for_ref?判定時 |
| BR-99-05 | ブランチフィルタデフォルト | デフォルトは'all'（全ブランチ通知） | 初期化時 |
| BR-99-06 | コミッター送信制限 | ドメインがGitLabインスタンスと一致する場合のみ | send_from_committer_email有効時 |
| BR-99-07 | リトライ | Sidekiqで3回リトライ | SMTP障害時 |

### アクション種別

| アクション | 条件 | 説明 |
|-----------|------|------|
| create | before_shaがブランク | 新規ブランチ作成 |
| delete | after_shaがブランク | ブランチ削除 |
| push | それ以外 | 通常プッシュ |

## データベース操作仕様

### 操作別データベース影響一覧

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 設定保存 | integrations | INSERT/UPDATE | メールオンプッシュ設定の保存 |

### テーブル別操作詳細

#### integrations

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT/UPDATE | type, active, project_id, properties | フォーム入力値 | type='Integrations::EmailsOnPush' |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | 受信者上限超過 | 751件以上のメールアドレス | アドレスを削減 |
| - | メール形式エラー | 無効なメール形式 | 正しい形式で入力 |
| Net::SMTPFatalError | SMTP致命エラー | メール送信失敗 | SMTPサーバー設定確認 |
| Net::SMTPSyntaxError | SMTP構文エラー | 不正なメールアドレス | アドレスを確認 |

## セキュリティ考慮事項

- **diff無効化オプション**：機密コードを含むdiffを通知に含めないオプション
- **送信元ドメイン制限**：コミッター送信はGitLabインスタンスのドメインと一致する場合のみ
- **受信者数制限**：750件までの制限でスパム防止
- **メール検証**：Devise.email_regexpによる形式検証

## 備考

- プロジェクトでメールが無効化されている場合は通知しない（emails_disabled?チェック）
- 同一コミットへのフォースプッシュでは逆方向の比較を実行
- Premailer::Rails::Hookでメールスタイルをインライン化
- GC.startでメモリ解放（大量のdiff処理後）

---

## コードリーディングガイド

### 推奨読解順序

#### Step 1: インテグレーションモデル

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | emails_on_push.rb | `app/models/integrations/emails_on_push.rb` | モデル定義 |
| 1-2 | base/emails_on_push.rb | `app/models/concerns/integrations/base/emails_on_push.rb` | 実装詳細 |

**主要処理フロー（base/emails_on_push.rb）**:
- **9行目**: RECIPIENTS_LIMIT = 750（受信者上限）
- **12-14行目**: valid_recipients - メール形式検証と重複排除
- **28-30行目**: supported_events = ['push', 'tag_push']
- **34-35行目**: バリデーション（recipients必須、件数制限）
- **37-50行目**: send_from_committer_emailフィールド
- **52-56行目**: disable_diffsフィールド
- **58-65行目**: branches_to_be_notifiedフィールド
- **80-93行目**: execute - ワーカーへのキュー投入
- **95-100行目**: notify_for_ref? - 通知対象判定

#### Step 2: バックグラウンドワーカー

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | emails_on_push_worker.rb | `app/workers/emails_on_push_worker.rb` | 非同期処理 |

**主要処理フロー**:
- **8行目**: sidekiq_options retry: 3
- **17-25行目**: perform - オプション解析
- **32-39行目**: アクション判定（create/delete/push）
- **45-61行目**: CompareServiceでdiff取得
- **63-80行目**: 各受信者へのメール送信ループ
- **77-79行目**: SMTPエラーハンドリング
- **84行目**: GC.startでメモリ解放

#### Step 3: メール生成

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | emails/projects.rb | `app/mailers/emails/projects.rb` | メーラー |

**主要処理フロー**:
- **96-113行目**: repository_push_email - メール生成
- **98行目**: RepositoryPushメッセージクラス使用
- **109行目**: sender - 送信元設定（send_from_user_email対応）

### プログラム呼び出し階層図

```
Project#execute_integrations
    |
    +-- Integrations::EmailsOnPush#execute
            |
            +-- notify_for_ref? [ブランチフィルタ]
            |
            +-- EmailsOnPushWorker.perform_async
                    |
                    +-- CompareService.new.execute [diff取得]
                    |
                    +-- Notify.repository_push_email [メール生成]
                            |
                            +-- Gitlab::Email::Message::RepositoryPush
                    |
                    +-- Premailer::Rails::Hook.perform [スタイルインライン化]
                    |
                    +-- email.deliver_now [送信]
```

### データフロー図

```
[プッシュ時]
git push
    ↓
PostReceiveService
    ↓
Project#execute_integrations
    ↓
Integrations::EmailsOnPush#execute
    ↓
EmailsOnPushWorker.perform_async(project_id, recipients, push_data, options)
    ↓ [非同期]
EmailsOnPushWorker#perform
    ↓
CompareService → diff取得
    ↓
Notify.repository_push_email → メール生成
    ↓
各受信者にメール送信
    ↓
SMTPサーバー
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| emails_on_push.rb | `app/models/integrations/emails_on_push.rb` | ソース | インテグレーションモデル |
| base/emails_on_push.rb | `app/models/concerns/integrations/base/emails_on_push.rb` | ソース | 実装詳細 |
| emails_on_push_worker.rb | `app/workers/emails_on_push_worker.rb` | ソース | バックグラウンドワーカー |
| projects.rb | `app/mailers/emails/projects.rb` | ソース | メーラー（repository_push_email） |
| notification_branch_selection.rb | `app/models/concerns/integrations/notification_branch_selection.rb` | ソース | ブランチフィルタ共通 |
| compare_service.rb | `app/services/compare_service.rb` | ソース | コミット比較サービス |
