Rewrite: Gitea Webhook AI Review 記事リライト
All checks were successful
Deploy Docusaurus Site / deploy (push) Successful in 34s
All checks were successful
Deploy Docusaurus Site / deploy (push) Successful in 34s
This commit is contained in:
parent
9567c5149b
commit
bdab8f3d8a
@ -1,127 +1,122 @@
|
|||||||
---
|
---
|
||||||
sidebar_position: 2
|
sidebar_position: 2
|
||||||
title: ローカルGitea × Webhook連携で、AIとの開発をもっと楽にしよう!
|
title: Issue 立てるだけで AI がコードレビュー&修正してくれる仕組みを作った
|
||||||
description: GiteaのIssue/PR WebhookとOpenClawを連携し、実運用で壊れにくく回す開発プロセスを構築する実践記録
|
description: ローカルGiteaにWebhookブリッジを噛ませて、Issue/PRをトリガーにAIエージェントが自動でコードレビュー・修正・pushまでやってくれる環境を構築した話。
|
||||||
hide_table_of_contents: false
|
hide_table_of_contents: false
|
||||||
displayed_sidebar: null
|
displayed_sidebar: null
|
||||||
image: ./banner.png
|
image: ./banner.png
|
||||||
---
|
---
|
||||||
|
|
||||||
# ローカルGitea × Webhook連携で、AIとの開発をもっと楽にしよう!
|
# Issue 立てるだけで AI がコードレビュー&修正してくれる仕組みを作った
|
||||||
|
|
||||||
## はじめに
|
## きっかけ
|
||||||
|
|
||||||
「Issue を立てるだけで、AI がレビューして修正提案まで返してくれたら最高なのに…」
|
GitHub だと Copilot や GitHub Actions を使って、PR にレビューコメントつけたり自動テスト回したりできるよね。Devin みたいに Issue を投げたら勝手にコード書いてくれるサービスもある。
|
||||||
|
|
||||||
そんな願望から始めたのが、**Gitea Webhook → OpenClaw** の自動レビュー連携です。
|
でもうちの開発環境は **ローカルの Gitea**。GitHub のエコシステムはそのままじゃ使えない。
|
||||||
最初は「Issue 作成でAIを呼べればOK」と思っていたのですが、実運用に入るとすぐに壁に当たりました。
|
|
||||||
|
|
||||||
- `EOF` でWebhookが落ちる
|
「じゃあ同じことをローカルで再現すればいいじゃん」ということで、Gitea の Webhook と OpenClaw の AI エージェントを繋げて、**Issue 立てたら AI が勝手にレビューして修正して push してくれる** 仕組みを作った。
|
||||||
- PRを投げても `OK (ignored event)`
|
|
||||||
- ブリッジの別機能改修で本線が壊れる
|
|
||||||
- Gitが苦手なメンバーがブランチ運用で詰まる
|
|
||||||
|
|
||||||
この記事は、その失敗込みで最終的に安定した **運用可能な形** をまとめたものです。
|
## 何ができるようになったか
|
||||||
|
|
||||||
## この記事でわかること
|
こんな感じの開発フローが回ってる:
|
||||||
|
|
||||||
- Issue / PR の両方をトリガーにする Webhook 設計
|
1. Gitea で Issue を立てる(「この関数のエラーハンドリングがザルだから直して」とか)
|
||||||
- bridge.py の実装で絶対外せない防御ポイント
|
2. Webhook が飛んで、AI エージェント(GPT-5.3-codex)が起動
|
||||||
- systemd での安定運用
|
3. リポジトリをクローンして、Issue の内容に沿ってコードレビュー
|
||||||
- 壊れにくいブランチ運用(Git弱者向け手順を毎回添える)
|
4. 修正ブランチを切って、コード修正、push
|
||||||
- 機能混在で壊れたときの再発防止(リポジトリ分離)
|
5. Issue にコメントで結果報告(ブランチ名、何を直したか、マージ手順)
|
||||||
|
|
||||||
## 全体構成(最新版)
|
PR が来た時も同じようにレビューが走る。人間がやるのは **Issue を書くことと、最後にマージするかどうか判断するだけ**。
|
||||||
|
|
||||||
|
## 全体の構成
|
||||||
|
|
||||||
```
|
```
|
||||||
┌─────────┐ Webhook (issues / pull_request) ┌────────────────────┐
|
Gitea (Issue/PR作成)
|
||||||
│ Gitea │ ────────────────────────────────────→ │ gitea-webhook-bridge │
|
│
|
||||||
└─────────┘ │ bridge.py │
|
│ Webhook (POST /webhook/gitea)
|
||||||
└────────┬──────────┘
|
▼
|
||||||
│
|
gitea-webhook-bridge (Python, port 9876)
|
||||||
openclaw agent
|
│
|
||||||
│
|
│ openclaw agent --agent codex
|
||||||
code-review-loop
|
▼
|
||||||
│
|
OpenClaw Codex エージェント (GPT-5.3-codex)
|
||||||
Issue/PRコメントで返却
|
│
|
||||||
|
│ code-review-loop スキル
|
||||||
|
▼
|
||||||
|
レビュー → 修正 → push → Issue にコメント
|
||||||
```
|
```
|
||||||
|
|
||||||
## なぜ最初に壊れたのか(実話)
|
登場人物は3つだけ:
|
||||||
|
|
||||||
### 1) `EOF` でDelivery失敗
|
- **Gitea** — セルフホストの Git サーバー
|
||||||
|
- **Webhook Bridge** — Gitea のイベントを受けて AI を起動する Python スクリプト
|
||||||
|
- **OpenClaw + Codex エージェント** — 実際にコードを読んで直す AI
|
||||||
|
|
||||||
原因は、Gitea側ではなく受信側でした。`do_POST` 内で例外が出ているのに、HTTPレスポンスを返さず接続が閉じる。
|
## Webhook Bridge の中身
|
||||||
Giteaから見ると `Post ... EOF` です。
|
|
||||||
|
|
||||||
**対策:**
|
ブリッジ自体はシンプルな Python の HTTP サーバー。Gitea からの Webhook を受けて、OpenClaw の codex エージェントにタスクを投げるだけ。
|
||||||
- `do_POST` 全体を `try/except` で囲む
|
|
||||||
- 例外時も `500` を必ず返す
|
|
||||||
|
|
||||||
### 2) PRが無視される
|
```python title="bridge.py(抜粋)"
|
||||||
|
class WebhookHandler(BaseHTTPRequestHandler):
|
||||||
|
def do_POST(self):
|
||||||
|
if self.path == "/webhook/gitea":
|
||||||
|
return self.handle_gitea_webhook()
|
||||||
|
|
||||||
Issue対応だけ先に作ると、PRイベントは素通りします。
|
def handle_gitea_webhook(self):
|
||||||
その結果 `OK (ignored event)`。
|
# 1. 署名検証
|
||||||
|
# 2. イベント種別チェック (issues / pull_request / issue_comment)
|
||||||
|
# 3. タスク文を組み立て
|
||||||
|
# 4. openclaw agent を非同期で起動
|
||||||
|
# 5. すぐに 200 OK を返す
|
||||||
|
```
|
||||||
|
|
||||||
**対策:**
|
### 対応しているイベント
|
||||||
- `issues` だけでなく `pull_request` も受理
|
|
||||||
- `opened / reopened / synchronize` を最低限処理
|
|
||||||
|
|
||||||
### 3) 別用途改修でブリッジが壊れる
|
| イベント | アクション | やること |
|
||||||
|
|---------|-----------|---------|
|
||||||
|
| Issue 作成 | `opened` | コードレビュー&修正 |
|
||||||
|
| Issue コメント | `created` | コメントの依頼に対応 |
|
||||||
|
| PR 作成/更新 | `opened` / `reopened` / `synchronize` | PR レビュー |
|
||||||
|
|
||||||
Discord通知などを同じブリッジに混ぜると、修正の副作用でGitea連携が壊れやすい。
|
Issue へのコメントも拾えるようにしてるから、「ここもう少し直して」みたいな追加指示もそのまま通る。
|
||||||
|
|
||||||
**対策:**
|
### ボットのコメントは無視する
|
||||||
- 役割ごとにリポジトリを分離
|
|
||||||
- `gitea-webhook-bridge`
|
|
||||||
- `discord-notify-bridge`
|
|
||||||
- systemdサービスも分離
|
|
||||||
|
|
||||||
## bridge.py の実装指針
|
これ忘れると無限ループになる。AI がコメント → Webhook 発火 → AI がまたコメント → ...
|
||||||
|
|
||||||
### 受けるイベント
|
```json title="config.json"
|
||||||
|
{
|
||||||
|
"bot_user": "koide",
|
||||||
|
"ignore_comment_users": ["koide"]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
- `issues`
|
AI エージェントが使う Gitea アカウント名を `ignore_comment_users` に入れておけば OK。
|
||||||
- `opened`
|
|
||||||
- `pull_request`
|
|
||||||
- `opened`
|
|
||||||
- `reopened`
|
|
||||||
- `synchronize`
|
|
||||||
|
|
||||||
### 最低限の処理順
|
## Gitea 側の設定
|
||||||
|
|
||||||
1. 署名検証(`X-Gitea-Signature`)
|
### Webhook の登録
|
||||||
2. イベント種別チェック
|
|
||||||
3. アクションチェック
|
|
||||||
4. タスク本文を整形
|
|
||||||
5. `openclaw agent` を非同期起動
|
|
||||||
6. すぐに `200 OK` を返す
|
|
||||||
|
|
||||||
### 署名検証は必須
|
リポジトリの Settings → Webhooks → Add Webhook:
|
||||||
|
|
||||||
:::warning
|
- **URL**: `http://<bridge-host>:9876/webhook/gitea`
|
||||||
ローカルネットワークでも署名検証は省略しない方が安全です。
|
- **Content Type**: `application/json`
|
||||||
Webhook URLを知っているだけで叩ける状態は避けましょう。
|
- **Secret**: bridge 側の `.env` と一致させる
|
||||||
:::
|
- **Trigger**: Issues / Pull Request にチェック
|
||||||
|
|
||||||
## Gitea側の設定
|
### プライベート IP への Webhook を許可する
|
||||||
|
|
||||||
### `ALLOWED_HOST_LIST` を忘れない
|
デフォルトだと Gitea はプライベート IP への Webhook をブロックする。ローカル環境では `app.ini` に以下を追加:
|
||||||
|
|
||||||
```ini title="app.ini"
|
```ini title="app.ini"
|
||||||
[webhook]
|
[webhook]
|
||||||
ALLOWED_HOST_LIST = private
|
ALLOWED_HOST_LIST = private
|
||||||
```
|
```
|
||||||
|
|
||||||
これを忘れると、プライベートIP向けWebhookがブロックされます。
|
これを忘れると Webhook が一切飛ばないから、最初にやっておくこと。
|
||||||
|
|
||||||
### Webhookの基本値
|
## systemd で常駐させる
|
||||||
|
|
||||||
- URL: `http://<bridge-host>:9876/webhook/gitea`
|
|
||||||
- Content-Type: `application/json`
|
|
||||||
- Secret: bridgeと一致
|
|
||||||
- Trigger: `issues`, `pull_request`
|
|
||||||
|
|
||||||
## systemd常駐(再起動に強い構成)
|
|
||||||
|
|
||||||
```ini title="/etc/systemd/system/gitea-webhook-bridge.service"
|
```ini title="/etc/systemd/system/gitea-webhook-bridge.service"
|
||||||
[Unit]
|
[Unit]
|
||||||
@ -133,7 +128,7 @@ Type=simple
|
|||||||
User=swallow
|
User=swallow
|
||||||
WorkingDirectory=/home/swallow/gitea-webhook-bridge
|
WorkingDirectory=/home/swallow/gitea-webhook-bridge
|
||||||
EnvironmentFile=/home/swallow/gitea-webhook-bridge/.env
|
EnvironmentFile=/home/swallow/gitea-webhook-bridge/.env
|
||||||
ExecStart=/usr/bin/python3 /home/swallow/gitea-webhook-bridge/bridge.py
|
ExecStart=/usr/bin/python3 bridge.py
|
||||||
Restart=always
|
Restart=always
|
||||||
RestartSec=5
|
RestartSec=5
|
||||||
|
|
||||||
@ -142,85 +137,88 @@ WantedBy=multi-user.target
|
|||||||
```
|
```
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo systemctl daemon-reload
|
|
||||||
sudo systemctl enable --now gitea-webhook-bridge
|
sudo systemctl enable --now gitea-webhook-bridge
|
||||||
sudo systemctl status gitea-webhook-bridge
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## 開発プロセス(ここが本体)
|
`Restart=always` にしてるから、万が一落ちても勝手に復帰する。
|
||||||
|
|
||||||
単にWebhookを動かすだけだと、チーム運用では破綻します。
|
## ハマったところ
|
||||||
最終的に安定したのは、以下のルールを固定したからです。
|
|
||||||
|
|
||||||
### ルール1: ブランチ命名を固定
|
### Webhook Bridge の実装
|
||||||
|
|
||||||
- `fix/issue-<番号>-<要約>`
|
正直、ブリッジ自体も vibe コーディングで AI に作ってもらった。「Gitea の Webhook 受けて OpenClaw のエージェント起動するブリッジ書いて」って投げたらだいたい動くものが出てきた。
|
||||||
- `feat/issue-<番号>-<要約>`
|
|
||||||
- `docs/issue-<番号>-<要約>`
|
|
||||||
|
|
||||||
### ルール2: Issueコメントに毎回「3点セット」
|
ただ、いくつかハマりポイントはあった:
|
||||||
|
|
||||||
- ブランチ切替手順
|
**1. EOF で Webhook が失敗する**
|
||||||
|
|
||||||
|
`do_POST` 内で例外が出た時に HTTP レスポンスを返さないと、Gitea 側から見ると `EOF` エラーになる。`try/except` で囲んで、例外時も必ず `500` を返すようにする。
|
||||||
|
|
||||||
|
```python
|
||||||
|
def do_POST(self):
|
||||||
|
try:
|
||||||
|
# 処理
|
||||||
|
except Exception as e:
|
||||||
|
self.send_response(500)
|
||||||
|
self.end_headers()
|
||||||
|
```
|
||||||
|
|
||||||
|
**2. PR イベントが無視される**
|
||||||
|
|
||||||
|
最初 Issue だけ対応して満足してたら、PR のレビューが走らなくて「あれ?」ってなった。`pull_request` イベントも明示的に受理する必要がある。
|
||||||
|
|
||||||
|
**3. 署名検証**
|
||||||
|
|
||||||
|
ローカルネットワークだからいいかと思って最初は省略してたけど、Webhook URL を知ってるだけで叩けちゃう状態はさすがにまずいので追加した。HMAC-SHA256 で検証してる。
|
||||||
|
|
||||||
|
## AI エージェントの動き
|
||||||
|
|
||||||
|
OpenClaw の codex エージェント(うちでは「小出君」と呼んでる)が実際にやってくれることは:
|
||||||
|
|
||||||
|
1. リポジトリを clone
|
||||||
|
2. Issue/PR の内容を読んで問題を把握
|
||||||
|
3. コードを読んでレビュー
|
||||||
|
4. 修正が必要ならブランチを切ってコード修正
|
||||||
|
5. push して Issue にコメント
|
||||||
|
|
||||||
|
コメントには毎回こういう情報を含めてくれる:
|
||||||
|
|
||||||
|
- 何を修正したか
|
||||||
|
- ブランチの切替手順(`git fetch && git switch fix/issue-123-xxx`)
|
||||||
- 検証コマンド
|
- 検証コマンド
|
||||||
- mainへの反映手順
|
- main へのマージ手順
|
||||||
|
|
||||||
これを毎回書くだけで、Gitに不慣れな人の詰まりが激減しました。
|
Git に不慣れでも、コメントをコピペすれば OK な状態になってるのが地味にありがたい。
|
||||||
|
|
||||||
### ルール3: PRイベントでも同じフローでレビュー
|
## 実際の使用感
|
||||||
|
|
||||||
IssueだけでなくPRでもレビューが走ると、
|
普段の開発では、ちょっとした修正や改善は Issue を立てるだけで回ってる。人間がやるのは:
|
||||||
「修正は出たが最終反映前チェックが抜ける」問題を防げます。
|
|
||||||
|
|
||||||
## Git弱者向けテンプレ(コピペ用)
|
- Issue を書く(日本語で OK)
|
||||||
|
- 修正結果を確認する
|
||||||
|
- マージするかどうか判断する
|
||||||
|
|
||||||
### 1) 修正ブランチへ切替
|
体感として、**コードレビュー+修正の作業時間が 8 割くらい減った**。特に「このへん直して」みたいなざっくりした指示でもちゃんとコード読んで対応してくれるのがいい。
|
||||||
|
|
||||||
```bash
|
もちろん的外れなこともあるけど、その時は Issue にコメントで「そうじゃなくてこうして」って返せばまた対応してくれる。
|
||||||
git fetch origin
|
|
||||||
git switch fix/issue-123-xxx
|
|
||||||
# 古いgitなら: git checkout fix/issue-123-xxx
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2) 検証
|
## 今後やりたいこと
|
||||||
|
|
||||||
```bash
|
- `ai-review` ラベルが付いた Issue だけ処理する(全 Issue に反応するのは過剰)
|
||||||
python3 -m py_compile src/*.py app/*.py
|
- レビュー結果の品質スコアリング
|
||||||
```
|
- 失敗時の自動リトライと通知
|
||||||
|
|
||||||
### 3) mainへ反映
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git switch main
|
|
||||||
git pull origin main
|
|
||||||
git merge --no-ff fix/issue-123-xxx
|
|
||||||
git push origin main
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4) 後片付け(任意)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git branch -d fix/issue-123-xxx
|
|
||||||
git push origin --delete fix/issue-123-xxx
|
|
||||||
```
|
|
||||||
|
|
||||||
## 今後の拡張
|
|
||||||
|
|
||||||
- ラベル付きIssueのみ処理(`ai-review` など)
|
|
||||||
- PRへの自動レビューコメント整形
|
|
||||||
- 失敗時の自動再試行と通知
|
|
||||||
- レビュー結果のダッシュボード化
|
|
||||||
|
|
||||||
## まとめ
|
## まとめ
|
||||||
|
|
||||||
Gitea × OpenClaw 連携は、作るだけなら簡単です。
|
GitHub の Copilot や Devin みたいな「AI が開発を手伝ってくれる」体験を、ローカルの Gitea 環境でも再現できた。仕組み自体はシンプルで、Webhook Bridge の Python スクリプト 1 本と、OpenClaw のエージェント設定だけ。
|
||||||
でも、**壊れずに回す**には運用設計がすべてでした。
|
|
||||||
|
|
||||||
- Issue + PR イベント対応
|
ローカル AI 開発に興味がある人は、まず Issue → AI レビューの一本道から始めてみるといいと思う。それだけでも十分便利。
|
||||||
- EOF対策(例外時も必ず応答)
|
|
||||||
- ブリッジの責務分離
|
|
||||||
- ブランチ/手順テンプレの固定
|
|
||||||
|
|
||||||
この4点を押さえると、AI連携は実務で使えるレベルまで安定します。
|
## 参考リンク
|
||||||
|
|
||||||
|
- [Gitea 公式ドキュメント](https://docs.gitea.com/)
|
||||||
|
- [OpenClaw](https://github.com/openclaw/openclaw)
|
||||||
|
|
||||||
---
|
---
|
||||||
*この記事は2026年2月時点の情報です。*
|
|
||||||
|
*この記事は2026年3月時点の情報です。*
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user