有用户要在 arm64 的 Debian 上跑 drouter。这一轮只对一个发行版做了适配 —— Armbian 26.8.1 trixie(Debian 13.6)/ aarch64 / 内核 6.18.35-ophub (Amlogic S912 板),别的 arm64 发行版未验证,验证范围与实测数据全部 写在 docs/07-arm64-适配说明.md。 改掉的四处 x86 假设: 1. helper._read_bios():ARM 板既无 /sys/firmware/efi 也无 /sys/class/dmi, 原代码把它们一律落到 else 分支,系统概览显示「传统 BIOS」(错的, 这类机器根本没有 BIOS)。新增设备树分支,判据顺序 EFI → 设备树 → BIOS (树莓派 4 这类两可的机器要优先认 EFI);读 /proc/device-tree 与 /sys/firmware/devicetree 两个候选路径,compatible 按 NUL 切开取第一条; 没有 DMI 厂商时拿设备树板名顶上,厂商格不再空白。返回结构加 fw_type / is_dt / compat,旧字段一个没动。 2. update.py:离线包资源名原先写死 -offline-amd64,arm64 机器点一键更新 会下载 x86 离线包,传几十 MB 才在装包阶段炸依赖。改为按 dpkg --print-architecture 拼名(_deb_arch / build_assets),并为带架构 后缀的附件在下载前先拿官方 SHA256SUMS 核对存在性,没有就明确报错。 3. packaging:离线包说明里两处写死的 amd64 换成 $ARCH(包名早就是变量拼的, 但包里那份 README 还在教用户核对 amd64);build-docker.sh 新增可选的 第 2 个参数 [arch],交叉构建时加 --platform 并给标签加 -<arch> 后缀, 不传第二参数时行为与改动前逐字节一致。 4. web/i18n.js:给新增的「设备树 (Device Tree)」展示串登记英文, 否则英文界面漏中文。 配套: - _dev/t-arch.py(31 条判据)+ _dev/t-arch-inject.py(20 例反向验证)。 反向验证抓出一条假绿:i18n 词条的 n/d 判据用了 300 字符松弛窗口, 会跨到下一条词条,删掉本词条的 d 之后照样绿 —— 已收紧为本词条花括号内。 - _dev/run-static.py 的 EXCL 补上 t-update-inject —— 它名字也是 t- 前缀, 之前一直被全量回归自动执行,会在回归过程中反复改写 backend/ 与 web/。 - devtools/verify-arm64.py:只读验收脚本,方便他人在自己的板子上复验。 - README 支持矩阵里 arm64 从「理论可用」改为「已实测 1 个发行版」。 本机全量静态回归 71 个检查器 / 0 失败;目标机上只读验证 12 条全通 (构建 753 396 字节的 deb / depcheck 73 项 / 8 个渲染模块 / 架构感知清单)。 目标机未安装 drouter,未启用任何服务,未改动网络。
101 lines
4.0 KiB
Python
101 lines
4.0 KiB
Python
"""drouter 静态回归驱动器(Windows 开发机用)。
|
||
|
||
为什么不用 shell 脚本跑:
|
||
这个环境里 bash 会话有 SIGTERM 风险,把整轮结果一起带走,
|
||
表现为「跑全量回归 → 命令莫名死掉」,看不出跑到第几个。
|
||
改成 Python逐个跑 + 每项前后写日志文件,崩了也能定位到具体那一项。
|
||
|
||
用法:
|
||
python _dev/run-static.py 跑全部
|
||
python _dev/run-static.py t-107 t-ca 只跑指定的
|
||
"""
|
||
import glob
|
||
import os
|
||
import re
|
||
import subprocess
|
||
import sys
|
||
|
||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||
ROOT = os.path.dirname(HERE)
|
||
|
||
# 需要真机 / 网络 / 交互 / 长时间压测的,不在静态回归范围内。
|
||
# ⚠️ 必须带 `t-` 前缀 —— ls 出来的名字就是 t-xxx。写裸名的话一个都
|
||
# 排除不掉,交互脚本照样跑,其中一个会把整个会话干掉。
|
||
EXCL = {
|
||
't-live', 't-smoke', 't-ulog-perf', 't-ulog-detail', 't-perf',
|
||
't-print', 't-cachebust', 't-cleanup', 't-webshell', 't-pty',
|
||
't-daemon-restart',
|
||
# ⚠️ 反向验证脚本**会真的改源文件**(注入 bug → 跑测试 → 还原)。
|
||
# 名字前缀也是 t-,会被 glob 自动收进来;一旦被自动执行,
|
||
# 它就会在回归过程中反复改写 backend/ 与 web/ —— 后果是
|
||
# 别的检查器读到被注入污染的代码,报出一堆假红,
|
||
# 更糟的是中途被打断会留下污染(已经发生过一次)。
|
||
# 只能手动跑:python _dev/t-107-inject-wg.py --batch 0
|
||
't-107-inject-wg',
|
||
# arm64 适配(2026-10-16)的反向验证,同上:覆盖 backend/drouter-update.py、
|
||
# backend/drouter-helper.py、web/i18n.js、packaging/*.sh 四处源码。
|
||
't-arch-inject', 't-update-inject', 'upd-inject',
|
||
}
|
||
|
||
|
||
def main():
|
||
args = sys.argv[1:]
|
||
if args:
|
||
names = [a if a.startswith('t-') else 't-' + a for a in args]
|
||
else:
|
||
names = sorted(os.path.basename(p)[:-3]
|
||
for p in glob.glob(os.path.join(HERE, 't-*.py')))
|
||
names = [n for n in names if n not in EXCL]
|
||
|
||
logpath = os.path.join(HERE, '.run-static.log')
|
||
log = open(logpath, 'w', encoding='utf-8')
|
||
|
||
def say(msg):
|
||
sys.stdout.write(msg + '\n')
|
||
sys.stdout.flush()
|
||
log.write(msg + '\n')
|
||
log.flush()
|
||
|
||
passed, failed = [], []
|
||
for n in names:
|
||
f = os.path.join(HERE, n + '.py')
|
||
if not os.path.isfile(f):
|
||
say(' %-20s 跳过(不存在)' % n)
|
||
continue
|
||
say(' %-20s ... ' % n)
|
||
try:
|
||
p = subprocess.run([sys.executable, f], capture_output=True,
|
||
text=True, encoding='utf-8', errors='replace',
|
||
timeout=180)
|
||
rc, out = p.returncode, (p.stdout or '')
|
||
except subprocess.TimeoutExpired:
|
||
rc, out = 124, 'TIMEOUT'
|
||
if rc == 0:
|
||
m = re.findall(r'通过 (\d+) 项', out)
|
||
say('ok%s' % ((' (%s 项)' % m[-1]) if m else ''))
|
||
passed.append(n)
|
||
else:
|
||
say('失败 rc=%s' % rc)
|
||
# 把红的那几行打出来 —— 只写 rc 的话,排查时只能一个个单独跑
|
||
# (2026-10-06:t-msg-en 在全量里红、单独跑绿,顺序依赖查不出来,
|
||
# 就是因为日志里只有「失败 rc=1」,看不到是哪条判据。)
|
||
# 各检查器的红标记不统一:有的用 [NG]、有的用 [FAIL]、有的打 ✘。
|
||
for line in out.splitlines():
|
||
if re.search(r'\[NG\]|\[FAIL\]|Traceback|Error|✘|异常',
|
||
line):
|
||
say(' ' + line[:150])
|
||
failed.append(n)
|
||
|
||
log.close()
|
||
print()
|
||
print('通过 %d 个检查器,失败 %d 个' % (len(passed), len(failed)))
|
||
if failed:
|
||
print('失败:%s' % ' '.join(failed))
|
||
return 1
|
||
print('(明细见 _dev/.run-static.log)')
|
||
return 0
|
||
|
||
|
||
if __name__ == '__main__':
|
||
sys.exit(main())
|