有用户要在 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,未启用任何服务,未改动网络。
122 lines
5.4 KiB
Bash
122 lines
5.4 KiB
Bash
#!/bin/bash
|
||
# =============================================================================
|
||
# 构建 drouter 的 Docker 镜像
|
||
#
|
||
# bash packaging/build-docker.sh [tag] [arch]
|
||
#
|
||
# 产物:drouter:<tag> (arch 省略 / 与本机架构相同时)
|
||
# drouter:<tag>-<arch> (交叉构建时,标签带架构后缀)
|
||
#
|
||
# arm64 适配(2026-10-16):Debian 13 的 arm64 机器也能跑 drouter
|
||
# (deb 是 Architecture: all,纯 Python),镜像自然也该能出 arm64 版本:
|
||
# bash packaging/build-docker.sh 1.0.10 arm64
|
||
# ⚠️ 交叉构建要靠 buildx/qemu 提供目标架构的执行能力,
|
||
# 宿主机没有装 binfmt_misc 会卡在 RUN 那一步;这种机器上请直接跑到 ARM 主机上构建。
|
||
#
|
||
# 注意:Dockerfile 里 COPY 的是相对路径,所以构建上下文必须是项目根目录。
|
||
# =============================================================================
|
||
set -euo pipefail
|
||
|
||
HERE="$(cd "$(dirname "$0")/.." && pwd)"
|
||
# 版本号唯一真源是 packaging/VERSION(见 build-deb.sh 里的说明)
|
||
VF="$HERE/packaging/VERSION"
|
||
[ -f "$VF" ] || { echo "缺少 $VF(版本号唯一真源)"; exit 1; }
|
||
TAG="${1:-$(tr -d ' \t\r\n' < "$VF")}"
|
||
# 目标架构。省略时按本机 dpkg 架构处理 —— 那等于「原生构建」:
|
||
# 不加 --platform,标签也不带后缀,产物与改动前**逐字节一致**。
|
||
DPKG_NATIVE="$(dpkg --print-architecture 2>/dev/null || echo amd64)"
|
||
ARCH="${2:-$DPKG_NATIVE}"
|
||
|
||
command -v docker >/dev/null 2>&1 || { echo "需要 docker"; exit 1; }
|
||
|
||
# 交叉构建(目标架构 ≠ 本机架构)才加 --platform 与标签后缀。
|
||
# 原生构建硬塞 --platform 反而会拖慢(docker 可能跳过本地缓存层)。
|
||
if [ "$ARCH" = "$DPKG_NATIVE" ]; then
|
||
PLATFORM_ARG=()
|
||
IMGTAG="$TAG"
|
||
echo "== 目标架构:$ARCH(与本机一致,原生构建)=="
|
||
else
|
||
PLATFORM_ARG=(--platform "linux/$ARCH")
|
||
IMGTAG="$TAG-$ARCH"
|
||
echo "== 目标架构:$ARCH(交叉构建,标签后缀 -$ARCH)=="
|
||
fi
|
||
|
||
echo "== 1/4 构建前自测(避免把坏代码打进镜像)=="
|
||
python3 - <<'PY'
|
||
import ast, glob
|
||
bad = 0
|
||
for f in sorted(glob.glob('backend/*.py')):
|
||
try:
|
||
ast.parse(open(f, encoding='utf-8').read()); print(' OK:', f)
|
||
except Exception as e:
|
||
print(' FAIL:', f, e); bad += 1
|
||
raise SystemExit(1 if bad else 0)
|
||
PY
|
||
command -v node >/dev/null 2>&1 && node --check web/app.js && echo " OK: web/app.js"
|
||
|
||
echo "== 2/4 构建镜像 drouter:$IMGTAG =="
|
||
docker build "${PLATFORM_ARG[@]}" -f "$HERE/packaging/docker/Dockerfile" \
|
||
-t "drouter:$IMGTAG" "$HERE"
|
||
|
||
# 同时打一个带用户名的别名,方便日后直推 Docker Hub / 私有仓库。
|
||
# 两个标签指向同一个 image id,不额外占空间。
|
||
docker tag "drouter:$IMGTAG" "liuzhuohua/drouter:$IMGTAG"
|
||
|
||
echo "== 3/4 渲染 Release 附件(docker-compose.yml / DOCKER-GUIDE.md)=="
|
||
# 这两个文件是**模板**,里面写的是 __VERSION__ 占位符,不是具体版本号。
|
||
# 早先它们直接硬编码版本号,于是每次发版都要手工 sed 一遍 —— 上一版就漏过,
|
||
# 附件里带着 drouter:1.0.8,用户拿 1.0.9 的 tar 加载后标签对不上,
|
||
# `docker compose up` 直接报「找不到镜像」。所以这里从 VERSION 渲染。
|
||
mkdir -p "$HERE/dist"
|
||
for f in docker-compose.yml DOCKER-GUIDE.md; do
|
||
src="$HERE/packaging/docker/$f"
|
||
[ -f "$src" ] || { echo "缺少模板 $src"; exit 1; }
|
||
# 两步:① 整块删掉 TEMPLATE-NOTE 标记块(那是给维护者看的「这是模板」提示,
|
||
# 留着会让用户看到「1.0.9 是占位符」这种自相矛盾的话);
|
||
# ② 再把剩下的 __VERSION__ 换成真实版本号。
|
||
# 顺序不能反 —— 先替换的话,标记块里的 __VERSION__ 也会被换掉,
|
||
# 删的时候连同「这是模板」的语义一起留在了附件里。
|
||
sed '/TEMPLATE-NOTE:BEGIN/,/TEMPLATE-NOTE:END/d' "$src" \
|
||
| sed "s/__VERSION__/$IMGTAG/g" > "$HERE/dist/$f"
|
||
# 渲染完必须一个占位符都不剩 —— 剩下就说明模板里有漏网的写法
|
||
if grep -q '__VERSION__' "$HERE/dist/$f"; then
|
||
echo "✘ $f 渲染后仍含 __VERSION__,模板有问题"; exit 1
|
||
fi
|
||
# 标记块也不能残留(否则说明嵌套写错了,BEGIN/END 没配对)
|
||
if grep -q 'TEMPLATE-NOTE' "$HERE/dist/$f"; then
|
||
echo "✘ $f 渲染后仍含 TEMPLATE-NOTE 标记"; exit 1
|
||
fi
|
||
echo " dist/$f ($IMGTAG)"
|
||
done
|
||
|
||
echo "== 4/4 产物 =="
|
||
docker images --filter "reference=drouter" --filter "reference=liuzhuohua/drouter"
|
||
echo ""
|
||
cat <<RUN
|
||
运行示例(详见 dist/DOCKER-GUIDE.md):
|
||
|
||
# 方式 A:docker compose(Release 里附了 docker-compose.yml)
|
||
docker compose up -d
|
||
|
||
# 方式 B:docker run
|
||
docker run -d --name drouter --restart unless-stopped \\
|
||
--network host \\
|
||
--cap-add NET_ADMIN --cap-add NET_RAW \\
|
||
-e TZ=Asia/Shanghai \\
|
||
-v drouter-etc:/etc/drouter \\
|
||
-v drouter-data:/opt/drouter/data \\
|
||
-v drouter-snapshots:/opt/drouter/snapshots \\
|
||
-v drouter-log:/var/log/drouter \\
|
||
drouter:$IMGTAG
|
||
|
||
导出成 tar(用于分发):
|
||
docker save drouter:$IMGTAG liuzhuohua/drouter:$IMGTAG \\
|
||
-o dist/drouter-$IMGTAG-docker.tar
|
||
|
||
说明:
|
||
--network host 让容器直接看到宿主机网卡(路由类功能的前提)
|
||
NET_ADMIN 允许操作 nftables / 网卡 / 路由表
|
||
不需要 --privileged:只要 NET_ADMIN + NET_RAW 就够
|
||
默认账号 admin / admin123,登录后请立即修改
|
||
RUN
|