Files
ewoooc/.gitea/workflows/cd.yaml
ogt 8d0b79cd00
All checks were successful
CD Pipeline / deploy (push) Successful in 1m19s
feat(ops): restore Telegram chain + P2/P3 price decisions + ADR-011
P2 (Inline Keyboard 降價決策):
- routes/bot_api_routes.py: POST /bot/api/price-decision/notify
- services/telegram_bot_service.py: pa:/pr: callback handlers

P3 (OpenClaw 自動觸發):
- services/openclaw_strategist_service.py: Gemini 週報末尾輸出
  PRICE_DECISIONS_JSON,解析後自動推送 inline keyboard 給 admin

Ops 修復(跨專案隔離與容器斷訊根因):
- ADR-011 全面規範多專案共存邊界、禁用 --remove-orphans
- .gitea/workflows/cd.yaml: sync 模式一次重啟三容器
  (原本僅 momo-pro-system,scheduler/telegram-bot 靜默落伍)
- run_telegram_bot.py: 從 scripts/tools/ 複製到根目錄
  (消滅 docker-compose mount 建空目錄的陷阱)
- CLAUDE.md: 補核心容器表、診斷黃金三句、緊急指令

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

202 lines
8.8 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/
# 重建並重啟
ssh -i ~/.ssh/id_deploy -o StrictHostKeyChecking=no ollama@192.168.0.188 \
"cd /home/ollama/momo-pro && docker compose build momo-app && docker compose up -d --force-recreate momo-app && 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
# ── 部署成功通知 ──────────────────────────────────────────────────────
- 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"}')"