Files
ewoooc/.gitea/workflows/cd.yaml
ogt 0099543c05
Some checks failed
CD Pipeline / deploy (push) Failing after 5m18s
fix(security): 全域健檢 — 40 項安全/Bug/品質修復
🔴 Critical
- auto_heal_service: 補 import re + sqlalchemy.text + 修正 orchestrator 變數名
  + autoheal_playbook→playbooks 表名 + _alert_and_store cooldown 修復
- aider_heal_executor: shell injection 改 shell=False + list 參數
- docker-compose: DISABLE_LOGIN 改 env var + 移除密碼 fallback + POSTGRES_HOST 修正
- app.py: /api/backup /api/run_task 等 6 個管理 API 加 @login_required
- config.py + pg_sync + e2e_test: 移除 wooo_pg_2026 hardcoded 密碼 fallback
- pg_backup.sh: 移除 TELEGRAM_TOKEN= 中間變數,直接用 $TELEGRAM_BOT_TOKEN
- migration 014: trigger_pattern→match_pattern + 補 error_type NOT NULL 欄位

🟡 High
- telegram_bot_service: str(e) 改通用訊息 + session try/finally + 移除 pa:/pr: 舊 callback
- run_scheduler: ElephantAlpha thread 死亡監控 + 自動重啟 + Telegram 告警
  + agent_context 03:30 TTL 定時清理任務
- openclaw_learning_service: build_rag_context 兩路徑加 .limit(200)
- hooks: commit-quality + momo-prod-guard 空 catch 改 stderr+exit(1)
- scripts/code_review: auto_yes 預設改 false
- db_backup_service: PGPASSWORD 透過 env dict 傳遞

📦 Migrations
- 013_autoheal: 修正建表順序 playbooks→incidents(外鍵前向引用)
- 018_add_missing_indexes: heal_logs/incidents 外鍵索引 + cleanup_expired_agent_context()

🟢 Infrastructure
- requirements.txt: 加版本下界 Flask>=2.3 SQLAlchemy>=1.4 等
- cd.yaml: 新增 run_scheduler.py + run_telegram_bot.py 監聽路徑
- .gitignore: insert_playbook_local.py 加入忽略

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-22 01:12:23 +08:00

225 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'
- 'run_scheduler.py'
- 'run_telegram_bot.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"}')"