diff --git a/doc/backlog/TODO.md b/doc/backlog/TODO.md index b5f44d8..d833de7 100644 --- a/doc/backlog/TODO.md +++ b/doc/backlog/TODO.md @@ -32,14 +32,6 @@ - **涉及**:`web_server.py`(注册钩子)+ 需要回归所有写接口(前端、脚本、MCP 的 `platform_client` 都已带 token,风险点是第三方直接调 API)。 - **验收**:注册后所有写接口在缺 token 时返回 403,且前端/MCP 全链路正常。 -### A4. 路由回归脚本在 Windows 上不可用 - -- **现象**:`python scripts/regression_test.py` 直接报 `AttributeError: module 'signal' has no attribute 'alarm'` 退出。 -- **原因**:脚本用了 Unix 专有的 `signal.alarm`(第 29 行),docstring 里的示例路径也是 `.venv/bin/python`。 -- **影响**:本机(Windows)开发时没有"一条命令扫全接口 500"的手段——A1/A2 这类 import 遗漏本可被它第一时间发现。 -- **涉及**:`scripts/regression_test.py`(把 `signal.alarm` 换成跨平台超时,或 `hasattr` 守卫)。 -- **验收**:Windows 与 Linux 都能跑;能扫出 A1。 - ### A5. 前端 `--card-line` 变量未定义 - **现象**:AI 控制台的会话/经验/动作模态框边框失效(`border:1px solid var(--card-line)` 解析为无效值)。 @@ -74,7 +66,7 @@ - `ANDROID_ADB_SERVER_ADDRESS/HOST/PORT` 会被 `.env` 注入 `os.environ`,**adb 客户端**据此把所有调用指向 220;`ANDROID_ADB_SERVER_HOST` 是非标准键(adb 不认)。 - 期望:在配置速查里写明该行为与取舍,或本地去掉这三行改走本机 adb。 -- [ ] **`.gitignore` 漏项**:`data/mcp_audit.log` 与 `data/uiauto.pid` 未被忽略(`git status` 会显示为未跟踪);`data/apks/` 目录无占位文件。 +- [ ] **`.gitignore` 漏项**:`data/mcp_audit.log` 已补(`data/*.log`,2026-09-13);**`data/uiauto.pid` 仍未忽略**(`git status` 会显示为未跟踪);`data/apks/` 目录无占位文件。 - [ ] 220 的 Tailscale / adb server(5037) 可达性巡检(曾出现连不通超时)。 @@ -129,3 +121,7 @@ - [x] 监控页任务卡去掉「编辑/删除」,只留执行/停用 + 显示**覆盖设备**。 - [x] 备份覆盖清单补 `agent_action` / `app_meta` + 导出/导入双向自检。 - [x] AI 控制台:Markdown 渲染、推理链可折叠、token 用量显示。 +- [x] 路由回归脚本 Windows 可用(2026-09-13:`signal.alarm` 加 `hasattr` 守卫)+ 新增安全闸: + 配置或目标库被标为 `prod` 时**拒绝运行**(脚本会发真实写请求,不能拿生产数据做实验)。 +- [x] 数据库从 SQLite 迁到 MySQL:连接层多环境配置 + 防混库校验、schema 收敛到 ORM metadata、 + 备份/恢复改为「SQLite 归档 + 单事务整库替换」、迁移脚本(2026-09-13)。 diff --git a/scripts/migrate_sqlite_to_mysql.py b/scripts/migrate_sqlite_to_mysql.py index 144cb4e..e619df1 100644 --- a/scripts/migrate_sqlite_to_mysql.py +++ b/scripts/migrate_sqlite_to_mysql.py @@ -40,6 +40,14 @@ import uuid ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) sys.path.insert(0, ROOT) +# Windows 控制台默认 GBK,输出里带 ✔ 之类的符号会直接抛 UnicodeEncodeError +# (本脚本是运维在终端里手跑的,崩在最后一步会让人以为迁移失败) +try: + sys.stdout.reconfigure(encoding="utf-8", errors="replace") + sys.stderr.reconfigure(encoding="utf-8", errors="replace") +except Exception: + pass + from sqlalchemy import inspect as sa_inspect, text as sa_text # noqa: E402 @@ -306,7 +314,7 @@ def main(): with app.app_context(): stats, ok = copy_and_verify(args.sqlite, tables, "verify") print_stats(stats) - print("\n校验结果: " + ("全部一致 ✔" if ok else "存在不一致 ✘")) + print("\n校验结果: " + ("全部一致 OK" if ok else "存在不一致 FAIL")) sys.exit(0 if ok else 1) # 目标库环境标签检查(防止误把 dev 数据灌进生产库) @@ -331,7 +339,7 @@ def main(): print_stats(stats) if not ok: _fail("搬运后校验不一致 —— 事务已回滚,目标库保持原样。请把上面的表格发给我排查") - print(f"\n搬运完成,用时 {time.time() - t0:.1f}s,逐表校验一致 ✔") + print(f"\n搬运完成,用时 {time.time() - t0:.1f}s,逐表校验一致 OK") # 4) 写库标签与版本 src = sqlite3.connect(_readonly_uri(args.sqlite), uri=True) diff --git a/scripts/regression_test.py b/scripts/regression_test.py index 5b1ff19..a9afb21 100644 --- a/scripts/regression_test.py +++ b/scripts/regression_test.py @@ -25,11 +25,34 @@ sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) # 干扰回归/占用设备——实测 ocr关键字任务每 30 分钟触发导致回归超时) os.environ["DISABLE_SCHEDULER"] = "1" -# 单接口超时保护:adb 相关接口可能慢(连接/查询设备),给足时间 -signal.alarm(300) +# 单接口超时保护:adb 相关接口可能慢(连接/查询设备),给足时间。 +# Windows 没有 signal.alarm(会 AttributeError 直接崩),那里就靠调用方自己掐时间。 +if hasattr(signal, "alarm"): + signal.alarm(300) from web_server import app # noqa: E402 (导入即完成 app 装配,与生产一致) +# ================== 安全闸:绝不对生产库跑写操作 ================== +# 本脚本会发真实写请求(设备池增删、剪贴板注入、亮灭屏…)。跑在连生产库的配置上 +# 就是拿正式数据做实验——直接拒绝启动。 +from core import db_config # noqa: E402 + +if db_config.DEPLOY_ENV == "prod": + print("=" * 70) + print("拒绝运行:当前 .env 声明 DEPLOY_ENV=prod,本脚本会发真实写请求。") + print("回归测试请在 dev 环境跑(或先把 .env 指向 dev 库/临时库)。") + print("=" * 70) + sys.exit(3) +with app.app_context(): + _labeled = db_config.meta_get("deployment_env") + if _labeled == "prod": + print("=" * 70) + print("拒绝运行:目标库 %s 被登记为 prod 环境(app_meta.deployment_env)。" + % db_config.describe_target()) + print("回归测试会写数据,不能对生产库执行。") + print("=" * 70) + sys.exit(3) + # 流式/二进制接口:无法用普通 GET 断言,跳过(已有单独验证路径) _SKIP_PREFIXES = (