#!/usr/bin/env bash # 构建 homeagent-mail-bridge —— 必须与**内核链在同一个 SDK 上**,构建前后都校验。 # # # 为什么(2026-09-14 事故 + 当天复发 + 今天查出的真根因) # # · 08:09 事故:内核升级,插件二进制还是旧 SDK 编的 → 启动即 SIGSEGV,握手失败, # 5 分钟内崩 4 次后熔断「停止自动重启,需人工介入」; # · 当天处置:重编成 0.2.2 / sdk=1.3.0; # · 现在又崩:`go version -m /usr/local/bin/homed` 显示**本机内核**链的是 # dep gitcode.com/JianFeeeee/homeagent-sdk v0.9.2 # => ./third_party/homeagent-sdk (devel) # 而插件 go.mod 钉的是 `/root/.homeagent/hmapdev/sdk/v1.3.0` —— # **两条完全不同的 SDK 血脉**。所以"照原样重编"不但没用,还必然再造一个崩的包。 # # 结论:判据不该是"版本号看起来一样",而该是**两个二进制的依赖表一样**。 # 这个脚本就用 `go version -m` 把内核的 SDK 依赖读出来,按它构建,再回头验产物。 set -euo pipefail HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" KERNEL_BIN="${HOMEAGENT_KERNEL_BIN:-/usr/local/bin/homed}" SDK_MODULE="gitcode.com/JianFeeeee/homeagent-sdk" die() { echo "构建失败:$*" >&2; exit 1; } # cron/非登录 shell 里 HOME 可能没设,Go 会因此拒绝工作(曾经在这里栽过) export HOME="${HOME:-/root}" export GOPATH="${GOPATH:-$HOME/go}" export GOCACHE="${GOCACHE:-${TMPDIR:-/tmp}/go-build-agentmail}" mkdir -p "$GOCACHE" [[ -x "$KERNEL_BIN" ]] || die "找不到内核二进制 $KERNEL_BIN(用 HOMEAGENT_KERNEL_BIN 指定)" # ── 1. 读内核链的 SDK:模块版本 + 本地替换目录 ────────────────────── KERNEL_INFO="$(go version -m "$KERNEL_BIN" 2>/dev/null)" || die "读不出 $KERNEL_BIN 的构建信息(go version -m)" KERNEL_SDK_VER="$(awk -v m="$SDK_MODULE" '$1=="dep" && $2==m {print $3}' <<<"$KERNEL_INFO" | head -1)" KERNEL_SDK_PATH="$(awk -v m="$SDK_MODULE" '/^[[:space:]]*=>/ {print $2}' <<<"$KERNEL_INFO" | head -1)" [[ -n "$KERNEL_SDK_VER" ]] || die "内核没有链 $SDK_MODULE —— 这不对,先人工看一眼" echo "[build] 内核 $KERNEL_BIN 链的 SDK:${KERNEL_SDK_VER}${KERNEL_SDK_PATH:+(替换目录 $KERNEL_SDK_PATH)}" # ── 2. 找到内核用的那份 SDK 源码 ─────────────────────────────────── SDK_DIR="${HOMEAGENT_SDK_DIR:-}" if [[ -z "$SDK_DIR" && -n "$KERNEL_SDK_PATH" ]]; then # 内核里的路径是**构建时**的相对路径,本机按候选根目录找同名子树 for root in /root/ha-test /root /home/program; do cand="$root/${KERNEL_SDK_PATH#./}" if [[ -f "$cand/go.mod" ]]; then SDK_DIR="$cand"; break; fi done fi if [[ -z "$SDK_DIR" ]]; then # 退路:本机 SDK 指针(hmapdev 那套)。**这里必须出声** —— 它与内核血脉是否一致 # 由第 4 步的依赖表比对来判,不靠人猜。 ptr_root="${HOMEAGENT_SDK_ROOT:-/root/.homeagent/hmapdev/sdk}" [[ -f "$ptr_root/current" ]] || die "既找不到内核的 SDK 源码,也没有 $ptr_root/current" SDK_DIR="$ptr_root/$(tr -d '[:space:]' <"$ptr_root/current")" echo "[build] 警告:没找到内核的 SDK 源码目录,改用本机指针 $SDK_DIR" >&2 fi [[ -f "$SDK_DIR/go.mod" ]] || die "$SDK_DIR 不是一个 Go 模块(没有 go.mod)" SDK_VER="$(grep -m1 -oE 'Version = "[0-9]+\.[0-9]+\.[0-9]+"' "$SDK_DIR/meta/meta.go" 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' || true)" echo "[build] 本次用 SDK 源码:$SDK_DIR(meta.Version=${SDK_VER:-未知})" # ── 3. 用临时 modfile 替换,**不改动**受版本管理的 go.mod ────────── cd "$HERE" TMP_MOD="$(mktemp -d)/go.local.mod" cp go.mod "$TMP_MOD" go mod edit -modfile="$TMP_MOD" -replace "$SDK_MODULE=$SDK_DIR" export GOFLAGS="${GOFLAGS:-} -modfile=$TMP_MOD" go build ./... hmapdev build # ── 4. 产物自检:依赖表必须与内核一致(这才是"对齐"的定义)──────── echo "[build] 校验产物:" BIN="build/plugin.bin" [[ -f "$BIN" ]] || die "没找到产物 $BIN" OUT_SDK_VER="$(go version -m "$BIN" | awk -v m="$SDK_MODULE" '$1=="dep" && $2==m {print $3}' | head -1)" echo " 产物链的 SDK:${OUT_SDK_VER:-(无)} | 内核链的 SDK:$KERNEL_SDK_VER" if [[ -z "$OUT_SDK_VER" ]]; then die "产物没有链 $SDK_MODULE —— 装上去必然握不上手" fi if [[ "$OUT_SDK_VER" != "$KERNEL_SDK_VER" ]]; then die "产物与内核链的 SDK 版本不同($OUT_SDK_VER vs $KERNEL_SDK_VER)—— 这正是握手失败/崩溃循环的形状。装上去之前必须先对齐。" fi echo "[build] 完成:$BIN 与内核链在同一 SDK 上($OUT_SDK_VER)"