From 9b7b2675daf3270fa3b7827500a47ee93ae556bb Mon Sep 17 00:00:00 2001 From: JianFeeeee Date: Fri, 4 Sep 2026 20:06:14 +0800 Subject: [PATCH] =?UTF-8?q?fix(packaging):=20arm64=20GUI=20=E5=A1=9E?= =?UTF-8?q?=E4=BA=86=20x86-64=20electron=E2=80=94=E2=80=94=E6=8C=89?= =?UTF-8?q?=E7=9B=AE=E6=A0=87=E6=9E=B6=E6=9E=84=E5=8F=96=E8=BF=90=E8=A1=8C?= =?UTF-8?q?=E6=97=B6=E5=B9=B6=E5=BC=BA=E5=88=B6=E6=A0=A1=E9=AA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 现象 v1.0.0 与 v1.0.1 的 arm64 full/client 包里,homed 与 waiter 都是正确的 aarch64,但 GUI 目录下的 electron 是 x86-64。实测从 gitcode 下载的 homeagent-full_1.0.1_arm64.deb: usr/bin/homed ELF 64-bit ARM aarch64 ✓ usr/bin/waiter ELF 64-bit ARM aarch64 ✓ usr/lib/homeagent-gui/electron ELF 64-bit x86-64 ✗ 在 arm64 机器上装完,双击 GUI 得到 Exec format error。 ## 根因 build_gui 无条件 `cp -r "$gui_dir/node_modules/electron/dist"/*`,而那里 永远是 **host 架构**(本机 x64)。目录名 homeagent-gui-linux-arm64 只是 命名,内容从未跟着目标架构变。 这与 v1.0.0 arm64 缺 homed 是同一类错误:**产物名声称的架构与实际内容 不符**,且都因为没做交叉验证而漏过整个发布流程——包名对、目录名对、 主二进制对,只有一个嵌套的运行时是错的,没有任何一环会喊出来。 ## 修法:三层取 + 一道强制校验 1. 优先从 electron 缓存取目标架构的 zip (~/.cache/electron//electron-v-linux-.zip)。 版本号从已安装的 node_modules/electron/package.json 读,保证运行时 与 app 依赖一致。 2. 回退到 host node_modules/electron/dist 前**先比对架构**:只有目标 架构 == host 架构才允许;否则打印缺哪个 zip、该放哪里,然后跳过 GUI。 3. 最后用 `file -b` 校验 electron 二进制的实际架构必须匹配目标架构, 不符就删掉 GUI 目录并跳过。 第 3 步是关键。前两步是「尽量拿对的」,第 3 步是「绝不发错的」—— 宁可不发 GUI,也不发装了跑不起来的包。`GUI built:` 日志行也加上架构 标注,日常构建就能看见。 ## 验证 下载 arm64 electron 运行时(electron-v33.4.11-linux-arm64.zip,106MB, unzip -t 无错,解出的 electron 确认为 ARM aarch64)放入缓存后重打包, 三个 arm64 deb 实测: server homed=aarch64 waiter=aarch64 full homed=aarch64 waiter=aarch64 electron=aarch64 client waiter=aarch64 electron=aarch64 amd64 对照 electron=x86-64 arm64 tar.gz 从 120M 涨到 125M,也印证运行时换成了正确架构。 --- deploy/packaging/package-linux.sh | 81 ++++++++++++++++++++++++++++--- 1 file changed, 73 insertions(+), 8 deletions(-) diff --git a/deploy/packaging/package-linux.sh b/deploy/packaging/package-linux.sh index d508df6..0cb7163 100644 --- a/deploy/packaging/package-linux.sh +++ b/deploy/packaging/package-linux.sh @@ -122,6 +122,15 @@ build_go() { } # ---- build GUI (manual directory assembly, avoids electron-packager network issues) ---- +# +# electron 运行时必须按**目标架构**取,不能用 host 的 +# node_modules/electron/dist——那里永远是 host 架构(本机 x64)。 +# v1.0.0 / v1.0.1 的 arm64 full/client 包都踩了这个坑:目录名带 +# -arm64、homed/waiter 确实是 aarch64,但里面的 electron 是 x86-64, +# 在 arm64 机器上一启动就是 Exec format error(从未被交叉验证过)。 +# +# 现在改为优先从 electron 缓存里取对应架构的 zip,并在最后做 +# 一道强制校验:架构不符就删掉目录并跳过 GUI,宁可不发也不发坏包。 build_gui() { local gui_dir="$PROJECT_ROOT/cmd/gui" local gui_out="$BUILD_DIR/homeagent-gui-linux-${TAR_ARCH}" @@ -138,17 +147,52 @@ build_gui() { (cd "$gui_dir" && npm install --production) fi - local electron_dir="$gui_dir/node_modules/electron/dist" - if [ ! -f "$electron_dir/electron" ]; then - echo " WARNING: electron binary not found at $electron_dir. GUI will be skipped." - return + # electron 版本从已安装的包里读,保证运行时与 app 依赖一致 + local ever + ever=$(python3 -c "import json;print(json.load(open('$gui_dir/node_modules/electron/package.json'))['version'])" 2>/dev/null || true) + + mkdir -p "$gui_out" + + # 优先:缓存里的目标架构 zip(~/.cache/electron//electron-v-linux-.zip) + local zip="" + if [ -n "$ever" ]; then + zip=$(find "$HOME/.cache/electron" -name "electron-v${ever}-linux-${TAR_ARCH}.zip" 2>/dev/null | head -1) + fi + if [ -z "$zip" ]; then + zip=$(find "$HOME/.cache/electron" -name "electron-v*-linux-${TAR_ARCH}.zip" 2>/dev/null | head -1) + fi + + if [ -n "$zip" ]; then + echo " electron runtime: $(basename "$zip")" + unzip -q -o "$zip" -d "$gui_out" + else + # 回退:仅当目标架构 == host 架构时才能用 host 的 dist + local host_arch + case "$(uname -m)" in + x86_64) host_arch=amd64 ;; + aarch64|arm64) host_arch=arm64 ;; + *) host_arch=unknown ;; + esac + if [ "$TAR_ARCH" != "$host_arch" ]; then + echo " WARNING: 缺 electron-v*-linux-${TAR_ARCH}.zip 缓存,且目标架构与 host" + echo " ($host_arch) 不同——不能用 host 的 electron 冒充。跳过 GUI。" + echo " 解法:下载 electron-v${ever:-}-linux-${TAR_ARCH}.zip 到" + echo " ~/.cache/electron/<任意子目录>/ 后重跑。" + rm -rf "$gui_out" + return + fi + local electron_dir="$gui_dir/node_modules/electron/dist" + if [ ! -f "$electron_dir/electron" ]; then + echo " WARNING: electron binary not found at $electron_dir. GUI will be skipped." + rm -rf "$gui_out" + return + fi + echo " electron runtime: host node_modules (同架构 $host_arch)" + cp -r "$electron_dir"/* "$gui_out/" 2>/dev/null fi mkdir -p "$gui_out/resources/app/node_modules" mkdir -p "$gui_out/resources/app/renderer" - - # copy electron runtime (binary + shared libs) - cp -r "$electron_dir"/* "$gui_out/" 2>/dev/null rm -f "$gui_out/resources/default_app.asar" 2>/dev/null # copy app source @@ -190,7 +234,28 @@ LAUNCHER chmod +x "$gui_out/homeagent-gui" chmod +x "$gui_out/electron" - echo " GUI built: $gui_out ($(du -sh "$gui_out" | cut -f1))" + # 最后一道强制校验:electron 二进制的实际架构必须匹配目标架构。 + # 不做这步就会重现 v1.0.0/v1.0.1 的隐形坏包:包名、目录名、 + # homed/waiter 全对,只有 electron 是错架构,直到用户在 arm64 机器上 + # 双击才发现 Exec format error。 + local want_pat + case "$TAR_ARCH" in + amd64) want_pat="x86-64" ;; + arm64) want_pat="aarch64" ;; + *) want_pat="" ;; + esac + if [ -n "$want_pat" ]; then + local got + got=$(file -b "$gui_out/electron" 2>/dev/null || echo "") + if ! printf '%s' "$got" | grep -q "$want_pat"; then + echo " ERROR: electron 架构不符——期望 $want_pat,实际: ${got%%,*}" + echo " 删除 GUI 目录并跳过(宁可不发,也不发装了跑不起来的包)。" + rm -rf "$gui_out" + return + fi + fi + + echo " GUI built: $gui_out ($(du -sh "$gui_out" | cut -f1), $(file -b "$gui_out/electron" | cut -d, -f2 | tr -d ' '))" echo "" }