# 機能設計書 43-リポジトリ操作

## 概要

本ドキュメントは、GitHubリポジトリ情報の取得およびファイル操作機能の設計について記載する。この機能は、GitHub App認証を使用してリポジトリのメタデータ、ブランチ情報、コミット履歴、ファイル内容などを取得するAPIを提供する。

### 本機能の処理概要

**業務上の目的・背景**：Liamアプリケーションは、ユーザーのGitHubリポジトリからスキーマファイルを取得してER図を生成する機能を提供する。この機能を実現するために、GitHubリポジトリに対する様々な操作（リポジトリ一覧取得、ブランチ一覧取得、ファイル取得など）が必要である。GitHub App認証を使用することで、インストール済みのリポジトリに対して安全にアクセスできる。

**機能の利用シーン**：
- プロジェクト新規作成時のリポジトリ選択
- スキーマファイルの取得とER図表示
- ブランチ切り替えによる異なるバージョンのスキーマ参照
- 最新コミット情報の表示

**主要な処理内容**：
1. GitHub App認証によるOctokitクライアント生成
2. インストールされたリポジトリ一覧の取得
3. リポジトリのブランチ一覧取得
4. ファイルコンテンツおよびSHAの取得
5. 最新コミット情報の取得
6. 組織情報（アバターURL等）の取得
7. フォルダコンテンツの取得
8. ファイルダウンロード（Raw URL経由）

**関連システム・外部連携**：
- GitHub REST API（Octokit経由）
- GitHub App認証（@octokit/auth-app）
- raw.githubusercontent.com（ファイルダウンロード）

**権限による制御**：GitHub Appがインストールされたリポジトリのみアクセス可能。インストールIDによって認可範囲が制限される。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 8 | プロジェクト新規作成画面 | 主画面 | GitHubリポジトリ情報の取得 |
| 10 | ブランチ詳細画面 | 参照画面 | ブランチ情報の取得 |
| 12 | スキーマ画面 | 参照画面 | ファイル取得時のリポジトリ操作 |

## 機能種別

外部API連携 / データ取得

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| installationId | number | Yes | GitHub Appインストール識別子 | 正の整数 |
| owner | string | Yes | リポジトリオーナー（ユーザー名または組織名） | 存在確認 |
| repo | string | Yes | リポジトリ名 | 存在確認 |
| repositoryFullName | string | Conditional | 「owner/repo」形式のフルネーム | `/`区切りで2要素 |
| filePath | string | Conditional | 取得対象ファイルパス | - |
| ref | string | Conditional | ブランチ名またはコミットSHA | - |
| branch | string | No | ブランチ名（デフォルト: main） | - |
| pullNumber | number | Conditional | プルリクエスト番号 | 正の整数 |
| path | string | Conditional | フォルダパス | - |
| url | string | Conditional | ダウンロード対象URL | raw.githubusercontent.comのみ許可 |
| timeoutMs | number | No | タイムアウト時間（デフォルト: 10000ms） | 正の整数 |
| maxBytes | number | No | 最大ファイルサイズ | 正の整数 |

### 入力データソース

- アプリケーション内部からの関数呼び出し
- データベースに保存されたinstallationId

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| repositories | Repository[] | リポジトリ一覧 |
| branches | GitHubBranch[] | ブランチ一覧（name, sha, isProduction） |
| fileContent | { content: string, sha: string } | ファイル内容とSHA |
| lastCommit | { sha, date, message, author } | 最新コミット情報 |
| organizationInfo | { avatar_url: string } | 組織情報 |
| folderContents | GitHubContentItem[] | フォルダ内のファイル/ディレクトリ一覧 |
| pullRequest | PullRequest | プルリクエスト詳細 |

### 出力先

呼び出し元への戻り値として返却

## 処理フロー

### 処理シーケンス

```
1. createOctokit関数呼び出し
   └─ GitHub App認証情報を使用してOctokitインスタンス生成
2. API呼び出し実行
   ├─ getRepositoriesByInstallationId: /installation/repositories
   ├─ getRepositoryBranches: repos.listBranches + repos.get
   ├─ getFileContent: repos.getContent
   ├─ getLastCommit: repos.listCommits
   ├─ getOrganizationInfo: repos.get
   ├─ getFolderContents: repos.getContent
   └─ downloadFileContent: fetch(raw URL)
3. レスポンス処理
   └─ データ整形と返却
```

### フローチャート

```mermaid
flowchart TD
    A[API呼び出し] --> B[createOctokit]
    B --> C{認証成功?}
    C -->|No| D[エラー返却]
    C -->|Yes| E[GitHub API呼び出し]
    E --> F{レスポンス成功?}
    F -->|No| G{404エラー?}
    G -->|Yes| H[null返却/ログ出力]
    G -->|No| I[エラーログ出力]
    F -->|Yes| J[データ整形]
    J --> K[結果返却]
    H --> K
    I --> D
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-43-01 | GitHub App認証必須 | 全てのリポジトリ操作はGitHub App認証を使用 | API呼び出し時 |
| BR-43-02 | インストールID必須 | 認証にはinstallationIdが必要 | 認可されたリポジトリのみアクセス可能 |
| BR-43-03 | 404静音処理 | ファイル不在の404エラーは警告ログのみ | getFileContent実行時 |
| BR-43-04 | ダウンロードURL制限 | raw.githubusercontent.comのみ許可 | downloadFileContent実行時 |
| BR-43-05 | デフォルトブランチ判定 | リポジトリのdefault_branchと一致するブランチをisProduction=trueに設定 | getRepositoryBranches実行時 |

### 計算ロジック

**ファイルサイズ制限判定**：
```typescript
if (typeof maxBytes === 'number') {
  const len = response.headers.get('content-length')
  if (len && Number(len) > maxBytes) {
    return err(new Error(`File too large: ${len} bytes`))
  }
}
```

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| - | - | - | 本機能はデータベースを直接操作しない |

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

本機能はGitHub APIを呼び出すユーティリティであり、データベースを直接操作しない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | 認証エラー | GitHub App認証失敗 | 設定確認（GITHUB_APP_ID、GITHUB_PRIVATE_KEY） |
| 404 | ファイル不在 | 指定ファイルが存在しない | nullを返却（正常系扱い） |
| - | タイムアウト | ダウンロード時間超過 | AbortControllerで処理中断 |
| - | ファイルサイズ超過 | maxBytes制限超過 | エラー返却 |
| - | 不正URL | raw.githubusercontent.com以外のURL | エラー返却 |
| - | リポジトリ形式エラー | owner/repo形式でない | nullを返却 |

### リトライ仕様

本機能自体はリトライを行わない。必要に応じて呼び出し元でretry関数（No.41）を使用する。

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

本機能はGitHub APIへの読み取り操作のみであり、トランザクション管理は不要。

## パフォーマンス要件

- GitHub API応答時間に依存（通常100-500ms）
- ダウンロード: タイムアウト10秒（デフォルト）
- ページネーション: per_page=100で最大100件取得

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

- **GitHub App認証**: Private Keyによるセキュアな認証
- **インストールID制限**: インストール済みリポジトリのみアクセス可能
- **URL検証**: raw.githubusercontent.comのみ許可（SSRF対策）
- **環境変数保護**: GITHUB_APP_ID、GITHUB_PRIVATE_KEYは環境変数で管理

## 備考

- @octokit/restライブラリを使用
- @octokit/auth-appによるGitHub App認証
- neverthrowのResult型を一部関数で使用
- 公開リポジトリへのアクセスは認証不要のOctokitインスタンスを使用可能

---

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

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

### 推奨読解順序

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

まず、GitHubリポジトリ操作で使用される型定義を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | types.ts | `frontend/internal-packages/github/src/types.ts` | GitHubContentItem、GitHubBranch等の型定義 |

**読解のコツ**: @octokit/openapi-typesからインポートされるInstallation、Repository型と、独自定義のGitHubContentItem、GitHubBranch型の違いに注目。

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

パッケージのエクスポート構造を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | index.ts | `frontend/internal-packages/github/src/index.ts` | 全モジュールのエクスポート構造 |

**主要処理フロー**:
- **1-5行目**: 各サブモジュールの再エクスポート

#### Step 3: サーバーサイドAPI実装を理解する

GitHub App認証を使用したAPIを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | api.server.ts | `frontend/internal-packages/github/src/api.server.ts` | メイン実装ファイル |

**主要処理フロー**:
- **11-22行目**: createOctokit関数（認証付きクライアント生成）
- **24-39行目**: getPullRequestDetails関数
- **41-49行目**: getRepositoriesByInstallationId関数
- **54-106行目**: getFileContent関数（ファイル取得とエラーハンドリング）
- **108-131行目**: getRepositoryBranches関数
- **137-183行目**: getLastCommit関数
- **189-212行目**: getOrganizationInfo関数
- **216-241行目**: getFolderContents関数
- **246-306行目**: downloadFileContent関数（セキュリティチェック付き）

#### Step 4: OAuth API実装を理解する

ユーザートークンを使用したAPIを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | api.oauth.ts | `frontend/internal-packages/github/src/api.oauth.ts` | OAuth認証でのInstallation取得 |

**主要処理フロー**:
- **26-57行目**: getInstallations関数（ユーザートークンでインストール一覧取得）

#### Step 5: 設定検証を理解する

必須環境変数の検証を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | config.ts | `frontend/internal-packages/github/src/config.ts` | 環境変数検証 |

**主要処理フロー**:
- **1-10行目**: validateConfig関数（GITHUB_APP_ID、GITHUB_PRIVATE_KEYの存在確認）

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

```
呼び出し元（Webアプリ等）
    │
    ├─ getRepositoriesByInstallationId(installationId)
    │      └─ createOctokit(installationId)
    │             └─ Octokit({ authStrategy: createAppAuth, ... })
    │
    ├─ getRepositoryBranches(installationId, owner, repo)
    │      └─ createOctokit(installationId)
    │             ├─ octokit.repos.get()  ← デフォルトブランチ取得
    │             └─ octokit.paginate(repos.listBranches)
    │
    ├─ getFileContent(repositoryFullName, filePath, ref, installationId)
    │      └─ createOctokit(installationId)
    │             └─ octokit.repos.getContent()
    │
    ├─ getLastCommit(installationId, owner, repo, branch)
    │      └─ createOctokit(installationId)
    │             └─ octokit.repos.listCommits()
    │
    ├─ getFolderContents(owner, repo, path, ref)
    │      └─ Octokit()  ← 認証なし（公開リポジトリ用）
    │             └─ octokit.repos.getContent()
    │
    └─ downloadFileContent(url, timeoutMs, maxBytes)
           └─ fetch(url, { signal: AbortController })
```

### データフロー図

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

                     ┌─────────────────────────┐
installationId ─────▶│    createOctokit       │
                     │    (GitHub App認証)     │
                     └──────────┬──────────────┘
                                │
                     ┌──────────▼──────────────┐
owner, repo ────────▶│   GitHub REST API      │──────────▶ Repository[]
                     │   /installation/repos   │           GitHubBranch[]
                     └──────────┬──────────────┘
                                │
                     ┌──────────▼──────────────┐
filePath, ref ──────▶│   repos.getContent     │──────────▶ { content, sha }
                     │   (Base64 decode)       │
                     └─────────────────────────┘

[ファイルダウンロードフロー]

                     ┌─────────────────────────┐
URL ────────────────▶│   URL検証              │
                     │   (raw.githubusercontent│
                     │    .comのみ許可)        │
                     └──────────┬──────────────┘
                                │
                     ┌──────────▼──────────────┐
                     │   fetch with timeout    │──────────▶ Result<string, Error>
                     │   + size check          │
                     └─────────────────────────┘
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| api.server.ts | `frontend/internal-packages/github/src/api.server.ts` | ソース | サーバーサイドGitHub API実装 |
| api.oauth.ts | `frontend/internal-packages/github/src/api.oauth.ts` | ソース | OAuthトークンを使用したAPI |
| types.ts | `frontend/internal-packages/github/src/types.ts` | ソース | 型定義 |
| config.ts | `frontend/internal-packages/github/src/config.ts` | ソース | 設定検証 |
| factories.ts | `frontend/internal-packages/github/src/factories.ts` | ソース | テスト用ファクトリ |
| index.ts | `frontend/internal-packages/github/src/index.ts` | ソース | パッケージエントリーポイント |
| api.server.test.ts | `frontend/internal-packages/github/src/api.server.test.ts` | テスト | API機能のテスト |
