mirror of
https://github.com/hotwa/luci-app-openclaw.git
synced 2026-03-31 04:52:33 +00:00
release: v1.0.6 — Docker OverlayFS 兼容性修复
This commit is contained in:
12
CHANGELOG.md
12
CHANGELOG.md
@@ -4,6 +4,18 @@
|
|||||||
|
|
||||||
格式基于 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.0.0/)。
|
格式基于 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.0.0/)。
|
||||||
|
|
||||||
|
## [1.0.6] - 2026-03-06
|
||||||
|
|
||||||
|
### 修复 Docker 环境下安装失败 "mkdir: can't create directory: Directory not empty"
|
||||||
|
|
||||||
|
#### 修复
|
||||||
|
- **OverlayFS 兼容性**: iStoreOS/OpenWrt 安装 Docker 后,Docker 的 bind mount (`/overlay/upper/opt/docker`) 导致 OverlayFS 合并视图中 `/opt` 目录完全不可写,所有 `mkdir`/`touch`/`ln` 操作均报 "Directory not empty"
|
||||||
|
- 新增 `_oc_fix_opt()` 检测函数,自动检测 `/opt` 是否可写
|
||||||
|
- 不可写时自动执行 `mount --bind /overlay/upper/opt /opt` 绕过 OverlayFS 冲突
|
||||||
|
- 三重保障: `uci-defaults` (首次安装)、`init.d` (每次开机)、`openclaw-env` (手动操作) 均包含修复逻辑
|
||||||
|
- 正常系统 (无 Docker) 不受影响,检测到可写后直接跳过
|
||||||
|
- **openclaw-env**: 新增 `ensure_mkdir()` 安全目录创建函数,替代所有裸 `mkdir -p` 调用
|
||||||
|
|
||||||
## [1.0.5] - 2026-03-05
|
## [1.0.5] - 2026-03-05
|
||||||
|
|
||||||
### 修复配置管理页面 "spawn script ENOENT" 启动失败 (#3, #4)
|
### 修复配置管理页面 "spawn script ENOENT" 启动失败 (#3, #4)
|
||||||
|
|||||||
@@ -13,6 +13,20 @@ EXTRA_HELP=" setup 下载 Node.js 并安装 OpenClaw
|
|||||||
NODE_BASE="/opt/openclaw/node"
|
NODE_BASE="/opt/openclaw/node"
|
||||||
OC_GLOBAL="/opt/openclaw/global"
|
OC_GLOBAL="/opt/openclaw/global"
|
||||||
OC_DATA="/opt/openclaw/data"
|
OC_DATA="/opt/openclaw/data"
|
||||||
|
|
||||||
|
# ── OverlayFS 兼容性修复 ──
|
||||||
|
# Docker bind mount (/overlay/upper/opt/docker) 会导致 /opt 不可写
|
||||||
|
# 解决: bind mount upper 层的 /opt 到合并视图的 /opt
|
||||||
|
_oc_fix_opt() {
|
||||||
|
mkdir -p /opt/openclaw/.probe 2>/dev/null && { rmdir /opt/openclaw/.probe 2>/dev/null; return 0; }
|
||||||
|
if [ -d /overlay/upper/opt ]; then
|
||||||
|
mkdir -p /overlay/upper/opt/openclaw 2>/dev/null
|
||||||
|
mount --bind /overlay/upper/opt /opt 2>/dev/null && return 0
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
_oc_fix_opt
|
||||||
|
|
||||||
NODE_BIN="${NODE_BASE}/bin/node"
|
NODE_BIN="${NODE_BASE}/bin/node"
|
||||||
CONFIG_FILE="${OC_DATA}/.openclaw/openclaw.json"
|
CONFIG_FILE="${OC_DATA}/.openclaw/openclaw.json"
|
||||||
|
|
||||||
@@ -64,8 +78,8 @@ port=$(uci -q get openclaw.main.port || echo "18789")
|
|||||||
bind=$(uci -q get openclaw.main.bind || echo "lan")
|
bind=$(uci -q get openclaw.main.bind || echo "lan")
|
||||||
token=$(uci -q get openclaw.main.token || echo "")
|
token=$(uci -q get openclaw.main.token || echo "")
|
||||||
|
|
||||||
# 确保配置目录和文件存在
|
# 确保配置目录和文件存在 (路径已在脚本开头做 OverlayFS 重映射)
|
||||||
mkdir -p "$(dirname "$CONFIG_FILE")"
|
mkdir -p "$(dirname "$CONFIG_FILE")" 2>/dev/null
|
||||||
if [ ! -f "$CONFIG_FILE" ]; then
|
if [ ! -f "$CONFIG_FILE" ]; then
|
||||||
echo '{}' > "$CONFIG_FILE"
|
echo '{}' > "$CONFIG_FILE"
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -25,6 +25,16 @@ if ! id openclaw >/dev/null 2>&1; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# 创建数据目录
|
# 创建数据目录
|
||||||
|
# ── OverlayFS 兼容: Docker bind mount 可能导致 /opt 不可写 ──
|
||||||
|
if ! mkdir -p /opt/openclaw/.probe 2>/dev/null; then
|
||||||
|
if [ -d /overlay/upper/opt ]; then
|
||||||
|
mkdir -p /overlay/upper/opt/openclaw 2>/dev/null
|
||||||
|
mount --bind /overlay/upper/opt /opt 2>/dev/null
|
||||||
|
fi
|
||||||
|
rmdir /opt/openclaw/.probe 2>/dev/null
|
||||||
|
else
|
||||||
|
rmdir /opt/openclaw/.probe 2>/dev/null
|
||||||
|
fi
|
||||||
mkdir -p /opt/openclaw/data/.openclaw
|
mkdir -p /opt/openclaw/data/.openclaw
|
||||||
mkdir -p /opt/openclaw/node
|
mkdir -p /opt/openclaw/node
|
||||||
mkdir -p /opt/openclaw/global
|
mkdir -p /opt/openclaw/global
|
||||||
|
|||||||
@@ -19,6 +19,28 @@ OC_VERSION="${OC_VERSION:-}"
|
|||||||
NODE_BASE="/opt/openclaw/node"
|
NODE_BASE="/opt/openclaw/node"
|
||||||
OC_GLOBAL="/opt/openclaw/global"
|
OC_GLOBAL="/opt/openclaw/global"
|
||||||
OC_DATA="/opt/openclaw/data"
|
OC_DATA="/opt/openclaw/data"
|
||||||
|
|
||||||
|
# ── OverlayFS 兼容性修复 ──
|
||||||
|
# iStoreOS/OpenWrt 上 Docker 的 bind mount (/overlay/upper/opt/docker)
|
||||||
|
# 会导致 OverlayFS 合并视图中 /opt 完全不可写 (mkdir 报 "Directory not empty")。
|
||||||
|
# 解决方案: 将 /overlay/upper/opt bind mount 到 /opt,绕过 OverlayFS 冲突。
|
||||||
|
_oc_fix_opt() {
|
||||||
|
# 如果 /opt 可正常写入,无需修复
|
||||||
|
if mkdir -p /opt/openclaw/.probe 2>/dev/null; then
|
||||||
|
rmdir /opt/openclaw/.probe 2>/dev/null
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
# /opt 不可写且 overlay upper 层存在 — 执行 bind mount 修复
|
||||||
|
if [ -d /overlay/upper/opt ]; then
|
||||||
|
# 确保 upper 层有 openclaw 目录
|
||||||
|
mkdir -p /overlay/upper/opt/openclaw 2>/dev/null
|
||||||
|
# 绑定挂载 upper 层的 /opt 到合并视图的 /opt
|
||||||
|
mount --bind /overlay/upper/opt /opt 2>/dev/null && return 0
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
_oc_fix_opt
|
||||||
|
|
||||||
NODE_BIN="${NODE_BASE}/bin/node"
|
NODE_BIN="${NODE_BASE}/bin/node"
|
||||||
NPM_BIN="${NODE_BASE}/bin/npm"
|
NPM_BIN="${NODE_BASE}/bin/npm"
|
||||||
PNPM_BIN="${OC_GLOBAL}/bin/pnpm"
|
PNPM_BIN="${OC_GLOBAL}/bin/pnpm"
|
||||||
@@ -38,6 +60,17 @@ log_info() { echo " [✓] $1"; }
|
|||||||
log_warn() { echo " [!] $1"; }
|
log_warn() { echo " [!] $1"; }
|
||||||
log_error() { echo " [✗] $1"; }
|
log_error() { echo " [✗] $1"; }
|
||||||
|
|
||||||
|
# 安全创建目录 (会在 _oc_fix_opt 修复 /opt 后使用标准路径)
|
||||||
|
ensure_mkdir() {
|
||||||
|
local target="$1"
|
||||||
|
[ -d "$target" ] && return 0
|
||||||
|
if ! mkdir -p "$target" 2>/dev/null; then
|
||||||
|
log_error "无法创建目录: $target"
|
||||||
|
log_error "如果安装了 Docker,可能需要手动执行: mount --bind /overlay/upper/opt /opt"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
# 检测 C 运行时类型 (glibc vs musl)
|
# 检测 C 运行时类型 (glibc vs musl)
|
||||||
detect_libc() {
|
detect_libc() {
|
||||||
if ldd --version 2>&1 | grep -qi musl; then
|
if ldd --version 2>&1 | grep -qi musl; then
|
||||||
@@ -184,8 +217,13 @@ download_node() {
|
|||||||
|
|
||||||
# 解压
|
# 解压
|
||||||
echo " 正在解压到 ${NODE_BASE}..."
|
echo " 正在解压到 ${NODE_BASE}..."
|
||||||
rm -rf "$NODE_BASE"
|
# OverlayFS 兼容: rm -rf 后可能因 whiteout 导致 mkdir 失败
|
||||||
mkdir -p "$NODE_BASE"
|
# 先尝试常规方式,失败则通过 overlay upper 层操作
|
||||||
|
rm -rf "$NODE_BASE" 2>/dev/null
|
||||||
|
if [ -d /overlay/upper ]; then
|
||||||
|
rm -rf "/overlay/upper${NODE_BASE}" 2>/dev/null
|
||||||
|
fi
|
||||||
|
ensure_mkdir "$NODE_BASE"
|
||||||
tar xf "$tmp_file" -C "$NODE_BASE" --strip-components=1
|
tar xf "$tmp_file" -C "$NODE_BASE" --strip-components=1
|
||||||
rm -f "$tmp_file"
|
rm -f "$tmp_file"
|
||||||
|
|
||||||
@@ -210,7 +248,7 @@ install_pnpm() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# 使用 npm 安装 pnpm 到全局目录
|
# 使用 npm 安装 pnpm 到全局目录
|
||||||
mkdir -p "$OC_GLOBAL"
|
ensure_mkdir "$OC_GLOBAL"
|
||||||
"$NPM_BIN" install -g pnpm --prefix="$OC_GLOBAL" 2>/dev/null
|
"$NPM_BIN" install -g pnpm --prefix="$OC_GLOBAL" 2>/dev/null
|
||||||
|
|
||||||
if [ -x "$OC_GLOBAL/bin/pnpm" ]; then
|
if [ -x "$OC_GLOBAL/bin/pnpm" ]; then
|
||||||
@@ -264,7 +302,7 @@ install_openclaw() {
|
|||||||
# 优先用 npm 安装 (pnpm 在 musl 上全局安装可能有路径问题)
|
# 优先用 npm 安装 (pnpm 在 musl 上全局安装可能有路径问题)
|
||||||
local npm_ok=0
|
local npm_ok=0
|
||||||
if [ -x "$NPM_BIN" ]; then
|
if [ -x "$NPM_BIN" ]; then
|
||||||
mkdir -p "$OC_GLOBAL"
|
ensure_mkdir "$OC_GLOBAL"
|
||||||
"$NPM_BIN" install -g "$oc_pkg" --prefix="$OC_GLOBAL" $install_flags 2>&1 | tail -10
|
"$NPM_BIN" install -g "$oc_pkg" --prefix="$OC_GLOBAL" $install_flags 2>&1 | tail -10
|
||||||
# 检查是否安装成功
|
# 检查是否安装成功
|
||||||
if [ -n "$(find_oc_entry)" ]; then
|
if [ -n "$(find_oc_entry)" ]; then
|
||||||
@@ -275,7 +313,7 @@ install_openclaw() {
|
|||||||
[ -n "$(find_oc_entry)" ] && npm_ok=1
|
[ -n "$(find_oc_entry)" ] && npm_ok=1
|
||||||
fi
|
fi
|
||||||
elif [ -x "$PNPM_BIN" ]; then
|
elif [ -x "$PNPM_BIN" ]; then
|
||||||
mkdir -p "$OC_GLOBAL"
|
ensure_mkdir "$OC_GLOBAL"
|
||||||
"$PNPM_BIN" install -g "$oc_pkg" --prefix="$OC_GLOBAL" 2>&1 | tail -5
|
"$PNPM_BIN" install -g "$oc_pkg" --prefix="$OC_GLOBAL" 2>&1 | tail -5
|
||||||
else
|
else
|
||||||
log_error "npm 和 pnpm 均不可用"
|
log_error "npm 和 pnpm 均不可用"
|
||||||
@@ -315,7 +353,7 @@ init_openclaw() {
|
|||||||
echo "=== 初始化 OpenClaw ==="
|
echo "=== 初始化 OpenClaw ==="
|
||||||
|
|
||||||
# 创建数据目录
|
# 创建数据目录
|
||||||
mkdir -p "$OC_DATA/.openclaw"
|
ensure_mkdir "$OC_DATA/.openclaw"
|
||||||
|
|
||||||
# 运行 onboard
|
# 运行 onboard
|
||||||
local oc_entry=""
|
local oc_entry=""
|
||||||
@@ -516,7 +554,7 @@ do_factory_reset() {
|
|||||||
if [ -f "$config_file" ]; then
|
if [ -f "$config_file" ]; then
|
||||||
local backup_dir="${config_dir}/backups"
|
local backup_dir="${config_dir}/backups"
|
||||||
local backup_ts=$(date +%Y%m%d_%H%M%S)
|
local backup_ts=$(date +%Y%m%d_%H%M%S)
|
||||||
mkdir -p "$backup_dir"
|
ensure_mkdir "$backup_dir"
|
||||||
cp "$config_file" "${backup_dir}/openclaw_${backup_ts}.json"
|
cp "$config_file" "${backup_dir}/openclaw_${backup_ts}.json"
|
||||||
log_info "备份已保存: backups/openclaw_${backup_ts}.json"
|
log_info "备份已保存: backups/openclaw_${backup_ts}.json"
|
||||||
fi
|
fi
|
||||||
|
|||||||
Reference in New Issue
Block a user