From da77d8eb6bd86c9e9aab28c744244ba109ddf586 Mon Sep 17 00:00:00 2001 From: butubb <1422726308@qq.com> Date: Sun, 13 Sep 2026 10:48:36 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E5=9B=9E=E5=BD=92=E8=84=9A=E6=9C=AC=20W?= =?UTF-8?q?indows=20=E5=8F=AF=E7=94=A8=20+=20=E5=8A=A0"=E7=A6=81=E6=AD=A2?= =?UTF-8?q?=E5=AF=B9=E7=94=9F=E4=BA=A7=E5=BA=93=E8=B7=91"=E5=AE=89?= =?UTF-8?q?=E5=85=A8=E9=97=B8=EF=BC=9B=E8=BF=81=E7=A7=BB=E8=84=9A=E6=9C=AC?= =?UTF-8?q?=E5=8E=BB=E6=8E=89=20GBK=20=E6=8E=A7=E5=88=B6=E5=8F=B0=E4=BC=9A?= =?UTF-8?q?=E5=B4=A9=E7=9A=84=E7=AC=A6=E5=8F=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - scripts/regression_test.py: * signal.alarm 加 hasattr 守卫(Windows 上直接 AttributeError 退出, backlog A4)→ 本机终于能跑"一条命令扫全接口 500" * 新增安全闸:.env 声明 prod、或目标库 app_meta.deployment_env=prod 时 **拒绝运行**(exit 3)。本脚本会发真实写请求(设备池增删/剪贴板注入/ 亮灭屏),跑在生产配置上就是拿正式数据做实验 - scripts/migrate_sqlite_to_mysql.py: stdout 重设为 UTF-8 并去掉 ✔/✘ 符号。 实测在 GBK 控制台里打印 ✔ 会抛 UnicodeEncodeError,而且崩在"写库标签"之前, 看起来像迁移失败(数据其实已搬完) - doc/backlog/TODO.md: A4 移入已完成;gitignore 条目更新(mcp_audit.log 已补, uiauto.pid 仍缺);登记 MySQL 迁移与回归安全闸 实测(对 192.168.2.27 的 auto_control_dev):数据迁移 12 张表逐表 SHA-256 一致; 应用直连 MySQL 全部接口 200,设备名/指纹唯一索引语义与 SQLite 一致(空值可重复、 非空重复被拦、大小写敏感 A08≠a08);备份导出→预览→应用往返正常。 --- doc/backlog/TODO.md | 14 +++++--------- scripts/migrate_sqlite_to_mysql.py | 12 ++++++++++-- scripts/regression_test.py | 27 +++++++++++++++++++++++++-- 3 files changed, 40 insertions(+), 13 deletions(-) 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 = (