diff --git a/app.py b/app.py index 61fafd9..0c80c38 100644 --- a/app.py +++ b/app.py @@ -230,12 +230,12 @@ csrf.exempt(ai_bp) # ICAIM API 使用內部呼叫,不需要 CSRF sys_log.info("[Blueprint] ✅ AI 智慧文案系統 Blueprint 已註冊") # ========================================== -# 🔧 Blueprint 註冊 - CI/CD Dashboard +# 🔧 Blueprint 註冊 - 部署監控 # ========================================== from routes.cicd_routes import cicd_bp app.register_blueprint(cicd_bp) -csrf.exempt(cicd_bp) # CI/CD API doesn't need CSRF -sys_log.info("[Blueprint] CI/CD Dashboard Blueprint registered") +csrf.exempt(cicd_bp) # 部署監控 API 使用內部呼叫,不需要 CSRF +sys_log.info("[Blueprint] ✅ 部署監控 Blueprint 已註冊") # ========================================== # 🔧 Blueprint 註冊 - Code Review 系統 diff --git a/config.py b/config.py index 0ce4e1d..3edee47 100644 --- a/config.py +++ b/config.py @@ -402,7 +402,7 @@ YOUTUBE_API_KEY = os.getenv('YOUTUBE_API_KEY', '') # ========================================== # 系統版本與路徑 # ========================================== -SYSTEM_VERSION = "V10.613" +SYSTEM_VERSION = "V10.614" LOG_FILE_PATH = os.path.join(BASE_DIR, 'logs/system.log') public_url = PUBLIC_URL # 用於模板顯示 diff --git a/docs/AI_INTELLIGENCE_MODULE_SOT.md b/docs/AI_INTELLIGENCE_MODULE_SOT.md index 696142f..5c566ed 100644 --- a/docs/AI_INTELLIGENCE_MODULE_SOT.md +++ b/docs/AI_INTELLIGENCE_MODULE_SOT.md @@ -2,7 +2,7 @@ > **最後更新**: 2026-06-16 (台北時間) > **狀態**: 🟢 四 AI Agent 自動化閉環已落地;LLM 路由紅線升級為 Ollama-first 三主機級聯;PChome 後台業績匯入韌性已補強;產品定位正名為「PChome 業績成長自動化作戰系統」;外部市場來源正規化層、自動同步、作戰清單與價格參考表優先讀取、CSV 備援預檢、前台操作入口與高可見頁面繁中化守門已建立 -> **適用版本**: V10.613 +> **適用版本**: V10.614 --- @@ -60,6 +60,7 @@ - V10.611 起 `/ai_intelligence` 是營運使用者主入口:頁首提供「今日作戰入口」,依序連到 PChome 成長作戰、補商品對應、MOMO 外部價格參考與外部報價預檢;作戰清單左側會直接顯示今日優先動作與資料來源摘要。 - V10.612 起 `/api/ai/icaim/dashboard` 的「MOMO 外部價格參考」表格也優先讀 `external_offers`,缺資料才 fallback `competitor_prices`;價差與風險改採 PChome 視角,正數代表 PChome 比 MOMO 外部參考價高。 - V10.613 起高可見前台頁面必須以繁體中文呈現:程式碼審查、AI 自動化健康檢查、PPT 產線與商品看板操作標籤不得使用英文工程標題或簡體字;測試需防止頁面文案退回英文。 +- V10.614 起部署監控、基礎設施生命線與 PPT 產線狀態也納入繁中守門:前台不得顯示 `Dashboard`、`Pipeline`、`Runtime` 等工程詞,動態階段需轉成「測試 / 建置 / 部署」。 ## 零之一、12 Agent 決策信封(2026-05-24) diff --git a/docs/memory/current_execution_queue_20260524.md b/docs/memory/current_execution_queue_20260524.md index 7c54e28..f6a0155 100644 --- a/docs/memory/current_execution_queue_20260524.md +++ b/docs/memory/current_execution_queue_20260524.md @@ -233,3 +233,9 @@ - 已把 `/code-review/` 顯示文字改成「AI 程式碼審查」「流程進度」「程式碼審查完成」,並將 OpenClaw 模型顯示改成「Ollama 優先」,避免前台誤以為 Gemini 是主路徑。 - 已把 AI 自動化健康檢查頁改為白話繁中命名,狀態顯示改成「正常 / 注意 / 嚴重 / 產生時間」。 - 已把 PPT 觀測台與商品看板高可見英文標籤改成「產線健康度 / 工作隊列 / 視覺問題 / 產線控制台 / 覆蓋率流程」,並新增測試防回歸。 + +## 17. 2026-06-16 V10.614 部署與基礎設施頁繁中化 + +- `/cicd` 對使用者顯示為「部署監控」,不再以前台標題顯示 `CI/CD Dashboard`;部署流程、部署歷史、GitLab 部署紀錄皆使用白話繁中。 +- 部署流程圖會把後端階段代碼 `test / build / deploy` 轉成「測試 / 建置 / 部署」,診斷狀態也轉成「正常 / 注意 / 失敗」。 +- `/observability/host_health` 與 PPT 產線視覺狀態把 `Runtime` / `Vision QA` 改成「執行環境」與「視覺檢查」,並更新測試防回歸。 diff --git a/routes/admin_observability_routes.py b/routes/admin_observability_routes.py index c227b56..7a5df8d 100644 --- a/routes/admin_observability_routes.py +++ b/routes/admin_observability_routes.py @@ -988,7 +988,7 @@ def agent_orchestration_dashboard(): recommendations.append({ 'severity': 'high', 'agent': ag['label'], 'finding': f"錯誤率 {ag['error_rate']:.1f}%({ag['errors']}/{ag['calls']})", - 'suggestion': '觸發 Code Review Pipeline 找 regression(ai_calls 觀測台一鍵)', + 'suggestion': '觸發程式碼審查流程找回歸問題(ai_calls 觀測台一鍵)', }) # 規則 3:MCP 編排率 < 5% 但 calls 多 → 建議擴大 MCP 使用 if mcp_calls_table_exists and ag['mcp_rate'] < 5 and ag['calls'] > 50: @@ -1826,11 +1826,11 @@ def budget_dashboard(): @admin_observability_bp.route('/ai_calls/trigger_code_review', methods=['POST']) @login_required def ai_calls_trigger_code_review(): - """Phase 40 D-7 (L2 自動化):對高錯誤率時段觸發 Code Review Pipeline。 + """Phase 40 D-7 (L2 自動化):對高錯誤率時段觸發程式碼審查流程。 用途:admin 在觀測台看到某 caller 錯誤率飆高時,一鍵觸發 5-step pipeline (read→hermes_scan→openclaw_summary→ea_decision→nemoton_act) - 在 daemon thread 自動審查最近 commit 變更檔案,找出可能的 regression。 + 在 daemon thread 自動審查最近 commit 變更檔案,找出可能的回歸問題。 """ try: import subprocess @@ -1862,8 +1862,8 @@ def ai_calls_trigger_code_review(): 'pipeline_id': pipeline.pipeline_id, 'commit_sha': commit_sha[:8], 'changed_files_count': len(changed), - 'message': f'已觸發 Code Review (pipeline_id={pipeline.pipeline_id}) 在背景執行,' - f'5 step 完成後會推 Telegram 通知。', + 'message': f'已觸發程式碼審查流程(流程編號:{pipeline.pipeline_id})在背景執行,' + f'5 個步驟完成後會推 Telegram 通知。', }) except Exception as e: return jsonify({'ok': False, 'error': f'{type(e).__name__}: {str(e)[:200]}'}), 500 diff --git a/routes/cicd_routes.py b/routes/cicd_routes.py index d455530..16a8f3f 100644 --- a/routes/cicd_routes.py +++ b/routes/cicd_routes.py @@ -1,6 +1,6 @@ # ============================================================================= # WOOO TECH - Momo Pro System -# CI/CD Dashboard Routes +# 部署監控路由 # ============================================================================= from flask import Blueprint, jsonify, render_template, request @@ -119,12 +119,12 @@ ENVIRONMENTS = { } # ============================================================================= -# Dashboard 頁面 +# 部署監控頁面 # ============================================================================= @cicd_bp.route('/cicd') def cicd_dashboard(): - """CI/CD Dashboard 主頁面""" + """部署監控主頁面""" return render_template('cicd_dashboard.html', active_page='cicd') # ============================================================================= @@ -140,14 +140,14 @@ def get_cicd_status(): latest_pipeline = pipelines[0] if pipelines else None environments = get_all_environments_status() - # 取得最新 Pipeline 的詳細 Job 信息 + # 取得最新部署流程的詳細工作項目資訊 latest_jobs = [] failed_jobs = [] if latest_pipeline: latest_jobs = get_pipeline_jobs(latest_pipeline['id']) failed_jobs = [j for j in latest_jobs if j.get('status') == 'failed'] - # 如果最新 Pipeline 失敗且有未通知的失敗 Job,發送告警 + # 如果最新部署流程失敗且有未通知的失敗工作項目,發送告警 if latest_pipeline.get('status') == 'failed' and failed_jobs: # 使用緩存避免重複通知 cache_key = f"pipeline_alert_{latest_pipeline['id']}" @@ -453,7 +453,7 @@ def run_diagnosis(env): # EwoooC 已撤除舊叢集 runtime,這裡只保留現行 Docker Compose 狀態說明。 diagnosis['checks'].append({ - 'name': 'Runtime 狀態', + 'name': '執行環境狀態', 'status': 'ok', 'runtime': 'Docker Compose on 192.168.0.188', 'details': '舊叢集探測已停用;容器狀態請依 DevOps 手冊在 188 查 docker compose / /health。' @@ -554,19 +554,19 @@ def send_telegram_message(message): def send_pipeline_failure_alert(pipeline, failed_jobs): - """Pipeline 失敗時發送告警""" + """部署流程失敗時發送告警""" job_details = '\n'.join([ f" • {j['name']}: {j.get('failure_reason', '未知原因')}" for j in failed_jobs ]) - message = f"""🚨 *CI/CD Pipeline 失敗* + message = f"""🚨 *部署流程失敗* -📌 *Pipeline:* #{pipeline.get('id')} +📌 *部署編號:* #{pipeline.get('id')} 🌿 *分支:* `{pipeline.get('ref')}` -📝 *Commit:* `{pipeline.get('sha', '')[:8]}` +📝 *提交:* `{pipeline.get('sha', '')[:8]}` -❌ *失敗 Jobs:* +❌ *失敗工作項目:* {job_details} 🔗 [查看詳情]({pipeline.get('web_url')}) @@ -589,7 +589,7 @@ def send_fix_notification(env, action, results): for r in results ]) - message = f"""{status_emoji} *CI/CD 自動修復執行完成* + message = f"""{status_emoji} *部署監控自動修復執行完成* {env_icon} *環境:* {env_name} 🔧 *動作:* {action} diff --git a/routes/openclaw_bot_routes.py b/routes/openclaw_bot_routes.py index e0998f4..c910e58 100644 --- a/routes/openclaw_bot_routes.py +++ b/routes/openclaw_bot_routes.py @@ -8601,7 +8601,7 @@ def handle_cmd(cmd, arg, chat_id, reply_to): send_message(chat_id, f"❌ 查詢商業面失敗:{e}", reply_to, parse_mode=None) elif cmd == 'obs_trigger_review': - # Phase 44 (L2):Telegram inline 觸發 Code Review Pipeline + # Phase 44 (L2):Telegram inline 觸發程式碼審查流程 try: import subprocess import threading @@ -8616,7 +8616,7 @@ def handle_cmd(cmd, arg, chat_id, reply_to): ).decode().strip().split('\n') changed = [f for f in changed if f] if not changed: - send_message(chat_id, "⚠️ 最新 commit 無變更檔案,無需 Code Review", reply_to, parse_mode=None) + send_message(chat_id, "⚠️ 最新提交沒有變更檔案,無需程式碼審查", reply_to, parse_mode=None) return pipeline = CodeReviewPipeline( @@ -8627,15 +8627,15 @@ def handle_cmd(cmd, arg, chat_id, reply_to): ) threading.Thread(target=pipeline.run, daemon=True).start() ack = ( - f"🔬 Code Review Pipeline 已派出\n\n" - f"Pipeline ID: {pipeline.pipeline_id}\n" - f"Commit: {commit_sha[:8]}\n" + f"🔬 程式碼審查流程已派出\n\n" + f"流程編號: {pipeline.pipeline_id}\n" + f"提交: {commit_sha[:8]}\n" f"變更檔案: {len(changed)} 個\n\n" - f"5 step 完成後會推 Telegram 通知。" + f"5 個步驟完成後會推 Telegram 通知。" ) send_message(chat_id, ack, reply_to, parse_mode=None) except Exception as e: - send_message(chat_id, f"❌ Code Review 觸發失敗:{e}", reply_to, parse_mode=None) + send_message(chat_id, f"❌ 程式碼審查觸發失敗:{e}", reply_to, parse_mode=None) elif cmd == 'obs_force_throttle': # Phase 41 E-3 (L2):Telegram inline 觸發立即重算 cost throttle diff --git a/templates/admin/host_health.html b/templates/admin/host_health.html index 8d1dd43..9e9fa5e 100644 --- a/templates/admin/host_health.html +++ b/templates/admin/host_health.html @@ -84,7 +84,7 @@
{{ h.host }}MOMO Pro System - 持續整合與部署監控
+PChome 業績成長自動化作戰系統 · 持續整合與部署監控
${escapeHtml(issue.error.substring(0, 100))}` : ''}
暫無 Pipeline 數據
'; + container.innerHTML = '暫無部署流程資料
'; return; } @@ -775,7 +775,7 @@ const stages = groupJobsByStage(latestJobs); renderPipelineFlowWithJobs(container, stages); } else { - // 否則取得 Pipeline 的 Jobs + // 否則取得部署流程的工作項目 fetch(`/api/cicd/pipeline/${latestPipeline.id}`) .then(res => res.json()) .then(data => { @@ -784,13 +784,13 @@ } }) .catch(err => { - console.error('Failed to load pipeline details:', err); + console.error('部署流程細節讀取失敗:', err); renderSimplePipelineFlow(container, latestPipeline); }); } } - // 將 Jobs 按 Stage 分組 + // 將工作項目按階段分組 function groupJobsByStage(jobs) { const stageOrder = ['test', 'build', 'deploy']; const stages = {}; @@ -828,7 +828,7 @@ return stageOrder.map(name => stages[name]).filter(s => s); } - // 渲染 Pipeline 流程圖(帶 Job 詳情) + // 渲染部署流程圖(帶工作詳情) function renderPipelineFlowWithJobs(container, stages) { let html = ''; @@ -844,7 +844,7 @@Docker Compose runtime;舊叢集資訊不適用
'; + return 'Docker Compose 執行環境;舊叢集資訊不適用
'; } return pods.map(pod => ` @@ -978,12 +978,12 @@ `).join(''); } - // 更新 Pipeline 歷史 + // 更新部署歷史 function updatePipelineHistory(pipelines) { const container = document.getElementById('pipelineHistory'); if (!pipelines || pipelines.length === 0) { - container.innerHTML = '暫無 Pipeline 記錄
'; + container.innerHTML = '暫無部署流程紀錄
'; return; } @@ -1106,7 +1106,7 @@ return; } - if (!confirm(`確定要對 ${env.toUpperCase()} 執行完整修復嗎?\n這會重啟 Registry 並執行 runtime 診斷,不會重啟舊叢集。`)) return; + if (!confirm(`確定要對 ${env.toUpperCase()} 執行完整修復嗎?\n這會重啟 Registry 並執行執行環境診斷,不會重啟舊叢集。`)) return; showNotification('執行中', '正在執行完整修復...'); @@ -1151,12 +1151,12 @@ if (data.success) { const diagnosis = data.diagnosis; - let message = `診斷結果: ${diagnosis.summary?.overall_status?.toUpperCase()}\n`; + let message = `診斷結果: ${displayStatusText(diagnosis.summary?.overall_status)}\n`; message += `失敗: ${diagnosis.summary?.failed_count || 0}, 警告: ${diagnosis.summary?.warning_count || 0}\n\n`; diagnosis.checks?.forEach(check => { const icon = check.status === 'ok' ? '✅' : (check.status === 'warning' ? '⚠️' : '❌'); - message += `${icon} ${check.name}: ${check.status}\n`; + message += `${icon} ${check.name}: ${displayStatusText(check.status)}\n`; }); if (diagnosis.summary?.recommendations?.length > 0) { @@ -1184,6 +1184,30 @@ } // 輔助函數 + function displayStageName(stage) { + const labels = { + test: '測試', + build: '建置', + deploy: '部署', + unknown: '未分類', + }; + return labels[stage] || stage || '未分類'; + } + + function displayStatusText(status) { + const labels = { + ok: '正常', + warning: '注意', + failed: '失敗', + success: '成功', + running: '執行中', + pending: '等待中', + canceled: '已取消', + completed: '已完成', + }; + return labels[status] || status || '未知'; + } + function getStatusIcon(status) { const icons = { 'success': '✅', 'passed': '✅', diff --git a/tests/test_admin_observability_routes.py b/tests/test_admin_observability_routes.py index 40609e0..8f8edd2 100644 --- a/tests/test_admin_observability_routes.py +++ b/tests/test_admin_observability_routes.py @@ -990,7 +990,7 @@ def test_ppt_audit_history_renders_last_vision_status(client, monkeypatch): assert 'data-ppt-vision-status-title' in html assert '最近一次視覺 QA 已完成。' in html assert '2 份 / 1 問題' in html - assert 'Runtime' in html + assert '執行環境' in html def test_ppt_audit_trigger_aider_heal_accepts_issue_summary(client, monkeypatch): diff --git a/tests/test_cicd_legacy_cluster_disabled.py b/tests/test_cicd_legacy_cluster_disabled.py index 1274b25..7947d9e 100644 --- a/tests/test_cicd_legacy_cluster_disabled.py +++ b/tests/test_cicd_legacy_cluster_disabled.py @@ -79,7 +79,7 @@ def test_cicd_diagnosis_uses_runtime_note_without_cluster_probe(monkeypatch): diagnosis = cicd_routes.run_diagnosis("uat") - runtime_checks = [check for check in diagnosis["checks"] if check["name"] == "Runtime 狀態"] + runtime_checks = [check for check in diagnosis["checks"] if check["name"] == "執行環境狀態"] assert runtime_checks assert runtime_checks[0]["runtime"] == "Docker Compose on 192.168.0.188" diff --git a/tests/test_frontend_v2_assets.py b/tests/test_frontend_v2_assets.py index 5599b22..225803b 100644 --- a/tests/test_frontend_v2_assets.py +++ b/tests/test_frontend_v2_assets.py @@ -55,8 +55,10 @@ def test_high_visibility_pages_use_traditional_chinese_labels(): "templates/code_review.html", "templates/ai_automation_smoke.html", "templates/admin/ppt_audit_history.html", + "templates/admin/host_health.html", "templates/dashboard_v2.html", "templates/components/_navbar.html", + "templates/cicd_dashboard.html", ] combined = "\n".join((ROOT / path).read_text(encoding="utf-8") for path in page_paths) @@ -69,6 +71,10 @@ def test_high_visibility_pages_use_traditional_chinese_labels(): assert "覆蓋率流程" in combined assert "NemoTron · 派遣器" in combined assert "同步部署" in combined + assert "部署監控" in combined + assert "最新部署流程" in combined + assert "執行環境正常" in combined + assert "視覺檢查" in combined forbidden_visible_text = [ "AI Code Review", @@ -93,6 +99,15 @@ def test_high_visibility_pages_use_traditional_chinese_labels(): "Branch", "${f.severity}", "ea.priority.toUpperCase", + "CI/CD Dashboard", + ">最新 Pipeline<", + "暫無 Pipeline", + "開啟 GitLab Pipelines", + ">Runtime 狀態:<", + "Docker Compose runtime", + "Runtime 正常", + ">Runtime<", + "Vision QA", ] for marker in forbidden_visible_text: assert marker not in combined