# 通知設計書 16-GitHub Actionsアノテーション（警告）

## 概要

本ドキュメントは、Jestテストフレームワークにおける GitHub Actions 警告アノテーション機能の設計について記載する。GitHubActionsReporterクラスの`generateAnnotations`メソッドによって実装され、テストリトライ時にGitHub Actions特有の形式で警告アノテーションを出力する。

### 本通知の処理概要

**業務上の目的・背景**：不安定なテスト（flaky test）が発生した場合、テストは自動的にリトライされることがある。リトライが発生した場合、最終的にテストが成功しても、その過程でエラーが発生していたことを開発者に知らせる必要がある。警告アノテーションを使用することで、リトライの発生とその原因をGitHub UIで直接確認できる。

**通知の送信タイミング**：各テストファイルの実行完了時に`onTestResult`メソッドが呼び出され、その中で`generateAnnotations`が実行される。テスト結果に`retryReasons`が存在する場合、各リトライ理由に対して警告アノテーションが出力される。

**通知の受信者**：GitHub ActionsのCI/CDパイプラインを通じて、プルリクエストの作成者およびレビュアー。GitHubのWeb UI上で警告として表示される。

**通知内容の概要**：`::warning`形式でファイル名、行番号、タイトル（RETRY回数とテスト名）、エラーメッセージを含むアノテーションを出力する。

**期待されるアクション**：開発者はflakyテストの存在を認識し、テストの安定性向上を検討する。同じテストで繰り返しリトライが発生している場合は、根本原因の調査を行う。

## 通知種別

CI統合（GitHub Actionsアノテーション）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期 |
| 優先度 | 中（警告） |
| リトライ | なし |

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

標準出力（this.log）に`::warning`形式で出力。GitHub Actionsランナーがこの形式を解釈して警告アノテーションを生成する。

## 通知テンプレート

### コンソール出力形式

| 項目 | 内容 |
|-----|------|
| 出力先 | 標準出力（stdout） |
| 形式 | GitHub Actionsワークフローコマンド |

### 本文テンプレート

```
::warning file={ファイル名},line={行番号},title=RETRY {リトライ回数}: {テスト名}::{エラーメッセージ}
```

### 警告メッセージ例

```
::warning file=src/api/user.ts,line=25,title=RETRY 1: User API > should fetch user data::Timeout - Async callback was not invoked within 5000ms%0A%0A    at Object.<anonymous> (src/api/__tests__/user.test.ts:30:5)
```

### 特殊文字エスケープ

| 文字 | エスケープ後 |
|-----|------------|
| % | %25 |
| \r | %0D |
| \n | %0A |

### 添付ファイル

なし

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| file | 失敗が発生したファイルパス | topFrame.file | No |
| line | 失敗が発生した行番号 | topFrame.line | No |
| index | リトライ回数（0起算、表示時は+1） | retryReasons配列のindex | Yes |
| title | テスト名（ancestorTitles + title） | result.ancestorTitles, result.title | Yes |
| retryReason | リトライ時のエラーメッセージ | result.retryReasons[index] | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| テスト完了後 | onTestResult → generateAnnotations | retryReasonsが存在 | GitHubActionsReporter.generateAnnotations()で実行 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| リトライなし | result.retryReasonsがundefinedまたは空の場合 |
| GitHub Actions環境外 | GitHubActionsReporterが設定されていない場合 |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[onTestResult呼び出し] --> B[generateAnnotations]
    B --> C{testResults配列ループ}
    C --> D{retryReasons存在?}
    D -->|No| E[failureMessagesチェック]
    D -->|Yes| F[retryReasonsループ]
    F --> G[インデックス取得]
    G --> H[#getMessageDetails]
    H --> I[separateMessageFromStack]
    I --> J[getStackTraceLines]
    J --> K[getTopFrame]
    K --> L[formatPath]
    L --> M[#createAnnotation]
    M --> N[title: RETRY {index+1}: {title}]
    N --> O[type: 'warning']
    O --> P[特殊文字エスケープ]
    P --> Q[this.log出力]
    Q --> R[次のリトライ理由]
    R --> F
    F -->|完了| E
    E --> S[次のテスト結果]
    S --> C
    C -->|完了| T[終了]
```

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

### 参照テーブル一覧

本機能はデータベースを使用しない。

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

該当なし

### 更新テーブル一覧

該当なし

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| スタックトレース解析失敗 | topFrameがnullの場合 | file/lineをundefinedとして出力 |

### リトライ仕様

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

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | なし |
| 1日あたり上限 | なし |

### 配信時間帯

制限なし

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

- アノテーションはGitHub UIで公開されるため、機密情報を含むエラーメッセージに注意
- スタックトレースにはファイルパスが含まれる
- ANSIエスケープシーケンスはstripAnsiで除去される

## 備考

- リトライ回数は0起算で保存されているが、表示時は1起算（+1）で表示される
- 警告はエラーとは異なり、ビルドを失敗させない
- 同一テストに複数のリトライがある場合、各リトライに対して個別の警告が出力される

---

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

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

### 推奨読解順序

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

警告アノテーション生成で扱うデータ構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | GitHubActionsReporter.ts | `packages/jest-reporters/src/GitHubActionsReporter.ts` | L29-35: AnnotationOptions型 |
| 1-2 | test-result/types | `packages/jest-test-result/src/types.ts` | AssertionResult.retryReasonsの定義 |

**読解のコツ**: retryReasonsはstring[]型で、各リトライ時のエラーメッセージを保持。

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

処理の起点となるonTestResultメソッドを特定。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | GitHubActionsReporter.ts | `packages/jest-reporters/src/GitHubActionsReporter.ts` | L86-98: onTestResultメソッド |

**主要処理フロー**:
1. **L91**: `generateAnnotations`を呼び出し
2. **L92-94**: silentでなければ`printFullResult`を呼び出し

#### Step 3: 警告アノテーション生成処理

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | GitHubActionsReporter.ts | `packages/jest-reporters/src/GitHubActionsReporter.ts` | L109-116: retryReasons処理 |

**主要処理フロー**:
- **L109**: `if (result.retryReasons)`でリトライ理由の存在をチェック
- **L110**: `for (const [index, retryReason] of result.retryReasons.entries())`でループ
- **L111-115**: `#createAnnotation`を呼び出し、type: 'warning'を指定
- **L113**: タイトルに`RETRY ${index + 1}: ${title}`を設定

#### Step 4: メッセージ詳細の取得（エラーと共通）

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | GitHubActionsReporter.ts | `packages/jest-reporters/src/GitHubActionsReporter.ts` | L128-144: #getMessageDetailsメソッド |

**主要処理フロー**:
- **L129**: separateMessageFromStackでメッセージとスタックを分離
- **L131-132**: getStackTraceLines, getTopFrameでスタック情報を抽出
- **L134-136**: formatPathでスタックラインを正規化

#### Step 5: アノテーション出力処理（エラーと共通）

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | GitHubActionsReporter.ts | `packages/jest-reporters/src/GitHubActionsReporter.ts` | L146-158: #createAnnotationメソッド |

**主要処理フロー**:
- **L147-153**: 特殊文字をエスケープ
- **L155-157**: `::warning`または`::error`形式で出力（typeパラメータで制御）

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

```
GitHubActionsReporter.onTestResult (L86)
    │
    └─ generateAnnotations (L100)
           │
           ├─ titleの構築 (L104-107)
           │
           └─ [retryReasons] ループ (L109-116)
                  │
                  ├─ #getMessageDetails (L112)
                  │      │
                  │      ├─ separateMessageFromStack
                  │      │
                  │      ├─ getStackTraceLines
                  │      │
                  │      ├─ getTopFrame
                  │      │
                  │      └─ formatPath
                  │
                  └─ #createAnnotation (L111-115)
                         │
                         ├─ title: `RETRY ${index + 1}: ${title}` (L113)
                         │
                         ├─ type: 'warning' (L114)
                         │
                         ├─ stripAnsi (L147)
                         │
                         ├─ 特殊文字エスケープ (L149-152)
                         │
                         └─ this.log `::warning ...` (L155-157)
```

### データフロー図

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

TestResult ─────────────▶ generateAnnotations
  │                              │
  │ testResults                  │
  │ (AssertionResult[])          ▼
  │                        各テスト結果ループ
  │                              │
  │ retryReasons ────────────────┤
  │ (string[])                   │
  │                              ▼
  │                      retryReasonsループ
  │                              │
  │                              ├─ index (0, 1, 2, ...)
  │                              │
  │                              ├─ retryReason (エラーメッセージ)
  │                              │
  │                              ▼
  │                      #getMessageDetails
  │                              │
  │                              ▼
  │                      #createAnnotation
  │                              │
  │                    type = 'warning'
  │                              │
  │                    title = `RETRY ${index+1}: ...`
  │                              │
  │                              ▼
  └─────────────────────────▶ stdout
                        ::warning file=...,line=...,title=RETRY N::...
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| GitHubActionsReporter.ts | `packages/jest-reporters/src/GitHubActionsReporter.ts` | ソース | メイン実装ファイル |
| BaseReporter.ts | `packages/jest-reporters/src/BaseReporter.ts` | ソース | Reporterインターフェースの基底実装 |
| jest-message-util | `packages/jest-message-util/` | パッケージ | スタックトレース解析ユーティリティ |
| @jest/test-result | `packages/jest-test-result/` | 型定義 | AssertionResult.retryReasons等の型定義 |
