Files
ewoooc/.gitea/workflows/cd.yaml
ogt 2e0de960ce
All checks were successful
CD Pipeline / deploy (push) Successful in 1m21s
feat(code-review): 重建為 Post-Deploy AI Agent Pipeline
架構重建:
- 移除 pre-commit hook(本機 commit 不再阻塞)
- 改為 CD 健康檢查通過後自動觸發 webhook

新建 services/code_review_pipeline_service.py:
  5-Step Pipeline(後台 daemon thread)
  Step1 system        讀取部署後變更檔案內容
  Step2 Hermes        程式碼掃描(bugs/security/perf,hermes3:latest)
  Step3 OpenClaw      架構品質評估(Gemini 2.5 Flash)
  Step4 ElephantAlpha 決策協調(severity + auto_fix 裁量)
  Step5 NemoTron      action_plans 寫入 + AiderHeal 觸發
  全程 Telegram 告警(啟動/完成/錯誤)+ ai_insights DB 持久化

重建 routes/code_review_routes.py:
  POST /code-review/api/internal/trigger  CD webhook(X-Internal-Token)
  GET  /code-review/api/status            前端即時 polling
  GET  /code-review/api/history           歷史清單
  GET  /code-review/                      前端儀表板

重建 templates/code_review.html:
  深色儀表板,Pipeline 即時進度 + Severity 分佈 + 問題清單 + EA 決策
  3s polling(running)/ 30s(idle)

.gitea/workflows/cd.yaml:
  健康檢查通過後注入「觸發 AI Code Review」step
  continue-on-error: true(不影響部署結果)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-21 20:55:23 +08:00

223 lines
10 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# =============================================================================
# EwoooC CD Pipeline (Gitea Actions)
# =============================================================================
# 流程: Sync Files → 188 Docker Restart → Health Check
# 部署架構: Docker Compose on ollama@192.168.0.188 (Volume Mount)
# 加速措施: Python 檔案走 rsync僅 Dockerfile/requirements 變動才重建 image
# 參考: AWOOOI cd.yaml pattern (ADR-008 — Docker Compose 非 K8s)
name: CD Pipeline
on:
push:
branches: [main]
paths:
# 應用程式碼volume-mounted
- 'app.py'
- 'auth.py'
- 'config.py'
- 'scheduler.py'
- 'services/**'
- 'routes/**'
- 'database/**'
- 'templates/**'
- 'static/**'
# 需重建 image 的檔案
- 'Dockerfile'
- 'requirements.txt'
- 'docker-compose.yml'
# 工作流程本身
- '.gitea/workflows/**'
# docs/、memory/、ADR、k8s/ 等不觸發
workflow_dispatch:
# 手動觸發永遠可用(用於補跑、緊急部署)
# 新 push 立即取消舊 job只部署最新版本
concurrency:
group: cd-deploy-${{ github.ref }}
cancel-in-progress: true
jobs:
deploy:
timeout-minutes: 20
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 2
- name: 取得 Commit 資訊
id: commit
run: |
echo "short_sha=${GITHUB_SHA::7}" >> $GITHUB_OUTPUT
echo "message=$(git log -1 --pretty=%s | head -c 60)" >> $GITHUB_OUTPUT
echo "start_time=$(date +%s)" >> $GITHUB_OUTPUT
echo "actor=${{ github.actor }}" >> $GITHUB_OUTPUT
# 偵測是否需重建 Docker imageDockerfile / requirements.txt / docker-compose.yml 變動)
- name: 偵測部署類型
id: deploy_type
run: |
CHANGED=$(git diff --name-only HEAD~1 HEAD 2>/dev/null || echo "")
if echo "$CHANGED" | grep -qE '^(Dockerfile|requirements\.txt|docker-compose\.yml)$'; then
echo "type=rebuild" >> $GITHUB_OUTPUT
echo "label=🔨 重建 Docker Image" >> $GITHUB_OUTPUT
else
echo "type=sync" >> $GITHUB_OUTPUT
echo "label=📁 同步 Python 檔案" >> $GITHUB_OUTPUT
fi
# 設定 SSH 金鑰(用於連線到 188
- name: 設定 SSH 金鑰
run: |
mkdir -p ~/.ssh
echo "${{ secrets.DEPLOY_SSH_KEY }}" > ~/.ssh/id_deploy
chmod 600 ~/.ssh/id_deploy
cat > ~/.ssh/config << 'EOF'
Host 192.168.0.188
HostName 192.168.0.188
User ollama
IdentityFile ~/.ssh/id_deploy
StrictHostKeyChecking no
ConnectTimeout 10
EOF
- name: 通知部署開始
run: |
COMMIT_ESC=$(echo "${{ steps.commit.outputs.message }}" | sed 's/&/\&amp;/g; s/</\&lt;/g; s/>/\&gt;/g')
MSG=$(printf '🚀 <b>EwoooC 部署開始</b>\n├ 📝 <code>%s</code>\n├ 🔖 <code>%s</code>\n├ 👤 %s\n└ %s' \
"${COMMIT_ESC}" \
"${{ steps.commit.outputs.short_sha }}" \
"${{ steps.commit.outputs.actor }}" \
"${{ steps.deploy_type.outputs.label }}")
curl -fS -X POST "https://api.telegram.org/bot${{ secrets.TELEGRAM_BOT_TOKEN }}/sendMessage" \
-H "Content-Type: application/json" \
-d "$(jq -n --arg c "${{ secrets.TELEGRAM_CHAT_ID }}" --arg t "$MSG" '{chat_id:$c,text:$t,parse_mode:"HTML"}')"
# ── 安裝部署工具 ────────────────────────────────────────────────────
- name: 安裝 rsync / ssh
run: |
apt-get update -qq && apt-get install -y -qq rsync openssh-client
# ── 模式 A僅同步 Python 檔案(最常見,~10s ────────────────────────
- name: 同步 Python 檔案至 188
if: steps.deploy_type.outputs.type == 'sync'
run: |
rsync -avz \
-e "ssh -i ~/.ssh/id_deploy -o StrictHostKeyChecking=no" \
--exclude='.git/' \
--exclude='.gitea/' \
--exclude='data/' \
--exclude='logs/' \
--exclude='backups/' \
--exclude='config/google_credentials.json' \
--exclude='config/google_token.pickle' \
--exclude='venv/' \
--exclude='__pycache__/' \
--exclude='*.pyc' \
--exclude='.env' \
--exclude='*.db' \
--exclude='*.db-journal' \
--exclude='docs/' \
--exclude='memory/' \
--exclude='k8s/' \
--exclude='n8n-workflows/' \
--exclude='aiops-core/' \
./ ollama@192.168.0.188:/home/ollama/momo-pro/
- name: 重啟容器Sync 模式)
if: steps.deploy_type.outputs.type == 'sync'
run: |
ssh -i ~/.ssh/id_deploy -o StrictHostKeyChecking=no ollama@192.168.0.188 \
"docker restart momo-pro-system momo-scheduler momo-telegram-bot 2>&1 && \
echo '✅ 三容器已重啟app/scheduler/telegram-bot'"
# ── 模式 B重建 Docker ImageDockerfile / requirements.txt 變動) ──
- name: 同步所有檔案並重建 Image
if: steps.deploy_type.outputs.type == 'rebuild'
run: |
# 先同步全部檔案
rsync -avz \
-e "ssh -i ~/.ssh/id_deploy -o StrictHostKeyChecking=no" \
--exclude='.git/' \
--exclude='data/' \
--exclude='logs/' \
--exclude='backups/' \
--exclude='config/google_credentials.json' \
--exclude='config/google_token.pickle' \
--exclude='venv/' \
--exclude='__pycache__/' \
--exclude='*.pyc' \
--exclude='.env' \
--exclude='*.db' \
./ ollama@192.168.0.188:/home/ollama/momo-pro/
# 重建並重啟(先強制移除舊容器,避免 container name conflict
ssh -i ~/.ssh/id_deploy -o StrictHostKeyChecking=no ollama@192.168.0.188 \
"cd /home/ollama/momo-pro && \
docker stop momo-pro-system momo-scheduler momo-telegram-bot 2>/dev/null; \
docker rm momo-pro-system momo-scheduler momo-telegram-bot 2>/dev/null; \
docker compose build momo-app && \
docker compose up -d --no-deps momo-app scheduler telegram-bot && \
echo '✅ Image 重建完成(三容器)'"
# ── 健康檢查(最多重試 5 次,每次間隔 10s ───────────────────────────
- name: 健康檢查
run: |
echo "⏳ 等待服務啟動15s..."
sleep 15
for i in $(seq 1 5); do
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" https://mo.wooo.work/health --max-time 10 || echo "000")
if [ "$HTTP_CODE" = "200" ]; then
echo "✅ 健康檢查通過HTTP $HTTP_CODE"
exit 0
fi
echo "⏳ 嘗試 $i/5HTTP $HTTP_CODE等待 10s..."
sleep 10
done
echo "❌ 健康檢查失敗"
exit 1
# ── 觸發 Post-Deploy Code Review ─────────────────────────────────────
- name: 觸發 AI Code Review
if: success()
continue-on-error: true
run: |
CHANGED=$(git diff --name-only HEAD~1 HEAD 2>/dev/null || echo "")
FILES_JSON=$(echo "$CHANGED" | grep -E '\.(py|yaml|yml|json)$' | \
jq -Rs '[split("\n")[] | select(. != "")]')
curl -fS --max-time 10 \
-X POST "https://mo.wooo.work/code-review/api/internal/trigger" \
-H "Content-Type: application/json" \
-H "X-Internal-Token: ${{ secrets.INTERNAL_WEBHOOK_TOKEN }}" \
-d "{\"commit_sha\":\"${{ github.sha }}\",\"changed_files\":${FILES_JSON},\"branch\":\"${{ github.ref_name }}\",\"deploy_type\":\"${{ steps.deploy_type.outputs.type }}\"}" \
&& echo "✅ Code Review Pipeline 已觸發" \
|| echo "⚠️ Code Review webhook 呼叫失敗(不影響部署結果)"
# ── 部署成功通知 ──────────────────────────────────────────────────────
- name: 通知部署成功
if: success()
run: |
END_TIME=$(date +%s)
DURATION=$((END_TIME - ${{ steps.commit.outputs.start_time }}))
COMMIT_ESC=$(echo "${{ steps.commit.outputs.message }}" | sed 's/&/\&amp;/g; s/</\&lt;/g; s/>/\&gt;/g')
MSG=$(printf '✅ <b>EwoooC 部署成功</b>\n├ 📝 <code>%s</code>\n├ 🔖 <code>%s</code>\n├ ⏱ 耗時 %ss\n└ 🌐 https://mo.wooo.work' \
"${COMMIT_ESC}" \
"${{ steps.commit.outputs.short_sha }}" \
"${DURATION}")
curl -fS -X POST "https://api.telegram.org/bot${{ secrets.TELEGRAM_BOT_TOKEN }}/sendMessage" \
-H "Content-Type: application/json" \
-d "$(jq -n --arg c "${{ secrets.TELEGRAM_CHAT_ID }}" --arg t "$MSG" '{chat_id:$c,text:$t,parse_mode:"HTML"}')"
# ── 部署失敗通知 ──────────────────────────────────────────────────────
- name: 通知部署失敗
if: failure()
run: |
COMMIT_ESC=$(echo "${{ steps.commit.outputs.message }}" | sed 's/&/\&amp;/g; s/</\&lt;/g; s/>/\&gt;/g')
MSG=$(printf '❌ <b>EwoooC 部署失敗</b>\n├ 📝 <code>%s</code>\n├ 🔖 <code>%s</code>\n└ 🔍 請查看 Gitea Actions 日誌' \
"${COMMIT_ESC}" \
"${{ steps.commit.outputs.short_sha }}")
curl -fS -X POST "https://api.telegram.org/bot${{ secrets.TELEGRAM_BOT_TOKEN }}/sendMessage" \
-H "Content-Type: application/json" \
-d "$(jq -n --arg c "${{ secrets.TELEGRAM_CHAT_ID }}" --arg t "$MSG" '{chat_id:$c,text:$t,parse_mode:"HTML"}')"