# 機能設計書 26-変更ログ

## 概要

本ドキュメントは、Jenkinsの変更ログ（ChangeLogSet）機能の設計仕様を定義する。ChangeLogSetは、SCMからチェックアウトしたソースコードの変更履歴を表現するためのデータ構造を提供する。

### 本機能の処理概要

変更ログ機能は、ビルドに含まれるSCMの変更（コミット）情報を収集し、構造化されたデータとして提供する機能である。

**業務上の目的・背景**：継続的インテグレーションでは、各ビルドにどのような変更が含まれているかを把握することが重要である。変更ログは、コミット情報、作者、変更されたファイルなどの情報を提供し、ビルド結果の追跡可能性を確保する。

**機能の利用シーン**：
- ビルド詳細ページでの変更内容表示
- メール通知での変更概要の送信
- 変更に基づくビルド参加者の特定
- 課題管理システムへの自動リンク（ChangeLogAnnotator）
- リリースノートの自動生成

**主要な処理内容**：
1. SCMからの変更ログ取得と解析
2. 変更エントリ（Entry）の構造化
3. 変更メッセージへのアノテーション付与
4. 変更されたファイル（AffectedFile）の管理
5. Remote APIでの変更情報公開

**関連システム・外部連携**：各種SCMプラグイン、ChangeLogAnnotator、ChangeLogParser、RepositoryBrowser、Remote APIとの連携。

**権限による制御**：変更ログの閲覧はビルドへのアクセス権限に基づく。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 16 | ビルド詳細 | 主画面 | 変更ログの表示 |

## 機能種別

データモデル / ビルド情報

## 入力仕様

### 入力パラメータ（コンストラクタ）

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| run | Run<?,?> | Yes | 関連するビルド | null不可（テスト時を除く） |
| browser | RepositoryBrowser<?> | No | リポジトリブラウザ | nullの場合あり |

### 入力データソース

SCM.checkoutによって生成された変更ログファイル（changelog.xml等）をChangeLogParserで解析。

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| items | Object[] | 変更エントリの配列 |
| kind | String | SCMの種別識別子 |
| isEmptySet | boolean | 変更なしかどうか |

### 出力先

- ビルド詳細ページ（Jelly/Groovyビュー）
- Remote API（XML/JSON）
- メール通知

## 処理フロー

### 処理シーケンス

```
1. SCM.checkout()完了後
   └─ 変更ログファイル生成（changelog.xml等）
2. ChangeLogParser.parse()呼び出し
   └─ 変更ログファイルを解析
   └─ ChangeLogSetオブジェクトを生成
3. ChangeLogSet.Entry生成
   └─ 各コミット情報をEntryに変換
   └─ Entry.setParent()でChangeLogSetと関連付け
4. ビュー表示時
   └─ ChangeLogSet.getItems()で一覧取得
   └─ Entry.getMsgAnnotated()でアノテーション付きメッセージ取得
```

### フローチャート

```mermaid
flowchart TD
    A[SCM.checkout完了] --> B[changelog.xml生成]
    B --> C[ChangeLogParser.parse]
    C --> D[ChangeLogSet生成]
    D --> E[Entry生成]
    E --> F[Entry.setParent呼び出し]
    F --> G[ビルドに関連付け]
    G --> H{表示リクエスト}
    H -->|index view| I[詳細表示]
    H -->|digest view| J[サマリー表示]
    H -->|Remote API| K[XML/JSON出力]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-001 | イテレータ順序 | Iteratorは新しい変更から古い変更の順で返す | 全変更セット |
| BR-002 | パス形式 | AffectedFileのパスはスラッシュ区切り、先頭/末尾スラッシュなし | 全ファイルパス |
| BR-003 | タイムスタンプ | タイムスタンプは-1で未サポートを表現 | getTimestamp() |
| BR-004 | コミットID | コミットIDはnullで未サポートを表現（CVS等） | getCommitId() |

### 計算ロジック

```java
// アノテーション付きメッセージの生成
public String getMsgAnnotated() {
    MarkupText markup = new MarkupText(getMsg());
    for (ChangeLogAnnotator a : ChangeLogAnnotator.all()) {
        a.annotate(parent.run, this, markup);
    }
    return markup.toString(false);
}
```

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 変更ログ保存 | changelog.xml | INSERT | ビルドごとの変更ログ |

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

#### changelog.xml（変更ログファイル）

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | ログエントリ | SCM固有フォーマット | ビルドディレクトリ配下 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | UnsupportedOperationException | getAffectedFiles()未実装時 | SCM実装で対応 |
| - | RuntimeException | ChangeLogAnnotator実行時エラー | ログ出力して続行 |
| - | Error | ChangeLogAnnotator致命的エラー | ログ出力して続行 |

### リトライ仕様

リトライは行わない。変更ログの解析失敗時はEmptyChangeLogSetが使用される。

## トランザクション仕様

トランザクション管理はない。変更ログはファイルとして永続化される。

## パフォーマンス要件

- 大量のコミットがある場合でもイテレータベースで効率的に処理
- getItems()は全件をメモリに読み込むため、大規模な変更ログでは注意が必要

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

- 変更メッセージにはHTMLエスケープが適用される（getMsgEscaped）
- ChangeLogAnnotatorによるマークアップは信頼されたソースからのみ許可
- Remote APIでのエクスポートはジョブアクセス権限に基づく

## 備考

- ChangeLogSetは抽象クラスであり、各SCM実装で具体的なサブクラスを提供
- Entryも抽象クラスで、getMsg()とgetAuthor()は必須実装
- EmptyChangeLogSetは変更がない場合に使用される特殊な実装

---

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

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

### 推奨読解順序

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

ChangeLogSetとEntryの構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | ChangeLogSet.java | `core/src/main/java/hudson/scm/ChangeLogSet.java` | クラス構造、Entryネストクラス |

**読解のコツ**: ChangeLogSetはIterable<T>を実装し、Entry型の要素を持つ。EntryはChangeLogSetを親として参照する双方向関連。

#### Step 2: 主要メソッドを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | ChangeLogSet.java | `core/src/main/java/hudson/scm/ChangeLogSet.java` | getItems()、isEmptySet()、getKind() |

**主要処理フロー**:
- **56-72行目**: コンストラクタ - Run、RepositoryBrowserとの関連付け
- **88-91行目**: getRun() - ビルドへの参照取得
- **96-98行目**: getBrowser() - リポジトリブラウザ取得
- **103行目**: isEmptySet() - 抽象メソッド、変更なしの判定
- **109-115行目**: getItems() - Remote API用の全エントリ取得
- **122-125行目**: getKind() - SCM種別識別子

#### Step 3: Entryクラスを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | ChangeLogSet.java | `core/src/main/java/hudson/scm/ChangeLogSet.java` | Entry内部クラス |

**主要処理フロー**:
- **141-153行目**: Entry - 親への参照管理（parent、setParent）
- **167-170行目**: getCommitId() - コミットID（null可）
- **183-186行目**: getTimestamp() - タイムスタンプ（-1で未サポート）
- **198行目**: getMsg() - 抽象メソッド、コミットメッセージ
- **207行目**: getAuthor() - 抽象メソッド、作者
- **220行目**: getAffectedPaths() - 抽象メソッド、変更ファイルパス
- **236-244行目**: getAffectedFiles() - 変更ファイル詳細（オプション）
- **249-261行目**: getMsgAnnotated() - アノテーション付きメッセージ

#### Step 4: AffectedFileインターフェースを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | ChangeLogSet.java | `core/src/main/java/hudson/scm/ChangeLogSet.java` | AffectedFileインターフェース |

**主要処理フロー**:
- **281-297行目**: AffectedFile - ファイル変更情報のインターフェース
- **290行目**: getPath() - ファイルパス
- **296行目**: getEditType() - 編集種別（追加/変更/削除）

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

```
SCM.checkout()
    │
    └─ changelog.xml生成
           │
           └─ ChangeLogParser.parse(build, browser, changeLogFile)
                  │
                  └─ ChangeLogSet<Entry>生成
                         │
                         ├─ Entry.setParent(ChangeLogSet)
                         │
                         └─ [各Entry]
                                ├─ getMsg()
                                ├─ getAuthor()
                                ├─ getAffectedPaths()
                                ├─ getCommitId()
                                └─ getTimestamp()

ビルド詳細表示
    │
    ├─ ChangeLogSet.iterator()
    │      └─ 変更エントリのイテレート
    │
    └─ Entry.getMsgAnnotated()
           │
           └─ ChangeLogAnnotator.all()
                  └─ 各アノテータによるマークアップ
```

### データフロー図

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

changelog.xml        ChangeLogParser
(SCM固有形式)      ├─ parse() ─────────────▶     ChangeLogSet
                   │
                   └─ 各コミット解析 ─────────▶     Entry[]
                          │
                          ├─ msg
                          ├─ author
                          ├─ commitId
                          ├─ timestamp
                          └─ affectedPaths

Entry               ChangeLogAnnotator
├─ getMsg()      ├─ annotate() ──────────▶     MarkupText
│                │
│                └─ 課題リンク挿入等
│
└─ getMsgAnnotated() ───────────────────▶     HTML文字列
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| ChangeLogSet.java | `core/src/main/java/hudson/scm/ChangeLogSet.java` | ソース | 変更ログセットの抽象基底クラス |
| EmptyChangeLogSet.java | `core/src/main/java/hudson/scm/EmptyChangeLogSet.java` | ソース | 空の変更ログセット |
| ChangeLogParser.java | `core/src/main/java/hudson/scm/ChangeLogParser.java` | ソース | 変更ログパーサー抽象クラス |
| ChangeLogAnnotator.java | `core/src/main/java/hudson/scm/ChangeLogAnnotator.java` | ソース | 変更メッセージのアノテータ |
| EditType.java | `core/src/main/java/hudson/scm/EditType.java` | ソース | 編集種別の列挙型 |
| MarkupText.java | `core/src/main/java/hudson/MarkupText.java` | ソース | マークアップテキスト処理 |
| RepositoryBrowser.java | `core/src/main/java/hudson/scm/RepositoryBrowser.java` | ソース | リポジトリブラウザ抽象クラス |
