diff --git a/TODO_NEXT_STEPS.txt b/TODO_NEXT_STEPS.txt index 5e0db7c..a9697a9 100644 --- a/TODO_NEXT_STEPS.txt +++ b/TODO_NEXT_STEPS.txt @@ -4,6 +4,7 @@ ================================================================================ 【已完成】 + - V10.442 降噪 `/cicd` 舊 GitLab 探測:沒有明確啟用 `GITLAB_ENABLED=true` 與 token 時,不再打退役的 `192.168.0.110:8929` 或 SSH fallback,正式 responsive smoke 造訪 `/cicd` 只呈現空 pipeline 狀態,不污染 app logs。 - V10.441 補 PChome matcher re-score audit 與商品看板原因標籤:新增 read-only `competitor_match_attempt_rescore_audit` / `scripts/audit_competitor_match_attempt_rescore.py`,可用最新版 matcher 重新分類既有 `competitor_match_attempts`,預設不寫 DB、不更新正式價格;商品看板同步補蘭蔻/達特醫/hoi/Saugella/Lactacyd 等 focused matcher reason 中文標籤,讓「待對比」能拆成商品線不符、款式版本不符、可回刷或仍低信心。 - V10.439 收斂外部 BI / 資料協作入口:`/metabase`、`/grist` 正式頁維持 momo-pro 內部診斷 bridge,`.env.example` 與 bi profile Grist 預設改回 `https://mo.wooo.work/grist` / `GRIST_APP_HOME_URL`,並補測試禁止 `grist.wooo.work` / `awoooi` 回流到導覽設定;外部工具頁標題字級改用新版 token 與手機 media query。 - V10.414 補市場情報 MCP fetch run readiness gate:新增 `mcp_fetch_run_readiness` read-only builder、GET/POST endpoint、UI run readiness 審核面板與 deployment readiness smoke target,在 run package 後檢查 command preview、receipt path、artifact path、節流/timeout/dry-run-first 與操作員 shell-only 邊界;API/UI 不執行 CLI、不抓外站、不寫檔、不開 DB、不掛 scheduler,只放行到人工 shell dry-run 與後續 receipt gate。 diff --git a/config.py b/config.py index 6d869a8..d3c6066 100644 --- a/config.py +++ b/config.py @@ -325,7 +325,7 @@ YOUTUBE_API_KEY = os.getenv('YOUTUBE_API_KEY', '') # ========================================== # 系統版本與路徑 # ========================================== -SYSTEM_VERSION = "V10.441" +SYSTEM_VERSION = "V10.442" 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 59b484f..bca7806 100644 --- a/docs/AI_INTELLIGENCE_MODULE_SOT.md +++ b/docs/AI_INTELLIGENCE_MODULE_SOT.md @@ -2,7 +2,7 @@ > **最後更新**: 2026-05-24 (台北時間) > **狀態**: 🟢 四 AI Agent 自動化閉環已落地;LLM 路由紅線升級為 Ollama-first 三主機級聯,Gemini 備援預設關閉 -> **適用版本**: V10.441 +> **適用版本**: V10.442 --- diff --git a/docs/memory/history_logs.md b/docs/memory/history_logs.md index 329978c..f4787ff 100644 --- a/docs/memory/history_logs.md +++ b/docs/memory/history_logs.md @@ -13,6 +13,7 @@ ## 📅 詳細更新日誌 (考古存檔) ### 2026-05-24:PChome 近門檻身份回收第二輪 +- **V10.442 CI/CD legacy GitLab 探測降噪**: `/cicd` 舊 GitLab pipeline API 預設改為 disabled,除非明確設定 `GITLAB_ENABLED=true` 並提供 `GITLAB_TOKEN`,否則不再打 `192.168.0.110:8929` 或 SSH fallback;正式 responsive smoke 造訪 `/cicd` 時只呈現可診斷空狀態,不再把已退役 GitLab endpoint 的 connection refused / permission denied 寫成錯誤噪音。 - **V10.441 PChome matcher re-score audit**: 新增 read-only `competitor_match_attempt_rescore_audit` 服務與 `scripts/audit_competitor_match_attempt_rescore.py`,可針對既有 `competitor_match_attempts` 用最新版 matcher 重新分類成 `accepted_current` / `unit_comparable_current` / `identity_veto_current` / `low_score_current`,預設不寫 DB、不更新正式價格;商品看板同步補蘭蔻/達特醫/hoi/Saugella/Lactacyd 等 focused matcher reason 中文標籤。正式抽樣中 31 筆舊 `strong_exact_spec_match` 低信心資料,最新版 matcher 可讀出 10 筆 gate pass、1 筆單位價、11 筆 hard veto、9 筆仍低信心,作為後續人工覆核與批次回刷前的安全量化。 - **V10.440 Mustela 爽身潤膚乳同款 anchor**: marketplace matcher 新增 `慕之幼爽身潤膚乳` identity anchor,並讓標題中插入「加量版」時仍可抽出同一身份詞;正式樣本 `【Mustela 慕之恬廊】慕之幼 加量版爽身潤膚乳 500mlX2入` vs `【慕之恬廊】慕之幼爽身潤膚乳(500毫升X2入)` 由 0.741 提升到 0.801,維持 `hard_veto=false`、人工 review 型態,不放寬全域門檻、不寫正式 `competitor_prices`。 - **V10.439 外部 BI / 資料協作入口收斂**: `/metabase`、`/grist` 保持在 momo-pro 內部診斷 bridge,不再出現空白頁或錯連其他專案;`.env.example` 與 bi profile 的 Grist 預設 URL 改為 `https://mo.wooo.work/grist` / `GRIST_APP_HOME_URL`,測試同步守住 `grist.wooo.work` 與 `awoooi` 不再回到 app/template/env/compose 導覽設定。外部工具頁 H1 移除 viewport font scaling,改用新版 token 與手機 media query。 diff --git a/routes/cicd_routes.py b/routes/cicd_routes.py index 8341373..d455530 100644 --- a/routes/cicd_routes.py +++ b/routes/cicd_routes.py @@ -8,10 +8,12 @@ import requests import subprocess from datetime import datetime import json +import logging import os import re cicd_bp = Blueprint('cicd', __name__) +cicd_log = logging.getLogger('cicd_routes') # ============================================================================= # 錯誤分類與修復建議 @@ -84,12 +86,14 @@ def analyze_error(text): GITLAB_URL = os.environ.get('GITLAB_URL', 'http://192.168.0.110:8929') GITLAB_TOKEN = os.environ.get('GITLAB_TOKEN', '') GITLAB_PROJECT_ID = os.environ.get('GITLAB_PROJECT_ID', '1') +GITLAB_ENABLED = ( + os.environ.get('GITLAB_ENABLED', 'false').lower() in {'1', 'true', 'yes', 'on'} + and bool(GITLAB_TOKEN) +) -if not GITLAB_TOKEN: - import logging - logging.getLogger('cicd_routes').warning( - '[SECURITY] GITLAB_TOKEN is not set. GitLab API calls will fail. ' - 'Set the GITLAB_TOKEN environment variable.' +if not GITLAB_ENABLED: + cicd_log.info( + '[CI/CD] GitLab legacy API disabled; set GITLAB_ENABLED=true and GITLAB_TOKEN to query legacy pipelines.' ) # 環境配置 @@ -604,6 +608,9 @@ def send_fix_notification(env, action, results): def gitlab_api(endpoint, method='GET', data=None): """呼叫 GitLab API(支援 SSH 備用方案)""" + if not GITLAB_ENABLED: + return None + url = f"{GITLAB_URL}/api/v4{endpoint}" headers = {'PRIVATE-TOKEN': GITLAB_TOKEN} @@ -616,7 +623,7 @@ def gitlab_api(endpoint, method='GET', data=None): response.raise_for_status() return response.json() except requests.exceptions.RequestException as e: - print(f"GitLab API Error (direct): {e}") + cicd_log.warning("[CI/CD] GitLab direct API unavailable: %s", e) # 備用方案:透過 SSH 在主機上執行 curl return gitlab_api_via_ssh(endpoint, method, data) @@ -628,6 +635,9 @@ def gitlab_api_via_ssh(endpoint, method='GET', data=None): Security: curl 參數以 list 形式傳給 subprocess,避免 shell injection。 endpoint 和 json_data 均作為獨立 argv 傳入,不經過 shell 解析。 """ + if not GITLAB_ENABLED: + return None + try: # 使用本地 GitLab URL;endpoint 由 gitlab_api() 內部構造,不含外部輸入 url = f"http://127.0.0.1:8929/api/v4{endpoint}" @@ -665,10 +675,10 @@ def gitlab_api_via_ssh(endpoint, method='GET', data=None): if result.returncode == 0 and result.stdout: return json.loads(result.stdout) else: - print(f"GitLab API Error (SSH): {result.stderr}") + cicd_log.warning("[CI/CD] GitLab SSH API unavailable: %s", result.stderr) return None except Exception as e: - print(f"GitLab API Error (SSH fallback): {e}") + cicd_log.warning("[CI/CD] GitLab SSH fallback failed: %s", e) return None diff --git a/tests/test_cicd_legacy_cluster_disabled.py b/tests/test_cicd_legacy_cluster_disabled.py index d8891d5..1274b25 100644 --- a/tests/test_cicd_legacy_cluster_disabled.py +++ b/tests/test_cicd_legacy_cluster_disabled.py @@ -82,3 +82,17 @@ def test_cicd_diagnosis_uses_runtime_note_without_cluster_probe(monkeypatch): runtime_checks = [check for check in diagnosis["checks"] if check["name"] == "Runtime 狀態"] assert runtime_checks assert runtime_checks[0]["runtime"] == "Docker Compose on 192.168.0.188" + + +def test_cicd_gitlab_api_is_quiet_when_legacy_integration_disabled(monkeypatch): + from routes import cicd_routes + + def fail_if_called(*args, **kwargs): + raise AssertionError("disabled GitLab integration should not call network or SSH") + + monkeypatch.setattr(cicd_routes, "GITLAB_ENABLED", False) + monkeypatch.setattr(cicd_routes.requests, "get", fail_if_called) + monkeypatch.setattr(cicd_routes.subprocess, "run", fail_if_called) + + assert cicd_routes.gitlab_api("/projects/1/pipelines") is None + assert cicd_routes.get_recent_pipelines(limit=3) == []