mirror of
https://github.com/hotwa/luci-app-openclaw.git
synced 2026-03-30 20:25:44 +00:00
release v2.0.0: 适配 OpenClaw v2026.3.13
重大变更: - 配置管理菜单重构,更清晰的导航结构 - 新增高级配置菜单 - 新增全局环境变量 /etc/profile.d/openclaw.sh 修复: - QQ 机器人插件配置名称不匹配 (#XX) - 安装运行环境报错缺少 libstdcpp6 (#28) - 环境变量路径混乱 (#42) 新增: - 查看日志功能 - 飞书 Bot 配置流程优化 适配: - Node.js 版本升级到 22.16.0 - OpenClaw 版本升级到 v2026.3.13 - 依赖声明新增 libstdcpp6
This commit is contained in:
@@ -1,114 +1,262 @@
|
||||
#!/bin/sh
|
||||
# ============================================================================
|
||||
# Node.js ARM64 musl 构建脚本
|
||||
# 在 Alpine ARM64 Docker 容器内运行
|
||||
# 环境变量: NODE_VER (目标版本号), /output (输出目录)
|
||||
#
|
||||
# 环境变量:
|
||||
# NODE_VER (目标版本号)
|
||||
# BUILD_MODE (apk|cross) - apk: 使用 Alpine apk, cross: 从官方 glibc 版本交叉编译
|
||||
# /output (输出目录)
|
||||
#
|
||||
# 打包策略:
|
||||
# 1. apk 模式: 使用 Alpine apk 安装 nodejs,版本受限于 Alpine 仓库
|
||||
# 2. cross 模式: 从 Node.js 官方下载 glibc 版本,转换为 musl
|
||||
# 使用 patchelf 修改 node 二进制的 ELF interpreter 和 rpath,
|
||||
# 使其直接使用打包的 musl 链接器和共享库,无需 LD_LIBRARY_PATH。
|
||||
# 这样 process.execPath 返回正确的 node 路径,子进程 fork 也能正常工作。
|
||||
# 安装路径固定为 /opt/openclaw/node (与 openclaw-env 一致)。
|
||||
# ============================================================================
|
||||
set -e
|
||||
|
||||
INSTALL_PREFIX="/opt/openclaw/node"
|
||||
BUILD_MODE="${BUILD_MODE:-apk}"
|
||||
|
||||
apk add --no-cache nodejs npm xz icu-data-full patchelf
|
||||
echo "=== Node.js ARM64 musl Build ==="
|
||||
echo " Target version: v${NODE_VER}"
|
||||
echo " Build mode: ${BUILD_MODE}"
|
||||
|
||||
ACTUAL_VER=$(node --version | sed 's/^v//')
|
||||
echo "Alpine Node.js version: v${ACTUAL_VER} (requested: v${NODE_VER})"
|
||||
# ── apk 模式: 使用 Alpine 仓库的 Node.js ──
|
||||
build_apk() {
|
||||
echo ""
|
||||
echo "=== Building with Alpine apk mode ==="
|
||||
apk add --no-cache nodejs npm xz icu-data-full patchelf
|
||||
|
||||
# 使用实际版本号作为文件名 (Alpine apk 的 nodejs 版本可能与请求版本不同)
|
||||
if [ "$ACTUAL_VER" != "$NODE_VER" ]; then
|
||||
echo "WARNING: Actual version (${ACTUAL_VER}) differs from requested (${NODE_VER})"
|
||||
echo " Using actual version for package name"
|
||||
fi
|
||||
PKG_NAME="node-v${ACTUAL_VER}-linux-arm64-musl"
|
||||
PKG_DIR="/tmp/${PKG_NAME}"
|
||||
mkdir -p "${PKG_DIR}/bin" "${PKG_DIR}/lib/node_modules" "${PKG_DIR}/include/node"
|
||||
ACTUAL_VER=$(node --version | sed 's/^v//')
|
||||
echo "Alpine Node.js version: v${ACTUAL_VER} (requested: v${NODE_VER})"
|
||||
|
||||
# 复制 node 二进制
|
||||
cp "$(which node)" "${PKG_DIR}/bin/node"
|
||||
chmod +x "${PKG_DIR}/bin/node"
|
||||
# 使用实际版本号作为文件名 (Alpine apk 的 nodejs 版本可能与请求版本不同)
|
||||
if [ "$ACTUAL_VER" != "$NODE_VER" ]; then
|
||||
echo "WARNING: Actual version (${ACTUAL_VER}) differs from requested (${NODE_VER})"
|
||||
echo " Using actual version for package name"
|
||||
fi
|
||||
PKG_NAME="node-v${ACTUAL_VER}-linux-arm64-musl"
|
||||
PKG_DIR="/tmp/${PKG_NAME}"
|
||||
mkdir -p "${PKG_DIR}/bin" "${PKG_DIR}/lib/node_modules" "${PKG_DIR}/include/node"
|
||||
|
||||
# 收集 node 依赖的所有共享库 (Alpine node 是动态链接的)
|
||||
echo "=== Collecting shared libraries ==="
|
||||
LIB_DIR="${PKG_DIR}/lib"
|
||||
ldd "$(which node)" 2>/dev/null | while read -r line; do
|
||||
# 解析 ldd 输出: libxxx.so => /usr/lib/libxxx.so (0x...)
|
||||
lib_path=$(echo "$line" | grep -oE '/[^ ]+\.so[^ ]*' | head -1)
|
||||
if [ -n "$lib_path" ] && [ -f "$lib_path" ]; then
|
||||
cp -L "$lib_path" "$LIB_DIR/" 2>/dev/null || true
|
||||
echo " + $(basename "$lib_path")"
|
||||
fi
|
||||
done
|
||||
# 确保 musl 动态链接器也在
|
||||
if [ -f /lib/ld-musl-aarch64.so.1 ]; then
|
||||
cp -L /lib/ld-musl-aarch64.so.1 "$LIB_DIR/" 2>/dev/null || true
|
||||
echo " + ld-musl-aarch64.so.1"
|
||||
fi
|
||||
echo "Libraries collected: $(ls "$LIB_DIR"/*.so* 2>/dev/null | wc -l) files"
|
||||
# 复制 node 二进制
|
||||
cp "$(which node)" "${PKG_DIR}/bin/node"
|
||||
chmod +x "${PKG_DIR}/bin/node"
|
||||
|
||||
# 用 patchelf 修改 node 二进制:
|
||||
# - interpreter 指向打包的 musl 链接器 (绝对路径,对应安装后的位置)
|
||||
# - rpath 指向打包的 lib 目录
|
||||
echo "=== Patching ELF binary ==="
|
||||
patchelf --set-interpreter "${INSTALL_PREFIX}/lib/ld-musl-aarch64.so.1" "${PKG_DIR}/bin/node"
|
||||
patchelf --set-rpath "${INSTALL_PREFIX}/lib" "${PKG_DIR}/bin/node"
|
||||
echo " interpreter: ${INSTALL_PREFIX}/lib/ld-musl-aarch64.so.1"
|
||||
echo " rpath: ${INSTALL_PREFIX}/lib"
|
||||
# 收集 node 依赖的所有共享库 (Alpine node 是动态链接的)
|
||||
echo "=== Collecting shared libraries ==="
|
||||
LIB_DIR="${PKG_DIR}/lib"
|
||||
ldd "$(which node)" 2>/dev/null | while read -r line; do
|
||||
lib_path=$(echo "$line" | grep -oE '/[^ ]+\.so[^ ]*' | head -1)
|
||||
if [ -n "$lib_path" ] && [ -f "$lib_path" ]; then
|
||||
cp -L "$lib_path" "$LIB_DIR/" 2>/dev/null || true
|
||||
echo " + $(basename "$lib_path")"
|
||||
fi
|
||||
done
|
||||
# 确保 musl 动态链接器也在
|
||||
if [ -f /lib/ld-musl-aarch64.so.1 ]; then
|
||||
cp -L /lib/ld-musl-aarch64.so.1 "$LIB_DIR/" 2>/dev/null || true
|
||||
echo " + ld-musl-aarch64.so.1"
|
||||
fi
|
||||
echo "Libraries collected: $(ls "$LIB_DIR"/*.so* 2>/dev/null | wc -l) files"
|
||||
|
||||
# 复制 ICU 完整数据 (npm 的 Intl.Collator 需要)
|
||||
echo "=== Copying ICU data ==="
|
||||
ICU_DAT=$(find /usr/share/icu -name "icudt*.dat" 2>/dev/null | head -1)
|
||||
if [ -n "$ICU_DAT" ] && [ -f "$ICU_DAT" ]; then
|
||||
mkdir -p "${PKG_DIR}/share/icu"
|
||||
cp "$ICU_DAT" "${PKG_DIR}/share/icu/"
|
||||
echo " + $(basename "$ICU_DAT") ($(du -h "$ICU_DAT" | cut -f1))"
|
||||
else
|
||||
echo " WARNING: ICU data file not found"
|
||||
fi
|
||||
# 复制 ICU 完整数据
|
||||
echo "=== Copying ICU data ==="
|
||||
ICU_DAT=$(find /usr/share/icu -name "icudt*.dat" 2>/dev/null | head -1)
|
||||
if [ -n "$ICU_DAT" ] && [ -f "$ICU_DAT" ]; then
|
||||
mkdir -p "${PKG_DIR}/share/icu"
|
||||
cp "$ICU_DAT" "${PKG_DIR}/share/icu/"
|
||||
echo " + $(basename "$ICU_DAT") ($(du -h "$ICU_DAT" | cut -f1))"
|
||||
else
|
||||
echo " WARNING: ICU data file not found"
|
||||
fi
|
||||
|
||||
# 创建 node wrapper 脚本 (只设置 NODE_ICU_DATA,ELF 层面已解决链接器和库路径)
|
||||
cat > "${PKG_DIR}/bin/node-wrapper" << 'NODEWRAPPER'
|
||||
# 复制 npm
|
||||
if [ -d /usr/lib/node_modules/npm ]; then
|
||||
cp -r /usr/lib/node_modules/npm "${PKG_DIR}/lib/node_modules/"
|
||||
fi
|
||||
|
||||
# 返回包名供后续使用
|
||||
echo "PKG_NAME=${PKG_NAME}" >> /tmp/build_env
|
||||
echo "PKG_DIR=${PKG_DIR}" >> /tmp/build_env
|
||||
}
|
||||
|
||||
# ── cross 模式: 从 Node.js 官方 glibc 版本交叉编译 ──
|
||||
build_cross() {
|
||||
echo ""
|
||||
echo "=== Building with cross-compilation mode ==="
|
||||
apk add --no-cache xz patchelf curl wget ca-certificates
|
||||
|
||||
# 下载 Node.js 官方 ARM64 glibc 版本
|
||||
NODE_TARBALL="node-v${NODE_VER}-linux-arm64.tar.xz"
|
||||
NODE_URL="https://nodejs.org/dist/v${NODE_VER}/${NODE_TARBALL}"
|
||||
|
||||
echo "=== Downloading Node.js v${NODE_VER} ARM64 glibc ==="
|
||||
cd /tmp
|
||||
if ! curl -fSL -o "$NODE_TARBALL" "$NODE_URL"; then
|
||||
echo "ERROR: Failed to download Node.js from $NODE_URL"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 解压
|
||||
echo "=== Extracting Node.js ==="
|
||||
rm -rf "node-v${NODE_VER}-linux-arm64" 2>/dev/null || true
|
||||
tar xf "$NODE_TARBALL"
|
||||
SRC_DIR="node-v${NODE_VER}-linux-arm64"
|
||||
|
||||
# 创建输出目录
|
||||
PKG_NAME="node-v${NODE_VER}-linux-arm64-musl"
|
||||
PKG_DIR="/tmp/${PKG_NAME}"
|
||||
rm -rf "$PKG_DIR" 2>/dev/null || true
|
||||
mkdir -p "${PKG_DIR}/bin" "${PKG_DIR}/lib" "${PKG_DIR}/share/icu"
|
||||
|
||||
# 复制 node 二进制
|
||||
echo "=== Copying Node.js binary ==="
|
||||
cp "${SRC_DIR}/bin/node" "${PKG_DIR}/bin/node"
|
||||
chmod +x "${PKG_DIR}/bin/node"
|
||||
|
||||
# 复制 npm
|
||||
if [ -d "${SRC_DIR}/lib/node_modules/npm" ]; then
|
||||
mkdir -p "${PKG_DIR}/lib/node_modules"
|
||||
cp -r "${SRC_DIR}/lib/node_modules/npm" "${PKG_DIR}/lib/node_modules/"
|
||||
fi
|
||||
|
||||
# 复制 ICU 数据
|
||||
ICU_DAT=$(find "${SRC_DIR}" -name "icudt*.dat" 2>/dev/null | head -1)
|
||||
if [ -n "$ICU_DAT" ] && [ -f "$ICU_DAT" ]; then
|
||||
cp "$ICU_DAT" "${PKG_DIR}/share/icu/"
|
||||
echo " + ICU data: $(basename "$ICU_DAT")"
|
||||
fi
|
||||
|
||||
# ── 关键步骤: musl 库收集 ──
|
||||
echo "=== Converting to musl libc ==="
|
||||
|
||||
# 复制 musl 动态链接器
|
||||
if [ -f /lib/ld-musl-aarch64.so.1 ]; then
|
||||
cp -L /lib/ld-musl-aarch64.so.1 "${PKG_DIR}/lib/"
|
||||
echo " + ld-musl-aarch64.so.1"
|
||||
else
|
||||
echo "ERROR: musl dynamic linker not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 收集 musl 版本的依赖库
|
||||
# Node.js 官方 ARM64 版本依赖: libcrypto, libssl, libz, libstdc++, libgcc_s
|
||||
# 这些库需要从 Alpine (musl) 版本获取
|
||||
echo "=== Collecting musl libraries ==="
|
||||
LIB_DIR="${PKG_DIR}/lib"
|
||||
|
||||
# 安装必要的库包
|
||||
apk add --no-cache libcrypto3 libssl3 zlib libstdc++ libgcc
|
||||
|
||||
# 复制库文件
|
||||
for lib in libcrypto.so.3 libssl.so.3 libz.so.1 libstdc++.so.6 libgcc_s.so.1; do
|
||||
for libpath in /usr/lib/$lib /lib/$lib; do
|
||||
if [ -f "$libpath" ]; then
|
||||
cp -L "$libpath" "$LIB_DIR/" 2>/dev/null || true
|
||||
echo " + $lib"
|
||||
break
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
||||
# 收集所有 musl 库的依赖
|
||||
for lib in "$LIB_DIR"/*.so*; do
|
||||
[ -f "$lib" ] || continue
|
||||
ldd "$lib" 2>/dev/null | while read -r line; do
|
||||
lib_path=$(echo "$line" | grep -oE '/[^ ]+\.so[^ ]*' | head -1)
|
||||
if [ -n "$lib_path" ] && [ -f "$lib_path" ]; then
|
||||
lib_name=$(basename "$lib_path")
|
||||
[ -f "$LIB_DIR/$lib_name" ] || cp -L "$lib_path" "$LIB_DIR/" 2>/dev/null || true
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
||||
echo "Libraries collected: $(ls "$LIB_DIR"/*.so* 2>/dev/null | wc -l) files"
|
||||
|
||||
# 返回包名供后续使用
|
||||
echo "PKG_NAME=${PKG_NAME}" >> /tmp/build_env
|
||||
echo "PKG_DIR=${PKG_DIR}" >> /tmp/build_env
|
||||
}
|
||||
|
||||
# ── 公共步骤: patchelf 和打包 ──
|
||||
finalize_package() {
|
||||
. /tmp/build_env
|
||||
|
||||
# 用 patchelf 修改 node 二进制
|
||||
echo "=== Patching ELF binary ==="
|
||||
patchelf --set-interpreter "${INSTALL_PREFIX}/lib/ld-musl-aarch64.so.1" "${PKG_DIR}/bin/node"
|
||||
patchelf --set-rpath "${INSTALL_PREFIX}/lib" "${PKG_DIR}/bin/node"
|
||||
echo " interpreter: ${INSTALL_PREFIX}/lib/ld-musl-aarch64.so.1"
|
||||
echo " rpath: ${INSTALL_PREFIX}/lib"
|
||||
|
||||
# 创建 node wrapper 脚本
|
||||
cat > "${PKG_DIR}/bin/node-wrapper" << 'NODEWRAPPER'
|
||||
#!/bin/sh
|
||||
SELF_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
export NODE_ICU_DATA="${SELF_DIR}/../share/icu"
|
||||
exec "${SELF_DIR}/node" "$@"
|
||||
NODEWRAPPER
|
||||
chmod +x "${PKG_DIR}/bin/node-wrapper"
|
||||
chmod +x "${PKG_DIR}/bin/node-wrapper"
|
||||
|
||||
# 复制 npm
|
||||
if [ -d /usr/lib/node_modules/npm ]; then
|
||||
cp -r /usr/lib/node_modules/npm "${PKG_DIR}/lib/node_modules/"
|
||||
fi
|
||||
|
||||
# 创建 npm wrapper (直接调用 patchelf 后的 node,只需设置 ICU)
|
||||
cat > "${PKG_DIR}/bin/npm" << 'NPMWRAPPER'
|
||||
# 创建 npm wrapper
|
||||
cat > "${PKG_DIR}/bin/npm" << 'NPMWRAPPER'
|
||||
#!/bin/sh
|
||||
SELF_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
export NODE_ICU_DATA="${SELF_DIR}/../share/icu"
|
||||
exec "${SELF_DIR}/node" "${SELF_DIR}/../lib/node_modules/npm/bin/npm-cli.js" "$@"
|
||||
NPMWRAPPER
|
||||
# 创建 npx wrapper
|
||||
cat > "${PKG_DIR}/bin/npx" << 'NPXWRAPPER'
|
||||
|
||||
# 创建 npx wrapper
|
||||
cat > "${PKG_DIR}/bin/npx" << 'NPXWRAPPER'
|
||||
#!/bin/sh
|
||||
SELF_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
export NODE_ICU_DATA="${SELF_DIR}/../share/icu"
|
||||
exec "${SELF_DIR}/node" "${SELF_DIR}/../lib/node_modules/npm/bin/npx-cli.js" "$@"
|
||||
NPXWRAPPER
|
||||
chmod +x "${PKG_DIR}/bin/npm" "${PKG_DIR}/bin/npx"
|
||||
chmod +x "${PKG_DIR}/bin/npm" "${PKG_DIR}/bin/npx"
|
||||
|
||||
# 验证 (需要将打包内容放到目标路径来测试 patchelf 结果)
|
||||
echo "=== Verification ==="
|
||||
mkdir -p "${INSTALL_PREFIX}"
|
||||
cp -a "${PKG_DIR}"/* "${INSTALL_PREFIX}/"
|
||||
"${INSTALL_PREFIX}/bin/node" --version
|
||||
"${INSTALL_PREFIX}/bin/node" -e "console.log('execPath:', process.execPath)"
|
||||
"${INSTALL_PREFIX}/bin/node" -e "console.log(process.arch, process.platform, process.versions.modules)"
|
||||
NODE_ICU_DATA="${INSTALL_PREFIX}/share/icu" "${INSTALL_PREFIX}/bin/npm" --version 2>/dev/null || echo "npm needs ICU data"
|
||||
rm -rf "${INSTALL_PREFIX}"
|
||||
# 验证
|
||||
echo "=== Verification ==="
|
||||
mkdir -p "${INSTALL_PREFIX}"
|
||||
cp -a "${PKG_DIR}"/* "${INSTALL_PREFIX}/"
|
||||
|
||||
# 设置库路径并测试
|
||||
export LD_LIBRARY_PATH="${INSTALL_PREFIX}/lib"
|
||||
|
||||
"${INSTALL_PREFIX}/bin/node" --version
|
||||
"${INSTALL_PREFIX}/bin/node" -e "console.log('execPath:', process.execPath)"
|
||||
"${INSTALL_PREFIX}/bin/node" -e "console.log(process.arch, process.platform, process.versions.modules)"
|
||||
NODE_ICU_DATA="${INSTALL_PREFIX}/share/icu" "${INSTALL_PREFIX}/bin/npm" --version 2>/dev/null || echo "npm needs ICU data"
|
||||
|
||||
rm -rf "${INSTALL_PREFIX}"
|
||||
|
||||
# 打包
|
||||
cd /tmp
|
||||
tar cJf "/output/${PKG_NAME}.tar.xz" "${PKG_NAME}"
|
||||
ls -lh "/output/${PKG_NAME}.tar.xz"
|
||||
echo "=== Done: ${PKG_NAME}.tar.xz ==="
|
||||
# 打包
|
||||
echo "=== Creating tarball ==="
|
||||
cd /tmp
|
||||
tar cJf "/output/${PKG_NAME}.tar.xz" "${PKG_NAME}"
|
||||
ls -lh "/output/${PKG_NAME}.tar.xz"
|
||||
echo "=== Done: ${PKG_NAME}.tar.xz ==="
|
||||
}
|
||||
|
||||
# ── 主入口 ──
|
||||
rm -f /tmp/build_env
|
||||
|
||||
case "$BUILD_MODE" in
|
||||
apk)
|
||||
build_apk
|
||||
;;
|
||||
cross)
|
||||
build_cross
|
||||
;;
|
||||
*)
|
||||
echo "ERROR: Unknown BUILD_MODE: $BUILD_MODE"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
finalize_package
|
||||
|
||||
@@ -41,6 +41,11 @@ mkdir -p "$DATA_DIR/etc/init.d"
|
||||
cp "$PKG_DIR/root/etc/init.d/openclaw" "$DATA_DIR/etc/init.d/"
|
||||
chmod +x "$DATA_DIR/etc/init.d/openclaw"
|
||||
|
||||
# profile.d (v1.0.16+: 全局环境变量)
|
||||
mkdir -p "$DATA_DIR/etc/profile.d"
|
||||
cp "$PKG_DIR/root/etc/profile.d/openclaw.sh" "$DATA_DIR/etc/profile.d/"
|
||||
chmod +x "$DATA_DIR/etc/profile.d/openclaw.sh"
|
||||
|
||||
# bin
|
||||
mkdir -p "$DATA_DIR/usr/bin"
|
||||
cp "$PKG_DIR/root/usr/bin/openclaw-env" "$DATA_DIR/usr/bin/"
|
||||
@@ -68,6 +73,11 @@ cp "$PKG_DIR/root/usr/share/openclaw/web-pty.js" "$DATA_DIR/usr/share/openclaw/"
|
||||
# Web PTY UI
|
||||
cp -r "$PKG_DIR/root/usr/share/openclaw/ui" "$DATA_DIR/usr/share/openclaw/"
|
||||
|
||||
# profile.d 环境变量脚本 (v1.0.16+)
|
||||
mkdir -p "$DATA_DIR/etc/profile.d"
|
||||
cp "$PKG_DIR/root/etc/profile.d/openclaw.sh" "$DATA_DIR/etc/profile.d/"
|
||||
chmod +x "$DATA_DIR/etc/profile.d/openclaw.sh"
|
||||
|
||||
# i18n (po2lmo 可选)
|
||||
mkdir -p "$DATA_DIR/usr/lib/lua/luci/i18n"
|
||||
if command -v po2lmo >/dev/null 2>&1 && [ -f "$PKG_DIR/po/zh-cn/openclaw.po" ]; then
|
||||
@@ -86,7 +96,7 @@ mkdir -p "$CTRL_DIR"
|
||||
cat > "$CTRL_DIR/control" << EOF
|
||||
Package: ${PKG_NAME}
|
||||
Version: ${PKG_VERSION}-${PKG_RELEASE}
|
||||
Depends: luci-compat, luci-base, curl, openssl-util, script-utils, tar
|
||||
Depends: luci-compat, luci-base, curl, openssl-util, script-utils, tar, libstdcpp6
|
||||
Source: https://github.com/10000ge10000/luci-app-openclaw
|
||||
SourceName: ${PKG_NAME}
|
||||
License: GPL-3.0
|
||||
@@ -101,20 +111,84 @@ EOF
|
||||
cat > "$CTRL_DIR/postinst" << 'EOF'
|
||||
#!/bin/sh
|
||||
[ -n "${IPKG_INSTROOT}" ] || {
|
||||
( . /etc/uci-defaults/99-openclaw ) && rm -f /etc/uci-defaults/99-openclaw
|
||||
# ══════════════════════════════════════════════════════════════
|
||||
# 配置文件冲突处理 (opkg 将新配置保存为 .opkg 后缀)
|
||||
# ══════════════════════════════════════════════════════════════
|
||||
# opkg 配置文件冲突处理流程:
|
||||
# 1. opkg 检测到 /etc/config/openclaw 已存在且内容不同
|
||||
# 2. opkg 保留旧配置,将新配置保存为 /etc/config/openclaw-opkg
|
||||
# 3. postinst 需要合并用户配置到新配置文件
|
||||
|
||||
OLD_CONFIG="/etc/config/openclaw"
|
||||
NEW_CONFIG="/etc/config/openclaw-opkg"
|
||||
|
||||
if [ -f "$NEW_CONFIG" ]; then
|
||||
echo "检测到配置文件冲突,正在智能合并..."
|
||||
|
||||
# 步骤1: 从旧配置中提取用户设置 (在替换之前!)
|
||||
# 使用 sed 直接解析 UCI 格式,不依赖 uci 命令
|
||||
USER_ENABLED=$(sed -n "s/^\s*option\s\+enabled\s\+['\"]\\?\\([^'\"]*\\)['\"]\\?.*/\\1/p" "$OLD_CONFIG" 2>/dev/null | tail -1)
|
||||
USER_PORT=$(sed -n "s/^\s*option\s\+port\s\+['\"]\\?\\([^'\"]*\\)['\"]\\?.*/\\1/p" "$OLD_CONFIG" 2>/dev/null | tail -1)
|
||||
USER_BIND=$(sed -n "s/^\s*option\s\+bind\s\+['\"]\\?\\([^'\"]*\\)['\"]\\?.*/\\1/p" "$OLD_CONFIG" 2>/dev/null | tail -1)
|
||||
USER_TOKEN=$(sed -n "s/^\s*option\s\+token\s\+['\"]\\?\\([^'\"]*\\)['\"]\\?.*/\\1/p" "$OLD_CONFIG" 2>/dev/null | tail -1)
|
||||
USER_PTY_PORT=$(sed -n "s/^\s*option\s\+pty_port\s\+['\"]\\?\\([^'\"]*\\)['\"]\\?.*/\\1/p" "$OLD_CONFIG" 2>/dev/null | tail -1)
|
||||
|
||||
# 步骤2: 备份旧配置 (带时间戳)
|
||||
BAK_FILE="/etc/config/openclaw.$(date +%Y%m%d%H%M%S).bak"
|
||||
cp "$OLD_CONFIG" "$BAK_FILE" 2>/dev/null || true
|
||||
echo "旧配置已备份到: $BAK_FILE"
|
||||
|
||||
# 步骤3: 使用新配置文件
|
||||
mv "$NEW_CONFIG" "$OLD_CONFIG" 2>/dev/null || cp "$NEW_CONFIG" "$OLD_CONFIG" 2>/dev/null || true
|
||||
rm -f "$NEW_CONFIG" 2>/dev/null || true
|
||||
|
||||
# 步骤4: 合并用户设置到新配置
|
||||
# 直接使用 sed 修改配置文件,兼容性更好
|
||||
[ -n "$USER_ENABLED" ] && sed -i "s/^\(\s*option\s\+enabled\s\+\).*/\\1'$USER_ENABLED'/" "$OLD_CONFIG" 2>/dev/null || true
|
||||
[ -n "$USER_PORT" ] && sed -i "s/^\(\s*option\s\+port\s\+\).*/\\1'$USER_PORT'/" "$OLD_CONFIG" 2>/dev/null || true
|
||||
[ -n "$USER_BIND" ] && sed -i "s/^\(\s*option\s\+bind\s\+\).*/\\1'$USER_BIND'/" "$OLD_CONFIG" 2>/dev/null || true
|
||||
[ -n "$USER_TOKEN" ] && sed -i "s/^\(\s*option\s\+token\s\+\).*/\\1'$USER_TOKEN'/" "$OLD_CONFIG" 2>/dev/null || true
|
||||
[ -n "$USER_PTY_PORT" ] && sed -i "s/^\(\s*option\s\+pty_port\s\+\).*/\\1'$USER_PTY_PORT'/" "$OLD_CONFIG" 2>/dev/null || true
|
||||
|
||||
echo "配置合并完成,用户设置已保留"
|
||||
fi
|
||||
|
||||
# 执行 uci-defaults 初始化脚本
|
||||
if [ -f /etc/uci-defaults/99-openclaw ]; then
|
||||
( . /etc/uci-defaults/99-openclaw ) && rm -f /etc/uci-defaults/99-openclaw
|
||||
fi
|
||||
|
||||
# 清理 LuCI 缓存
|
||||
rm -f /tmp/luci-indexcache /tmp/luci-modulecache/* /tmp/luci-indexcache.*.json 2>/dev/null
|
||||
|
||||
# 重启 Web PTY (使其加载新文件和新 token)
|
||||
PTY_PID=$(pgrep -f 'web-pty.js' 2>/dev/null | head -1)
|
||||
[ -n "$PTY_PID" ] && kill "$PTY_PID" 2>/dev/null || true
|
||||
|
||||
exit 0
|
||||
}
|
||||
EOF
|
||||
chmod +x "$CTRL_DIR/postinst"
|
||||
|
||||
cat > "$CTRL_DIR/prerm" << 'EOF'
|
||||
#!/bin/sh
|
||||
[ -n "${IPKG_INSTROOT}" ] || {
|
||||
# 升级前备份当前配置
|
||||
if [ -f /etc/config/openclaw ]; then
|
||||
cp /etc/config/openclaw /etc/config/openclaw.pre-upgrade.bak 2>/dev/null || true
|
||||
fi
|
||||
}
|
||||
EOF
|
||||
chmod +x "$CTRL_DIR/prerm"
|
||||
|
||||
cat > "$CTRL_DIR/postrm" << 'EOF'
|
||||
#!/bin/sh
|
||||
[ -n "${IPKG_INSTROOT}" ] || {
|
||||
rm -f /tmp/luci-indexcache /tmp/luci-modulecache/* 2>/dev/null
|
||||
# 清理备份文件 (仅在完全卸载时)
|
||||
if [ "$1" = "0" ]; then
|
||||
rm -f /etc/config/openclaw.user.bak /etc/config/openclaw.pre-upgrade.bak 2>/dev/null
|
||||
fi
|
||||
}
|
||||
EOF
|
||||
chmod +x "$CTRL_DIR/postrm"
|
||||
@@ -145,3 +219,8 @@ echo "文件大小: ${IPK_SIZE} bytes"
|
||||
echo "安装大小: ${INSTALLED_SIZE} KB"
|
||||
echo ""
|
||||
echo "安装方法: opkg install ${PKG_NAME}_${PKG_VERSION}-${PKG_RELEASE}_all.ipk"
|
||||
|
||||
# ── 同步构建 .run 包 ──
|
||||
echo ""
|
||||
echo "=== 同步构建 .run 包 ==="
|
||||
"$SCRIPT_DIR/build_run.sh" "$OUT_DIR"
|
||||
|
||||
@@ -44,6 +44,11 @@ install_files() {
|
||||
cp "$PKG_DIR/root/etc/init.d/openclaw" "$dest/etc/init.d/"
|
||||
chmod +x "$dest/etc/init.d/openclaw"
|
||||
|
||||
# profile.d (v1.0.16+: 全局环境变量)
|
||||
mkdir -p "$dest/etc/profile.d"
|
||||
cp "$PKG_DIR/root/etc/profile.d/openclaw.sh" "$dest/etc/profile.d/"
|
||||
chmod +x "$dest/etc/profile.d/openclaw.sh"
|
||||
|
||||
# bin
|
||||
mkdir -p "$dest/usr/bin"
|
||||
cp "$PKG_DIR/root/usr/bin/openclaw-env" "$dest/usr/bin/"
|
||||
@@ -134,7 +139,7 @@ mkdir -p "$INFO_DIR"
|
||||
cat > "$INFO_DIR/$PKG.control" << CTLEOF
|
||||
Package: $PKG
|
||||
Version: $PKG_VER
|
||||
Depends: luci-compat, luci-base, curl, openssl-util, script-utils, tar
|
||||
Depends: luci-compat, luci-base, curl, openssl-util, script-utils, tar, libstdcpp6
|
||||
Section: luci
|
||||
Architecture: all
|
||||
Installed-Size: 0
|
||||
@@ -170,7 +175,7 @@ cat >> "$STATUS_FILE" << STEOF
|
||||
|
||||
Package: $PKG
|
||||
Version: $PKG_VER
|
||||
Depends: luci-compat, luci-base, curl, openssl-util, script-utils, tar
|
||||
Depends: luci-compat, luci-base, curl, openssl-util, script-utils, tar, libstdcpp6
|
||||
Status: install user installed
|
||||
Architecture: all
|
||||
Conffiles:
|
||||
|
||||
Reference in New Issue
Block a user