# 通知設計書 40-pipeline_failed_email

## 概要

本ドキュメントは、GitLabにおけるパイプライン失敗通知（pipeline_failed_email）の設計を記述したものである。

### 本通知の処理概要

**業務上の目的・背景**：CI/CDパイプラインの失敗は、コードの品質問題やインフラの問題を示す重要なシグナルである。パイプラインが失敗した際に関係者に迅速に通知することで、問題の早期発見と修正が可能になり、開発サイクルの遅延を最小化できる。

**通知の送信タイミング**：パイプラインが失敗ステータス（failed）で完了した時点で送信される。NotificationService#pipeline_finishedを経由し、ステータスに応じてpipeline_failed_emailが選択される。

**通知の受信者**：パイプラインに関連する以下のユーザーが受信者となる。パイプラインをトリガーしたユーザー、カスタム通知設定で「失敗パイプライン」を有効にしているユーザー。ユーザーの通知設定（watch/custom）に基づいてフィルタリングされる。

**通知内容の概要**：失敗したパイプラインの情報（パイプラインID/名前、プロジェクト、ブランチ、コミット情報）、失敗したジョブの一覧（ジョブ名、ステージ名）が含まれる。

**期待されるアクション**：受信者はパイプラインの失敗原因を調査し、問題を修正する。失敗したジョブのログを確認し、必要なコード修正や設定変更を行う。

## 通知種別

メール

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 非同期（deliver_later） |
| 優先度 | 高（失敗通知のため） |
| リトライ | Sidekiqのデフォルト設定に従う |

### 送信先決定ロジック

NotificationService内でnotifiable_usersを使用して受信者を決定する。custom_action: :failed_pipelineとしてフィルタリングされる。

## 通知テンプレート

### メール通知の場合

| 項目 | 内容 |
|-----|------|
| 送信元アドレス | GitLab設定のデフォルト送信元 |
| 送信元名称 | GitLab |
| 件名 | {プロジェクト名} \| Failed pipeline for {ブランチ名} \| {短縮SHA} |
| 形式 | HTML/テキスト（両形式） |

### 本文テンプレート

**HTML版：**
```
[赤色バナー] Pipeline {パイプライン名 or #ID} has failed!

Project: {ネームスペース}/{プロジェクト名}
Branch: {ブランチ名}
Commit: {短縮SHA} in {MR参照番号（あれば）}
        {コミットメッセージ（50文字まで）}
Commit Author: {コミット作成者アバター} {コミット作成者名}
Committed by: {コミッター名（異なる場合）}

Pipeline {パイプラインリンク} triggered by {トリガーユーザーアバター} {トリガーユーザー名}

[失敗したジョブ一覧]
Stage: {ステージ名}
- {ジョブ名} (リンク)
...
```

**テキスト版：**
```
Pipeline {パイプライン名 or #ID} has failed!

Project: {プロジェクト名} ( {プロジェクトURL} )
Branch: {ブランチ名} ( {ブランチURL} )
Merge request: {MR参照番号} ( {MR URL} ) ※あれば

Commit: {短縮SHA} ( {コミットURL} )
Commit Message: {コミットメッセージ（50文字まで）}
Commit Author: {コミット作成者名} ( {ユーザーURL} )
Committed by: {コミッター名} ( {ユーザーURL} ) ※異なる場合

Pipeline {パイプライン名 or #ID} ( {パイプラインURL} ) triggered by {トリガーユーザー名} ( {ユーザーURL} )
had {失敗ジョブ数} failed jobs.

{失敗ジョブごとの情報}
Stage: {ステージ名}
Name: {ジョブ名}
...
```

### 添付ファイル

なし

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| @pipeline | パイプラインオブジェクト | 引数から | Yes |
| @project | プロジェクト | @pipeline.project | Yes |
| @merge_request | 関連MR | @pipeline.merge_request or merge_requests_as_head_pipeline.first | No |
| recipient | 受信者メールアドレス | 引数から | Yes |
| failed | 失敗したジョブ一覧 | @pipeline.latest_statuses.failed | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| システムイベント | パイプライン失敗完了 | 通知設定で有効化されている場合 | パイプライン失敗時 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| プロジェクトでメール無効 | project.emails_disabled?がtrueの場合 |
| ユーザー通知設定 | failed_pipeline通知が無効の場合 |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[パイプライン完了] --> B{ステータス確認}
    B -->|failed| C[NotificationService#pipeline_finished]
    B -->|success| D[pipeline_success_email]
    B -->|fixed| E[pipeline_fixed_email]
    C --> F[email_template_name = pipeline_failed_email]
    F --> G[notifiable_users取得]
    G --> H{custom_action: failed_pipeline}
    H --> I[各受信者にメール送信]
    I --> J[Notify.pipeline_failed_email.deliver_later]
```

## データベース参照・更新仕様

### 参照テーブル一覧

| テーブル名 | 用途 | 備考 |
|-----------|------|------|
| ci_pipelines | パイプライン情報取得 | - |
| ci_builds | 失敗ジョブ一覧取得 | latest_statuses.failed |
| projects | プロジェクト情報取得 | - |
| merge_requests | 関連MR取得 | - |
| users | ユーザー情報取得 | - |
| notification_settings | 通知設定取得 | - |
| ci_stages | ステージ情報取得 | - |

### テーブル別参照項目詳細

#### ci_pipelines

| 参照項目（カラム名） | 用途 | 取得条件 |
|-------------------|------|---------|
| id | パイプライン識別 | - |
| name | パイプライン名 | - |
| status | ステータス | 'failed' |
| ref | ブランチ/タグ名 | - |
| sha | コミットSHA | - |
| user_id | トリガーユーザー | - |

#### ci_builds

| 参照項目（カラム名） | 用途 | 取得条件 |
|-------------------|------|---------|
| name | ジョブ名 | status = 'failed' |
| stage | ステージ名 | - |
| id | ジョブID | リンク生成用 |

### 更新テーブル一覧

更新なし（読み取りのみ）

## エラー処理

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | Sidekiqデフォルト |
| リトライ間隔 | 指数バックオフ |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | ApplicationRateLimiter設定に従う |
| 1日あたり上限 | 設定なし |

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

- メールにはパイプライン情報と失敗ジョブ情報が含まれる
- 受信者は通知設定に基づいてフィルタリングされる

## 備考

- 失敗したジョブは`@pipeline.latest_statuses.failed`で取得
- 赤色背景（#d22f57）で失敗を視覚的に強調
- _failed_buildsパーシャルで失敗ジョブ一覧を表示
- X-GitLab-Pipeline-Id, X-GitLab-Pipeline-Ref, X-GitLab-Pipeline-Statusヘッダーが設定される

---

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

### 推奨読解順序

#### Step 1: エントリーポイントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | notification_service.rb | `app/services/notification_service.rb` | pipeline_finishedメソッド（562-584行目） |

**主要処理フロー**:
- **562-584行目**: `pipeline_finished` - エントリーポイント
- **568行目**: `pipeline_notification_status` - ステータス決定（'failed'を返す）
- **569行目**: `email_template_name` - "pipeline_failed_email"を生成
- **575行目**: `custom_action: :"failed_pipeline"` - 失敗パイプライン通知設定

#### Step 2: メーラーを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | pipelines.rb | `app/mailers/emails/pipelines.rb` | pipeline_failed_emailメソッド（9-11行目） |

**主要処理フロー**:
- **9-11行目**: `pipeline_failed_email` - pipeline_mailへの委譲（status='Failed'）
- **19-36行目**: `pipeline_mail` - 共通メール生成処理
- **43-47行目**: `add_pipeline_headers` - ヘッダー設定

#### Step 3: テンプレートを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | pipeline_failed_email.html.haml | `app/views/notify/pipeline_failed_email.html.haml` | 失敗パイプライン専用テンプレート |
| 3-2 | _failed_builds.html.haml | `app/views/notify/_failed_builds.html.haml` | 失敗ジョブ一覧パーシャル |

**読解のコツ**:
- 背景色が#d22f57（赤）になっていることに注目
- `@pipeline.latest_statuses.failed`で失敗ジョブを取得

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

```
Pipeline完了イベント（status = 'failed'）
    │
    └─ NotificationService#pipeline_finished
           │
           ├─ emails_disabled? チェック
           │
           ├─ pipeline_notification_status
           │      └─ 'failed' 判定
           │
           ├─ email_template_name
           │      └─ "pipeline_failed_email"
           │
           ├─ notifiable_users
           │      └─ custom_action: :failed_pipeline
           │
           └─ Notify.pipeline_failed_email
                  │
                  └─ pipeline_mail(pipeline, recipient, 'Failed')
                         │
                         ├─ @project = pipeline.project
                         │
                         ├─ @merge_request取得
                         │
                         ├─ add_headers
                         │      └─ add_pipeline_headers
                         │
                         └─ email_with_layout
                                │
                                ├─ pipeline_failed_email.html.haml
                                │      └─ _failed_builds パーシャル
                                │             └─ @pipeline.latest_statuses.failed
                                │
                                └─ deliver_later (Sidekiq)
```

### データフロー図

```
[入力]                    [処理]                              [出力]

pipeline ─────────────▶ NotificationService#pipeline_finished
(status=failed)               │
                              ├─ status = 'failed'
                              │
                              ├─ email_template = 'pipeline_failed_email'
                              │
                              ▼
                       notifiable_users
                              │
                              ├─ custom_action: :failed_pipeline
                              │
                              ▼
recipient_emails ────▶ Notify.pipeline_failed_email ───▶ Email (SMTP)
                              │
                              └─ @pipeline.latest_statuses.failed ───▶ 失敗ジョブ一覧
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| notification_service.rb | `app/services/notification_service.rb` | ソース | 通知処理サービス |
| pipelines.rb | `app/mailers/emails/pipelines.rb` | ソース | Pipeline関連メーラー |
| pipeline_failed_email.html.haml | `app/views/notify/pipeline_failed_email.html.haml` | テンプレート | HTML版 |
| pipeline_failed_email.text.erb | `app/views/notify/pipeline_failed_email.text.erb` | テンプレート | テキスト版 |
| _failed_builds.html.haml | `app/views/notify/_failed_builds.html.haml` | テンプレート | 失敗ジョブ一覧パーシャル |
