显示完整性改动前后对比(ui-delta.py)¶
位置:
deploy/nfos/tools/ui-delta.py| 语言:python3 | 是否需要 root:否
工具性质:本地静态对比(只读两份 CSS;只写 --out 指定的文件)
运行前提:仓库检出必须在场(加载 apps/web/tests/lint-layout.py)。可从任意位置运行(工具自动向上找到含 deploy/nfos 的祖先目录)
部署状态:随仓库检出运行,不部署到 /opt/Cy/services/system.d/nfos/
用途¶
对「显示完整性」改动做前后对比,且做到工具版本无关。
源码 docstring 说明的动因(逐字引用):
为什么需要它:并发 agent 在我工作期间既改了 nf.css,也改了 tests/lint-layout.py。
拿“旧工具的结果”和“新工具的结果”对比是不成立的,所以这里:
1) 用**同一份当前版** lint-layout.py 分别去量「基线备份 .bak-20260926-190842」与「当前 nf.css」;
2) 另外用自带的纯正则模式计数(不依赖任何外部工具)交叉验证;
3) 逐条检查“审计缺口”是否仍然存在(针对具体规则字符串做断言式检查)。
输出 4 块:
| 块 | 内容 |
|---|---|
| 摘要行 | BASE <base 路径> / CUR <cur 路径> 各自的字节数与 sha16(sha256 前 16 位) |
| 1. 纯正则模式计数 | 源码内 PATTERNS 共 17 条模式,先剥离 /* */ 注释再计数(表格列为 模式 / 改动前 / 改动后 / 增减);随后再打印一份含注释的原始计数“仅供查证” |
| 2. lint-layout.py 对比 | 用同一份当前版 lint_layout 量两份 CSS(lint_css + lint_clip_ancestors),按规则名 R* 汇总 改动前 / 改动后 / 增减 与合计 |
| 3. 审计缺口逐条判定 | 源码内 GAPS 共 9 条(G1–G9),每条输出 已修 / 未修(后者若匹配“原写法仍在”正则会追加 (原写法仍在)),并打印该条的 判定式 |
| 4. 结尾追加区块标记 | 用 /\* ===== ([^\n]*?) ===== \*/ 提取 --cur 里所有追加区块标题 |
GAPS 的 9 条缺口(编号 + 说明逐字来自源码):
| 编号 | 缺口说明(源码逐字) |
|---|---|
G1 |
.nf-nowrap 仍用省略号吃掉 store 订单号/商品名/自提点等“必须看全”字段 |
G2 |
.nf-code 是 white-space:pre(长 JSON/长 URL 只能横向拖) |
G3 |
ui.js 给弹层写内联 width,窄屏盖掉媒体查询(弹层比视口宽,右侧在屏外) |
G4 |
.nf-drawer 无 max-width 兜底(同上,内联 420px) |
G5 |
.nf-search-results 无 max-height(absolute 浮层越出视口,页面滚不动) |
G6 |
.nf-user-menu 无 max-height/overflow(同上) |
G7 |
.nf-notify-list 固定 380px,矮窗口顶出屏幕 |
G8 |
折叠轨回归:.nf-side{min-width:240px} 未给 .collapsed 兜底(>1200px 手动折叠时侧栏压住正文) |
G9 |
.nf-kv dd 只靠 word-break(无 overflow-wrap:anywhere) |
典型场景¶
- 改完
nf.css后,给出“相对基线备份,17 个模式计数与 lint 规则命中各增减多少”的量化证据; - 回归核对:确认 G1–G9 的 9 条审计缺口确实从
未修变成已修,且没有把旧写法留在别处; - 复盘“改了 CSS 但视觉没变”的原因:对比 lint 命中数是否有变化,或某条缺口仍是
未修(原写法仍在)。
用法与参数¶
源码 docstring 给出的用法(逐字引用,.scratch/ 为升级前旧路径;本页按新位置书写,路径参数不变):
用法:
python .scratch/ui-delta.py --base apps/web/index/css/nf.css.bak-20260926-190842 \
--cur apps/web/index/css/nf.css --out .scratch/ui-delta.txt
参数来自 argparse.ArgumentParser():
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
--base |
字符串(文件路径) | 无默认值(required=True) |
改动前的基线文件(通常是 CSS 的 .bak-<时间戳> 备份) |
--cur |
字符串(文件路径) | 无默认值(required=True) |
改动后的当前文件 |
--out |
字符串(文件路径) | ''(空 = 写 stdout) |
输出文件;给值时打印 written: <path> (<N> bytes) |
无参数时的默认行为:没有默认行为 —— --base 与 --cur 都必填,缺失时 argparse 报错并以退出码 2 结束。
两个路径参数都按当前工作目录解析(源码不做仓库根拼接),推荐在仓库根执行并传相对路径。
示例¶
从仓库根目录执行(--base 请换成实际存在的基线备份文件名):
python deploy/nfos/tools/ui-delta.py `
--base apps/web/index/css/nf.css.bak-20260926-190842 `
--cur apps/web/index/css/nf.css `
--out .scratch/ui-delta.txt
期望输出形态:
正文形如:
== 显示完整性:基线 vs 当前 ==
BASE apps/web/index/css/nf.css.bak-... ... B sha16=...
CUR apps/web/index/css/nf.css ... B sha16=...
-- 1. 纯正则模式计数(不依赖任何工具版本;**注释已剥离**,...) --
模式 改动前 改动后 增减
overflow:hidden 12 9 -3
...
-- 2. lint-layout.py(**同一份当前版工具**量两份 css,公平对比) --
规则 改动前 改动后 增减
R1 5 2 -3
合计 ...
-- 3. 审计缺口逐条判定(G=gap,见脚本内 GAPS) --
G1 已修 .nf-nowrap 仍用省略号吃掉 ...
判定式: \.nf-nowrap\s*\{[^}]*white-space\s*:\s*normal
...
-- 4. 结尾追加区块标记 --
WEB-UI-CLIP-FIX BEGIN/END ...
| 情形 | 期望退出码 |
|---|---|
| 正常完成 | 0(源码未调用 sys.exit) |
缺 --base / --cur |
2(argparse 报错) |
任一路径不存在 / --out 不可写 |
1(Python traceback) |
lint-layout.py 缺失或 lint_css/lint_clip_ancestors 接口变更 |
1(importlib/AttributeError traceback) |
注意:G1–G9 的判定结果不影响退出码 —— 出现
未修也仍返回0;判定要看正文第 3 块。
依赖与权限¶
- 语言/版本:Python 3(
#!/usr/bin/env python3)。 - 第三方依赖:无,标准库
argparse/hashlib/importlib.util/io/os/re/sys。 - 仓库依赖:必需 ——
load_lint()加载<repo>/apps/web/tests/lint-layout.py,依赖其lint_css/lint_clip_ancestors(第 2 块)。第 1、3、4 块是自带纯正则,不依赖外部工具。 - 权限:不需要 root;不需要网络(纯离线);写权限仅用于
--out。
风险与注意事项¶
- 只读两份 CSS,只写
--out:不修改--base/--cur,不改任何仓库文件。不传--out时只写 stdout,不落盘。 --base必须是真正的“改动前”文件:本工具不做自动备份、不做版本控制查询。传错文件(例如两份都是当前版)会得出“无变化”的假结论。- 第 3 块是正则断言,不是语义证明:
已修仅代表匹配到了源码内那条“已修判定式”。例如 G1 的判定式只要求.nf-nowrap {...}里出现white-space: normal;若该声明被更靠后的规则覆盖,仍会判已修。确切结论需配合ui-keyrules.py看最终生效声明。 - 第 1 块与第 2 块口径不同:第 1 块剥注释后按 17 条正则计数;第 2 块走
lint-layout.py的规则体系。两块不应互相校验,它们是交叉验证用的两条独立证据。 G5/G6/G7/G8/G9在源码里alive_rx为None,因此这些条目只会有已修/未修两种输出,不会追加“(原写法仍在)”。
回滚¶
本工具不部署到线上、不改线上系统;不改动任何被对比文件。
- 产出/改动位置:仅
--out指定的文件(例如.scratch/ui-delta.txt)。 - 清理方式:
- 基线文件(
.bak-*)由使用者自己创建;本工具既不创建也不删除它。不给--out时无任何落盘物,无需清理;线上侧无需回滚。