This commit is contained in:
6
app.py
6
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 系統
|
||||
|
||||
@@ -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 # 用於模板顯示
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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` 改成「執行環境」與「視覺檢查」,並更新測試防回歸。
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -84,7 +84,7 @@
|
||||
<div>
|
||||
<div class="host-lane-top">
|
||||
<span class="host-name">{{ h.label }}</span>
|
||||
{% if h.healthy %}<span class="badge bg-success">Runtime 正常</span>{% else %}<span class="badge bg-danger">異常</span>{% endif %}
|
||||
{% if h.healthy %}<span class="badge bg-success">執行環境正常</span>{% else %}<span class="badge bg-danger">異常</span>{% endif %}
|
||||
</div>
|
||||
<div class="host-url"><code>{{ h.host }}</code></div>
|
||||
{% if h.error %}<div class="text-danger small mt-1">{{ h.error }}</div>{% endif %}
|
||||
|
||||
@@ -109,7 +109,7 @@
|
||||
<section class="ppt-vision-status is-{{ vision_audit_status.status }}" data-ppt-vision-status aria-live="polite">
|
||||
<div class="ppt-vision-status-main">
|
||||
<span class="ppt-run-status is-{{ 'ready' if vision_audit_status.status == 'completed' else 'planned' if vision_audit_status.status in ['idle', 'queued', 'running'] else 'error' }}">
|
||||
<i class="fas fa-eye me-1" aria-hidden="true"></i>Vision QA
|
||||
<i class="fas fa-eye me-1" aria-hidden="true"></i>視覺檢查
|
||||
</span>
|
||||
<div>
|
||||
<strong data-ppt-vision-status-title>{{ vision_audit_status.status_label }}</strong>
|
||||
@@ -118,7 +118,7 @@
|
||||
</div>
|
||||
<div class="ppt-vision-status-list" data-ppt-vision-status-list>
|
||||
<div class="ppt-vision-job is-runtime">
|
||||
<span>Runtime</span>
|
||||
<span>執行環境</span>
|
||||
<strong>{{ vision_status.status_label }}</strong>
|
||||
<small>{{ vision_status.model }} · {{ vision_status.converter or '無轉檔器' }}</small>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{% extends "ewoooc_base.html" %}
|
||||
|
||||
{% block title %}CI/CD Dashboard - EwoooC{% endblock %}
|
||||
{% block title %}部署監控 - EwoooC{% endblock %}
|
||||
|
||||
{% block extra_head %}
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.0/font/bootstrap-icons.css" rel="stylesheet">
|
||||
@@ -473,8 +473,8 @@
|
||||
<div class="container">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<h1><i class="bi bi-rocket-takeoff me-2"></i>CI/CD Dashboard</h1>
|
||||
<p class="mb-0 opacity-75">MOMO Pro System - 持續整合與部署監控</p>
|
||||
<h1><i class="bi bi-rocket-takeoff me-2"></i>部署監控</h1>
|
||||
<p class="mb-0 opacity-75">PChome 業績成長自動化作戰系統 · 持續整合與部署監控</p>
|
||||
</div>
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
<div class="realtime-badge">
|
||||
@@ -527,7 +527,7 @@
|
||||
<!-- Pipeline Flow -->
|
||||
<div class="card mb-4">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<span><i class="bi bi-diagram-3 me-2"></i>最新 Pipeline</span>
|
||||
<span><i class="bi bi-diagram-3 me-2"></i>最新部署流程</span>
|
||||
<span id="pipelineId" class="badge bg-secondary">#--</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
@@ -589,7 +589,7 @@
|
||||
<div class="row g-3">
|
||||
<div class="col-12">
|
||||
<a href="http://192.168.0.110:8929/root/momo-pro-system/-/pipelines" target="_blank" class="btn btn-outline-light w-100">
|
||||
<i class="bi bi-box-arrow-up-right me-2"></i>開啟 GitLab Pipelines
|
||||
<i class="bi bi-box-arrow-up-right me-2"></i>開啟 GitLab 部署紀錄
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
@@ -716,7 +716,7 @@
|
||||
<div class="issue-content">
|
||||
<div class="issue-title">${escapeHtml(issue.message)}</div>
|
||||
<div class="issue-detail">
|
||||
${issue.type === 'job' ? `<span class="badge bg-secondary me-1">${issue.stage}</span>` : ''}
|
||||
${issue.type === 'job' ? `<span class="badge bg-secondary me-1">${displayStageName(issue.stage)}</span>` : ''}
|
||||
${issue.type === 'runtime' ? `<span class="badge bg-info me-1">${issue.environment?.toUpperCase()}</span>` : ''}
|
||||
${issue.error ? `<br><code>${escapeHtml(issue.error.substring(0, 100))}</code>` : ''}
|
||||
</div>
|
||||
@@ -758,13 +758,13 @@
|
||||
}
|
||||
}
|
||||
|
||||
// 更新 Pipeline 流程圖
|
||||
// 更新部署流程圖
|
||||
function updatePipelineFlow(latestPipeline, latestJobs) {
|
||||
const container = document.getElementById('pipelineFlow');
|
||||
const pipelineIdEl = document.getElementById('pipelineId');
|
||||
|
||||
if (!latestPipeline) {
|
||||
container.innerHTML = '<p class="text-center text-muted">暫無 Pipeline 數據</p>';
|
||||
container.innerHTML = '<p class="text-center text-muted">暫無部署流程資料</p>';
|
||||
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 @@
|
||||
<div class="pipeline-stage">
|
||||
<div class="stage-box ${stage.status}" title="${escapeHtml(errorMsg)}">
|
||||
<span class="stage-icon">${getStatusIcon(stage.status)}</span>
|
||||
<span class="stage-name">${stage.name}</span>
|
||||
<span class="stage-name">${displayStageName(stage.name)}</span>
|
||||
</div>
|
||||
<span class="stage-duration">${formatDuration(stage.duration)}</span>
|
||||
${stage.status === 'failed' ? `
|
||||
@@ -859,7 +859,7 @@
|
||||
container.innerHTML = html;
|
||||
}
|
||||
|
||||
// 渲染 Pipeline 流程圖
|
||||
// 渲染部署流程圖
|
||||
function renderPipelineFlow(container, stages) {
|
||||
let html = '';
|
||||
|
||||
@@ -872,7 +872,7 @@
|
||||
<div class="pipeline-stage">
|
||||
<div class="stage-box ${stage.status}">
|
||||
<span class="stage-icon">${stage.status_icon}</span>
|
||||
<span class="stage-name">${stage.name}</span>
|
||||
<span class="stage-name">${displayStageName(stage.name)}</span>
|
||||
</div>
|
||||
<span class="stage-duration">${formatDuration(stage.duration)}</span>
|
||||
</div>
|
||||
@@ -882,7 +882,7 @@
|
||||
container.innerHTML = html;
|
||||
}
|
||||
|
||||
// 簡易 Pipeline 流程圖
|
||||
// 簡易部署流程圖
|
||||
function renderSimplePipelineFlow(container, pipeline) {
|
||||
const stages = ['test', 'build', 'deploy'];
|
||||
let html = '';
|
||||
@@ -901,7 +901,7 @@
|
||||
<div class="pipeline-stage">
|
||||
<div class="stage-box ${status}">
|
||||
<span class="stage-icon">${getStatusIcon(status)}</span>
|
||||
<span class="stage-name">${stage}</span>
|
||||
<span class="stage-name">${displayStageName(stage)}</span>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
@@ -949,7 +949,7 @@
|
||||
` : ''}
|
||||
|
||||
<div class="env-details mt-2">
|
||||
<strong class="d-block mb-2">Runtime 狀態:</strong>
|
||||
<strong class="d-block mb-2">執行環境狀態:</strong>
|
||||
${renderPods(env.pods, envId)}
|
||||
</div>
|
||||
</div>
|
||||
@@ -963,7 +963,7 @@
|
||||
// 渲染 runtime 狀態
|
||||
function renderPods(pods, envId) {
|
||||
if (!pods || pods.length === 0) {
|
||||
return '<p class="text-muted small mb-0">Docker Compose runtime;舊叢集資訊不適用</p>';
|
||||
return '<p class="text-muted small mb-0">Docker Compose 執行環境;舊叢集資訊不適用</p>';
|
||||
}
|
||||
|
||||
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 = '<p class="text-center text-muted">暫無 Pipeline 記錄</p>';
|
||||
container.innerHTML = '<p class="text-center text-muted">暫無部署流程紀錄</p>';
|
||||
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': '✅',
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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():
|
||||
"<b>Branch</b>",
|
||||
"${f.severity}</span>",
|
||||
"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
|
||||
|
||||
Reference in New Issue
Block a user