# 通知設計書 34-SCM変更原因（SCMTriggerCause）

## 概要

本ドキュメントは、Jenkins のビルド原因通知機能のうち、SCM（ソースコード管理）の変更検出によってビルドがトリガーされた場合に記録・表示される `SCMTriggerCause` の設計について記述する。

### 本通知の処理概要

SCM変更原因（SCMTriggerCause）は、Jenkins のビルドがどのような理由で開始されたかを追跡するための `Cause` クラス階層の一部であり、`SCMTrigger` による定期的なポーリングで SCM の変更が検出された場合に使用される。

**業務上の目的・背景**：継続的インテグレーション（CI）の基本原則は、ソースコードの変更を検出して自動的にビルドとテストを実行することである。SCMTriggerCause は、ビルドが SCM の変更によってトリガーされたことを記録し、ビルド履歴画面やコンソールログに表示することで、どの変更がビルドを引き起こしたかを追跡可能にする。ポーリングログへのリンクも提供され、変更検出の詳細を確認できる。

**通知の送信タイミング**：`SCMTrigger` が定期的に SCM をポーリングし、変更が検出された時点で `SCMTriggerCause` が生成される。ポーリング結果（変更あり/なし）はログファイルに記録され、変更があった場合にビルドがトリガーされる。

**通知の受信者**：この通知は特定の受信者に送信されるものではなく、ビルド履歴画面やビルドの詳細ページを閲覧するすべてのユーザーが確認できる形で表示される。ビルドのコンソールログにも出力される。

**通知内容の概要**：通知には、ビルドが SCM の変更によってトリガーされたことを示すメッセージが含まれる。また、ポーリングログへのリンクが提供され、変更検出の詳細を確認できる。

**期待されるアクション**：受信者（ビルド履歴閲覧者）は、ビルドが SCM の変更によって自動的に開始されたことを認識し、必要に応じてポーリングログを確認して変更内容を把握することができる。

## 通知種別

- アプリ内通知（ビルド履歴画面、ビルド詳細ページ）
- コンソールログ出力
- ポーリングログ（別ファイル）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期（ビルド開始時に即時記録） |
| 優先度 | 中 |
| リトライ | 無（永続化された情報のため） |

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

送信先は決定されない。ビルド原因情報はビルドオブジェクトに関連付けられ、ビルドの詳細ページやコンソールログを閲覧する全ユーザーに表示される。

## 通知テンプレート

### コンソールログ出力の場合

| 項目 | 内容 |
|-----|------|
| 形式 | テキスト |

### 本文テンプレート

```
Started by an SCM change
```

### 添付ファイル

| ファイル名 | 形式 | 条件 | 説明 |
|----------|------|------|------|
| polling.log | テキスト | 常に | ポーリング結果の詳細ログ |

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| pollingLog | ポーリングログの内容 | ログファイル | No |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| スケジュール | SCMポーリングで変更検出 | hasChanges() == true | SCMTrigger がポーリングを実行し、変更が検出された場合 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| 変更なし | ポーリング結果で変更が検出されなかった場合 |
| SCMDecisionHandler による拒否 | プラグインによってポーリングが拒否された場合 |
| ignorePostCommitHooks | Post-commit hook を無視する設定の場合（hook 経由のトリガー時） |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[SCMTrigger.run実行] --> B{jobがnull?}
    B -->|Yes| C[処理終了]
    B -->|No| D{SCMDecisionHandler拒否?}
    D -->|Yes| E[スキップログ出力]
    D -->|No| F[runPolling実行]
    F --> G{変更あり?}
    G -->|No| H[No changesログ出力]
    G -->|Yes| I[SCMTriggerCauseインスタンス生成]
    I --> J[ポーリングログからCause生成]
    J --> K[CauseAction作成]
    K --> L[scheduleBuild2実行]
    L --> M{ビルドスケジュール成功?}
    M -->|Yes| N[トリガーログ出力]
    M -->|No| O[既にキューにあるログ出力]
    E --> P[終了]
    H --> P
    N --> P
    O --> P
```

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

### 参照テーブル一覧

Jenkinsはファイルベースの永続化を使用しており、従来のRDBMSは使用しない。

| ストレージ | 用途 | 備考 |
|-----------|------|------|
| jobs/{jobName}/scm-polling.log | SCMポーリングログ | Runner.getLogFile()で参照 |
| jobs/{jobName}/config.xml | ジョブ設定（SCMTriggerのcron式） | トリガー設定の参照 |
| jobs/{jobName}/builds/{buildNumber}/build.xml | ビルド情報とCauseの永続化 | XStreamでシリアライズ |
| jobs/{jobName}/builds/{buildNumber}/polling.log | ビルド別ポーリングログ | BuildAction.getPollingLogFile() |

### 参照項目詳細

#### ポーリングログ

| 参照項目 | 用途 | 取得条件 |
|-------------------|------|---------|
| pollingLog | ポーリング結果の詳細 | Files.readString()で読み込み |

### 更新テーブル一覧

| ストレージ | 操作 | 概要 |
|-----------|------|------|
| jobs/{jobName}/builds/{buildNumber}/build.xml | INSERT | ビルド情報にCauseを含めて永続化 |
| jobs/{jobName}/builds/{buildNumber}/polling.log | INSERT | ポーリングログをビルドに保存 |

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| ポーリングログ読み込み失敗 | IOException発生時 | 空のポーリングログでCauseを生成 |
| ポーリング実行失敗 | SCMポーリング中にエラー発生 | エラーログを出力し、falseを返す |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | 0（リトライなし） |
| リトライ間隔 | N/A |
| リトライ対象エラー | N/A |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| ポーリングスレッド数 | 設定可能（デフォルト10、5-100の範囲） |
| STARVATION_THRESHOLD | 1時間（ポーリングキュー渋滞の検出閾値） |

### 配信時間帯

cron 式で定義されたスケジュールに従う

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

- SCMTriggerCause は追加のユーザー入力を含まないため、XSS リスクは低い
- ポーリングログには SCM の変更情報が含まれる可能性があるため、アクセス制御に注意
- ポーリングスレッド数の設定は管理者権限が必要

## 備考

- `SCMTrigger` は `@Symbol("pollSCM")` アノテーションを持ち、Pipeline や JCasC で `pollSCM` として参照可能
- ポーリングログは `onAddedTo()` コールバックでビルドディレクトリに移動される
- 同期ポーリングモード（`synchronousPolling`）が有効な場合、ポーリングは Trigger.Cron スレッドで直接実行される

---

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

本通知を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

まず、ビルド原因を表現するデータ構造を理解することが重要である。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | Cause.java | `core/src/main/java/hudson/model/Cause.java` | Causeクラス階層の基底クラス |
| 1-2 | SCMTrigger.java | `core/src/main/java/hudson/triggers/SCMTrigger.java` 708-776行 | SCMTriggerCauseの定義 |

**読解のコツ**: SCMTriggerCause は `pollingLog` フィールドを持ち、キュー中はメモリに保持され、ビルド開始後はファイルに移動される。

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

処理の起点となる Runner.run() メソッドを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | SCMTrigger.java | `core/src/main/java/hudson/triggers/SCMTrigger.java` 638-686行 | Runner.run()メソッドの実装 |
| 2-2 | SCMTrigger.java | `core/src/main/java/hudson/triggers/SCMTrigger.java` 607-635行 | runPolling()メソッドの実装 |

**主要処理フロー**:
1. **644行**: `SCMDecisionHandler.firstShouldPollVeto(job)` でポーリング拒否チェック
2. **664行**: `runPolling()` でSCMポーリング実行
3. **667-672行**: 変更があれば `SCMTriggerCause` を生成
4. **677行**: `scheduleBuild2()` でビルドをスケジュール

#### Step 3: Cause のライフサイクルを理解する

SCMTriggerCause がビルドに関連付けられる流れを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | SCMTrigger.java | `core/src/main/java/hudson/triggers/SCMTrigger.java` 737-739行 | onLoad()メソッド |
| 3-2 | SCMTrigger.java | `core/src/main/java/hudson/triggers/SCMTrigger.java` 742-755行 | onAddedTo()メソッド |
| 3-3 | SCMTrigger.java | `core/src/main/java/hudson/triggers/SCMTrigger.java` 757-760行 | getShortDescription()メソッド |

**主要処理フロー**:
- **742行**: `onAddedTo()` でビルドに追加された時に呼ばれる
- **745-748行**: `BuildAction` を作成し、ポーリングログをファイルに書き込む
- **750行**: ビルドに `BuildAction` を追加
- **754行**: `pollingLog` をnullにしてメモリを解放

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

```
SCMTrigger.run()
    │
    └─ Runner.run()
           │
           ├─ SCMDecisionHandler.firstShouldPollVeto()
           │
           ├─ runPolling()
           │      └─ job.poll(listener)
           │             └─ hasChanges()
           │
           └─ [変更あり]
                  │
                  ├─ new SCMTriggerCause(logFile)
                  │      └─ Files.readString(logFile)
                  │
                  ├─ new CauseAction(cause)
                  │
                  └─ scheduleBuild2(quietPeriod, actions)
                         │
                         └─ cause.onAddedTo(build)
                                ├─ new BuildAction(build)
                                ├─ Files.writeString(pollingLogFile)
                                └─ build.replaceAction(buildAction)
```

### データフロー図

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

cronスケジュール ───▶ SCMTrigger.run() ───▶ Runner.run()
                                                  │
SCM Repository ─────────────────────────▶ job.poll()
                                                  │
                                                  ▼
                                          [変更検出]
                                                  │
                                                  ▼
scm-polling.log ───▶ SCMTriggerCause() ───▶ pollingLog
                          │
                          ▼
                    CauseAction
                          │
                          ▼
                    scheduleBuild2()
                          │
                          ▼
                    onAddedTo()
                          │
                          ▼
                    [polling.log]
                    [ビルド詳細ページ]
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| SCMTrigger.java | `core/src/main/java/hudson/triggers/SCMTrigger.java` | ソース | SCMTriggerおよびSCMTriggerCauseの定義 |
| Cause.java | `core/src/main/java/hudson/model/Cause.java` | ソース | Cause基底クラス |
| BuildAction.java | `core/src/main/java/hudson/triggers/SCMTrigger.java` 418-499行 | ソース | ビルドにポーリングログを関連付けるAction |
| SCMAction.java | `core/src/main/java/hudson/triggers/SCMTrigger.java` 504-548行 | ソース | ジョブにポーリングログを関連付けるAction |
| SCMDecisionHandler.java | `core/src/main/java/jenkins/scm/SCMDecisionHandler.java` | ソース | ポーリング実行の判断ハンドラ |
| AdministrativeMonitorImpl | `core/src/main/java/hudson/triggers/SCMTrigger.java` 396-410行 | ソース | ポーリングキュー渋滞の監視 |
| Messages.properties | `core/src/main/resources/hudson/triggers/Messages.properties` | リソース | メッセージ定義 |
