# 機能設計書 2-プロジェクト編集

## 概要

本ドキュメントは、GitLabにおける既存プロジェクトの設定編集機能の詳細設計を記述する。この機能は、プロジェクト管理者がプロジェクトの名前、説明、アバター、可視性レベル、各種機能設定などを変更するための機能である。

### 本機能の処理概要

プロジェクト編集機能は、既存プロジェクトの様々な設定を更新するための包括的な機能を提供する。

**業務上の目的・背景**：プロジェクトのライフサイクルにおいて、プロジェクト名の変更、説明の更新、可視性レベルの調整、各種機能（イシュー、マージリクエスト、Wiki等）の有効化/無効化など、様々な設定変更が必要になる。この機能により、プロジェクト管理者は柔軟にプロジェクト設定を管理できる。

**機能の利用シーン**：
- プロジェクト名やパスの変更が必要な時
- プロジェクトの可視性レベル（Private/Internal/Public）を変更する時
- Wiki、イシュー、マージリクエストなどの機能を有効化/無効化する時
- デフォルトブランチを変更する時
- マージリクエストのマージ方法設定を変更する時

**主要な処理内容**：
1. プロジェクト設定の読み込みと編集フォーム表示
2. ユーザー入力のバリデーション
3. 可視性レベル変更の権限・制限チェック
4. デフォルトブランチ変更処理
5. プロジェクトレコードの更新
6. パス変更時のリネーム処理（リポジトリ、アップロード、コンテナレジストリ）
7. 変更に伴う関連処理（Todo削除、権限更新等）
8. システムフック実行

**関連システム・外部連携**：
- Gitaly（リポジトリ管理、ブランチ変更）
- Container Registry（パス変更時のリネーム）
- システムフック（更新イベント通知）

**権限による制御**：
- プロジェクト設定編集にはMaintainer以上のロールが必要
- 一部設定（max_artifacts_size等）はAdmin権限が必要
- 可視性レベル変更は名前空間の可視性制限に従う

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 26 | プロジェクト編集 | 主画面 | プロジェクト設定の編集処理 |
| 24 | プロジェクト詳細 | 結果表示画面 | 編集後のプロジェクト表示 |

## 機能種別

CRUD操作（Update）/ バリデーション / データ連携

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| name | String | No | プロジェクト名 | 空白不可（変更時）、255文字以内 |
| path | String | No | プロジェクトパス | 英数字・ハイフン・アンダースコア |
| description | String | No | プロジェクト説明 | 2000文字以内 |
| visibility_level | Integer | No | 可視性レベル | 名前空間の可視性以下 |
| default_branch | String | No | デフォルトブランチ | 存在するブランチ名 |
| issues_access_level | Integer | No | イシュー機能アクセスレベル | 0-20 |
| merge_requests_access_level | Integer | No | MR機能アクセスレベル | 0-20 |
| wiki_access_level | Integer | No | Wiki機能アクセスレベル | 0-20 |
| repository_access_level | Integer | No | リポジトリアクセスレベル | 0-20 |
| merge_method | String | No | マージ方法 | merge, rebase_merge, ff |
| squash_option | String | No | スカッシュオプション | never, always, default_on, default_off |
| avatar | File | No | アバター画像 | 画像ファイル形式 |
| remove_source_branch_after_merge | Boolean | No | マージ後ソースブランチ削除 | - |

### 入力データソース

- 画面入力（Web UI - Settings画面）
- API リクエスト（REST API / GraphQL）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| result | Hash | 処理結果 |
| result[:status] | Symbol | :success または :error |
| result[:message] | String | エラーメッセージ（失敗時） |

### 出力先

- データベース（projectsテーブル、project_featuresテーブル等）
- Gitリポジトリ（デフォルトブランチ変更時）
- ファイルシステム（パス変更時のリネーム）
- Container Registry（パス変更時）

## 処理フロー

### 処理シーケンス

```
1. リクエスト受付（ProjectsController#update）
   └─ パラメータのサニタイズとパーミッション確認

2. サービス呼び出し（Projects::UpdateService#execute）
   └─ ビジネスロジックの実行開始

3. 前処理
   ├─ トピック情報の構築
   ├─ CI/CD設定の確認・作成
   ├─ 許可されないパラメータの削除
   └─ Pagesユニークドメイン追加

4. バリデーション
   ├─ 可視性レベル変更チェック
   ├─ デフォルトブランチ変更チェック
   ├─ コンテナレジストリタグ存在時のリネームチェック
   ├─ パイプライン変数最小権限ロール変更チェック
   └─ Pagesアクセスレベル検証

5. Wiki有効化処理（必要時）
   └─ Wikiリポジトリ作成

6. リポジトリストレージ移動（必要時）
   └─ ストレージ移動スケジュール

7. プロジェクト更新
   └─ project.update!

8. 更新後処理（after_update）
   ├─ プライベート変更時のTodo削除スケジュール
   ├─ パス変更時のAfterRenameService実行
   │      ├─ アップロードの移動
   │      ├─ Pagesの移動
   │      └─ システムフック実行
   ├─ ランナー設定変更時のペンディングビルド更新
   └─ イベント発行

9. レスポンス返却
   └─ 成功時：編集画面へリダイレクト
      失敗時：エラーメッセージ表示
```

### フローチャート

```mermaid
flowchart TD
    A[開始: update リクエスト受付] --> B{認証・権限確認}
    B -->|No| C[権限エラー]
    B -->|Yes| D[前処理実行]
    D --> E{可視性レベル変更OK?}
    E -->|No| F[可視性エラー]
    E -->|Yes| G{デフォルトブランチ変更?}
    G -->|Yes| H[ブランチ変更処理]
    G -->|No| I{パス変更でCR有り?}
    H --> I
    I -->|Yes| J{CR API対応?}
    J -->|No| K[リネームエラー]
    J -->|Yes| L[ドライラン検証]
    L -->|失敗| K
    L -->|成功| M[プロジェクト更新]
    I -->|No| M
    M --> N{更新成功?}
    N -->|No| O[更新エラー]
    N -->|Yes| P[更新後処理]
    P --> Q{パス変更あり?}
    Q -->|Yes| R[AfterRenameService]
    Q -->|No| S[システムフック実行]
    R --> S
    S --> T[イベント発行]
    T --> U[成功レスポンス]

    C --> V[終了]
    F --> V
    K --> V
    O --> V
    U --> V
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-001 | 可視性レベル制限 | 名前空間の可視性を超える設定は不可 | 可視性変更時 |
| BR-002 | パス変更制限（CR） | コンテナレジストリタグ存在時、異なるルート名前空間への変更は不可 | パス変更時 |
| BR-003 | デフォルトブランチ存在確認 | 存在しないブランチへの変更は不可 | デフォルトブランチ変更時 |
| BR-004 | HEAD分岐競合チェック | HEADという名前のブランチが存在する場合、エラー | デフォルトブランチ変更時 |
| BR-005 | NPMパッケージパス制限 | ルートグループにスコープ付きNPMパッケージがある場合、パス変更不可 | パス変更時 |

### 計算ロジック

Pagesアクセスレベル決定：
```ruby
# プロジェクトの可視性に基づいて許可されるPagesアクセスレベルを決定
allowed_levels = ProjectFeature::PAGES_ACCESS_LEVELS_BY_PROJECT_VISIBILITY.fetch(visibility, [])
```

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| プロジェクト更新 | projects | UPDATE | プロジェクト基本情報更新 |
| 機能設定更新 | project_features | UPDATE | 機能アクセスレベル更新 |
| CI/CD設定更新 | project_ci_cd_settings | UPDATE | CI/CD関連設定更新 |
| 設定更新 | project_settings | UPDATE | プロジェクト設定更新 |
| ルート更新 | routes | UPDATE | パス変更時のルート情報 |
| リダイレクト追加 | redirect_routes | INSERT | パス変更時の旧パスリダイレクト |

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

#### projects

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | name | 入力値 | プロジェクト名 |
| UPDATE | path | 入力値 | URLパス（変更時リネーム処理発生） |
| UPDATE | description | 入力値 | 説明文 |
| UPDATE | visibility_level | 入力値（制限適用後） | 可視性レベル |
| UPDATE | updated_at | 現在時刻 | 更新日時 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| E001 | 権限エラー | 編集権限なし | Maintainer以上の権限を取得 |
| E002 | 可視性エラー | 許可されない可視性レベル設定 | 可視性レベルを下げる |
| E003 | ブランチエラー | 存在しないブランチをデフォルトに設定 | 存在するブランチを指定 |
| E004 | HEAD競合エラー | HEADブランチが存在しデフォルト変更不可 | HEADブランチをリネームまたは削除 |
| E005 | リネームエラー | CRタグ存在時のパス変更不可 | CRタグを削除してから変更 |
| E006 | バリデーションエラー | 入力値が不正 | 正しい形式で入力 |

### リトライ仕様

- 通常の更新処理でリトライは不要
- パス変更時のContainer Registry操作失敗時は手動リトライ必要

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

- プロジェクト更新はActiveRecordの暗黙的トランザクション
- パス変更時はroutes/redirect_routesの更新を含む
- 更新後処理（after_update）はコミット後に実行

## パフォーマンス要件

- 通常の更新処理は1秒以内に完了
- パス変更を伴う場合は追加処理のため数秒かかる可能性
- デフォルトブランチ変更はGitalyへの同期呼び出し

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

- Maintainer以上の権限チェック
- 可視性レベル変更時の権限検証
- パス変更時のContainer Registry権限確認
- 変更履歴の監査ログ記録

## 備考

- パス変更はプロジェクトのURLに影響するため、既存リンクのリダイレクト設定が自動作成される
- プライベートへの変更時は関連Todoが遅延削除される（誤操作対応）

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | project.rb | `app/models/project.rb` | 更新対象となる属性定義 |
| 1-2 | project_feature.rb | `app/models/project_feature.rb` | 機能アクセスレベルの定義 |

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | projects_controller.rb | `app/controllers/projects_controller.rb` | updateアクションの実装 |

**主要処理フロー**:
1. **127-134行目**: updateアクションの定義、UpdateServiceの呼び出し
2. **109-112行目**: editアクションでの編集画面表示

#### Step 3: ビジネスロジック層を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | update_service.rb | `app/services/projects/update_service.rb` | プロジェクト更新の中心ロジック |

**主要処理フロー**:
- **12-47行目**: executeメソッドのメイン処理フロー
- **73-84行目**: validate!メソッドでのバリデーション
- **111-133行目**: validate_default_branch_changeでのブランチ変更検証
- **135-155行目**: validate_renaming_project_with_tagsでのCRリネーム検証
- **227-256行目**: after_updateでの更新後処理

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

```
ProjectsController#update
    │
    ├─ Projects::UpdateService#execute
    │      │
    │      ├─ build_topics
    │      │
    │      ├─ ensure_ci_cd_settings
    │      │
    │      ├─ remove_unallowed_params
    │      │
    │      ├─ validate!
    │      │      ├─ valid_visibility_level_change?
    │      │      ├─ validate_default_branch_change
    │      │      │      └─ project.change_head (Gitalyへ)
    │      │      └─ validate_renaming_project_with_tags
    │      │             └─ ContainerRegistry::GitlabApiClient.rename_base_repository_path (dry_run)
    │      │
    │      ├─ ensure_wiki_exists (Wiki有効化時)
    │      │
    │      ├─ project.update! (DB更新)
    │      │
    │      └─ after_update
    │             │
    │             ├─ TodosDestroyer::ProjectPrivateWorker (プライベート変更時)
    │             │
    │             ├─ AfterRenameService (パス変更時)
    │             │      ├─ Gitlab::UploadsTransfer
    │             │      └─ Gitlab::PagesTransfer
    │             │
    │             ├─ system_hook_service.execute_hooks_for
    │             │
    │             └─ publish_events
    │                    ├─ ProjectFeaturesChangedEvent
    │                    └─ ProjectVisibilityChangedEvent
    │
    └─ render 'edit' (エラー時) / redirect_to (成功時)
```

### データフロー図

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

ユーザー入力          ProjectsController#update
  ├─ name                    │
  ├─ path                    ▼
  ├─ description      Projects::UpdateService
  ├─ visibility              │
  └─ feature設定             │
         │                   │
         ▼                   ▼
                     ┌──────────────────┐
                     │ バリデーション     │
                     │ ・可視性チェック   │
                     │ ・ブランチチェック │
                     │ ・CRタグチェック   │
                     └────────┬─────────┘
                              │
                              ▼
                     ┌──────────────────┐      ┌─────────────────┐
                     │ project.update!  │ ───▶ │ projectsテーブル │
                     │                  │      │ project_features │
                     └────────┬─────────┘      └─────────────────┘
                              │
                              ▼
                     ┌──────────────────┐
                     │ after_update     │
                     │ ・パス変更処理    │ ───▶ AfterRenameService
                     │ ・フック実行      │ ───▶ SystemHook
                     │ ・イベント発行    │ ───▶ EventStore
                     └──────────────────┘
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| projects_controller.rb | `app/controllers/projects_controller.rb` | ソース | コントローラー |
| update_service.rb | `app/services/projects/update_service.rb` | ソース | 更新ビジネスロジック |
| after_rename_service.rb | `app/services/projects/after_rename_service.rb` | ソース | パス変更後処理 |
| project.rb | `app/models/project.rb` | ソース | プロジェクトモデル |
| project_feature.rb | `app/models/project_feature.rb` | ソース | 機能設定モデル |
| update_visibility_level.rb | `app/services/concerns/update_visibility_level.rb` | ソース | 可視性更新ロジック |
