Skip to content

GitLab Webhook設計

概要

Issue Outsource PlatformがGitLab (gitlab.ethan-tech.jp) から受信するWebhookイベントの設計。 Webhookを通じてGitLab上の変更をリアルタイムにPF側へ反映する。


受信するWebhookイベント一覧

イベント種別GitLabイベント名トリガー条件PF側の処理
IssueissueIssue作成・更新・クローズ・再オープンPF側Issue同期、ステータス更新
Merge Requestmerge_requestMR作成・更新・マージ・クローズレビューステータス反映、納品判定
Note (コメント)noteIssue/MRへのコメント追加コメント同期、レビューフィードバック取得
PipelinepipelineCI/CDパイプライン完了・失敗テスト結果取得、品質チェック反映

Webhookエンドポイント

POST /api/v1/webhooks/gitlab

リクエストヘッダ

ヘッダ名説明
X-Gitlab-Eventイベント種別(例: Issue Hook, Merge Request Hook
X-Gitlab-Tokenシークレットトークン(署名検証用)
X-Gitlab-Event-UUIDイベントの一意識別子(冪等性に使用)
Content-Typeapplication/json

シークレットトークンによる署名検証

検証フロー

1. GitLabプロジェクト設定でWebhookシークレットトークンを登録
2. GitLabがWebhook送信時に `X-Gitlab-Token` ヘッダにトークンを付与
3. PF側で受信時にヘッダ値とサーバー保存値を比較
4. 不一致の場合は 403 Forbidden を返却しリクエストを破棄

実装方針

python
import hmac
from fastapi import Request, HTTPException

WEBHOOK_SECRET = os.environ["GITLAB_WEBHOOK_SECRET"]  # Keychainから取得

async def verify_gitlab_webhook(request: Request):
    token = request.headers.get("X-Gitlab-Token")
    if not token or not hmac.compare_digest(token, WEBHOOK_SECRET):
        raise HTTPException(status_code=403, detail="Invalid webhook token")

冪等性の担保(イベントID重複排除)

方針

GitLabは同一イベントを再送する可能性がある(ネットワーク障害時のリトライ等)。 X-Gitlab-Event-UUID を使い、処理済みイベントの重複実行を防ぐ。

実装方針

1. Webhook受信時に X-Gitlab-Event-UUID を取得
2. Redis (TTL: 24時間) または DBテーブルで処理済みUUIDを管理
3. 既に処理済みの場合は 200 OK を返却(再処理しない)
4. 未処理の場合はUUIDを記録し、イベント処理を実行

データ構造

sql
CREATE TABLE webhook_events (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    gitlab_event_uuid VARCHAR(255) UNIQUE NOT NULL,
    event_type VARCHAR(100) NOT NULL,
    payload JSONB NOT NULL,
    status VARCHAR(20) DEFAULT 'received',  -- received, processing, completed, failed
    created_at TIMESTAMP DEFAULT NOW(),
    processed_at TIMESTAMP
);

CREATE INDEX idx_webhook_events_uuid ON webhook_events(gitlab_event_uuid);
CREATE INDEX idx_webhook_events_status ON webhook_events(status);

各イベントの処理フロー

Issue Hook

mermaid
sequenceDiagram
    participant GL as GitLab
    participant WH as Webhook Handler
    participant DB as Database
    participant Sync as SyncService

    GL->>WH: POST /webhooks/gitlab (Issue Hook)
    WH->>WH: 署名検証 (X-Gitlab-Token)
    WH->>DB: 冪等性チェック (X-Gitlab-Event-UUID)
    alt 処理済み
        WH-->>GL: 200 OK (skip)
    else 未処理
        WH->>DB: イベント記録 (status: received)
        WH-->>GL: 200 OK (accepted)
        WH->>Sync: 非同期処理キュー投入
        Sync->>DB: GitLab Issueデータ取得
        alt action: open
            Sync->>DB: PF側Issue作成 or 更新
        else action: update
            Sync->>DB: タイトル・説明・ラベル同期
        else action: close
            Sync->>DB: PF側Issueステータス → closed
            Sync->>DB: アサイン状態チェック・報酬処理トリガー
        else action: reopen
            Sync->>DB: PF側Issueステータス → open
        end
        Sync->>DB: イベントstatus → completed
    end

Merge Request Hook

mermaid
sequenceDiagram
    participant GL as GitLab
    participant WH as Webhook Handler
    participant DB as Database
    participant Review as ReviewService
    participant Reward as RewardService

    GL->>WH: POST /webhooks/gitlab (MR Hook)
    WH->>WH: 署名検証
    WH->>DB: 冪等性チェック
    WH-->>GL: 200 OK
    WH->>Review: 非同期処理

    alt action: open
        Review->>DB: レビューレコード作成 (status: pending)
        Review->>DB: 対応Issue紐付け
    else action: update
        Review->>DB: MR情報更新(コミット、差分)
    else action: merge
        Review->>DB: レビューstatus → approved
        Review->>Reward: 報酬確定トリガー
        Reward->>DB: 報酬レコード作成 (status: confirmed)
        Reward->>DB: Issue status → completed
    else action: close (マージなし)
        Review->>DB: レビューstatus → rejected
        Review->>DB: Issue status → open (再アサイン可能)
    end

Note Hook (コメント)

mermaid
sequenceDiagram
    participant GL as GitLab
    participant WH as Webhook Handler
    participant DB as Database
    participant Notify as NotificationService

    GL->>WH: POST /webhooks/gitlab (Note Hook)
    WH->>WH: 署名検証・冪等性チェック
    WH-->>GL: 200 OK

    alt noteable_type: Issue
        WH->>DB: Issueコメント保存
        WH->>Notify: 担当エンジニアへ通知
    else noteable_type: MergeRequest
        WH->>DB: レビューコメント保存
        alt レビューア承認コメント
            WH->>DB: レビューstatus更新
        end
        WH->>Notify: MR関係者へ通知
    end

Pipeline Hook

mermaid
sequenceDiagram
    participant GL as GitLab
    participant WH as Webhook Handler
    participant DB as Database
    participant Quality as QualityService

    GL->>WH: POST /webhooks/gitlab (Pipeline Hook)
    WH->>WH: 署名検証・冪等性チェック
    WH-->>GL: 200 OK

    WH->>DB: パイプライン結果保存
    alt status: success
        WH->>Quality: テスト結果OK記録
        WH->>DB: MR品質チェック → passed
    else status: failed
        WH->>Quality: 失敗内容記録
        WH->>DB: MR品質チェック → failed
        WH->>DB: 担当エンジニアへ通知
    end

Webhookペイロード例

Issue Hook ペイロード(抜粋)

json
{
  "object_kind": "issue",
  "event_type": "issue",
  "user": {
    "id": 1,
    "username": "engineer1"
  },
  "project": {
    "id": 78,
    "name": "issueoutsourcing",
    "web_url": "https://gitlab.ethan-tech.jp/aieo/issueoutsourcing"
  },
  "object_attributes": {
    "id": 100,
    "iid": 5,
    "title": "GitLab連携API設計",
    "description": "...",
    "state": "opened",
    "action": "open",
    "labels": [
      { "title": "reward:10000" },
      { "title": "difficulty:medium" }
    ],
    "assignee_ids": [2]
  }
}

Merge Request Hook ペイロード(抜粋)

json
{
  "object_kind": "merge_request",
  "event_type": "merge_request",
  "user": {
    "id": 2,
    "username": "engineer1"
  },
  "project": {
    "id": 78
  },
  "object_attributes": {
    "id": 200,
    "iid": 10,
    "title": "feat: Issue #5 GitLab連携API実装",
    "state": "merged",
    "action": "merge",
    "source_branch": "feature/issue-5",
    "target_branch": "main",
    "merge_status": "can_be_merged"
  }
}

エラーハンドリング

状況レスポンス備考
署名検証失敗403 Forbiddenログに警告記録
不明なイベント種別200 OK無視(ログに記録)
ペイロードパースエラー400 Bad Requestエラーログ記録
内部処理エラー200 OK + キュー投入非同期リトライで対応
重複イベント200 OK処理スキップ

リトライ戦略

- Webhook受信は即座に 200 OK を返却(GitLab側リトライを防止)
- 実処理は非同期キュー(Celery / asyncio task)で実行
- 失敗時は指数バックオフでリトライ(最大5回)
  - 1回目: 10秒後
  - 2回目: 30秒後
  - 3回目: 90秒後
  - 4回目: 270秒後
  - 5回目: 810秒後
- 5回失敗後は status: failed としてアラート通知

GitLabプロジェクト設定

Webhook登録

GitLab Project > Settings > Webhooks
URL: https://<PF_DOMAIN>/api/v1/webhooks/gitlab
Secret Token: <GITLAB_WEBHOOK_SECRET>
Trigger:
  ✅ Push events (無効)
  ✅ Issues events
  ✅ Merge request events
  ✅ Note events
  ✅ Pipeline events
SSL verification: ✅ Enable