mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 06:00:43 +00:00
refactor(test): share bundled channel Docker helpers
This commit is contained in:
@@ -21,6 +21,7 @@ run_channel_scenario() {
|
||||
set -euo pipefail
|
||||
|
||||
source scripts/lib/openclaw-e2e-instance.sh
|
||||
source scripts/e2e/lib/bundled-channel/common.sh
|
||||
openclaw_e2e_eval_test_state_from_b64 "${OPENCLAW_TEST_STATE_SCRIPT_B64:?missing OPENCLAW_TEST_STATE_SCRIPT_B64}"
|
||||
export NPM_CONFIG_PREFIX="$HOME/.npm-global"
|
||||
export PATH="$NPM_CONFIG_PREFIX/bin:$PATH"
|
||||
@@ -79,154 +80,12 @@ test -d "$package_root/dist/extensions/slack"
|
||||
test -d "$package_root/dist/extensions/feishu"
|
||||
test -d "$package_root/dist/extensions/memory-lancedb"
|
||||
|
||||
stage_root() {
|
||||
printf "%s/.openclaw/plugin-runtime-deps" "$HOME"
|
||||
}
|
||||
|
||||
find_external_dep_package() {
|
||||
local dep_path="$1"
|
||||
find "$(stage_root)" -maxdepth 12 -path "*/node_modules/$dep_path/package.json" -type f -print -quit 2>/dev/null || true
|
||||
}
|
||||
|
||||
assert_package_dep_absent() {
|
||||
local channel="$1"
|
||||
local dep_path="$2"
|
||||
for candidate in \
|
||||
"$package_root/dist/extensions/$channel/node_modules/$dep_path/package.json" \
|
||||
"$package_root/dist/extensions/node_modules/$dep_path/package.json" \
|
||||
"$package_root/node_modules/$dep_path/package.json"; do
|
||||
if [ -f "$candidate" ]; then
|
||||
echo "packaged install should not mutate package tree for $channel: $candidate" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
if [ -d "$package_root/dist/extensions/$CHANNEL/node_modules" ]; then
|
||||
echo "$CHANNEL runtime deps should not be preinstalled in package" >&2
|
||||
find "$package_root/dist/extensions/$CHANNEL/node_modules" -maxdepth 2 -type f | head -20 >&2 || true
|
||||
exit 1
|
||||
fi
|
||||
|
||||
write_config() {
|
||||
local mode="$1"
|
||||
node - <<'NODE' "$mode" "$TOKEN" "$PORT"
|
||||
const fs = require("node:fs");
|
||||
const path = require("node:path");
|
||||
|
||||
const mode = process.argv[2];
|
||||
const token = process.argv[3];
|
||||
const port = Number(process.argv[4]);
|
||||
const configPath = path.join(process.env.HOME, ".openclaw", "openclaw.json");
|
||||
const config = fs.existsSync(configPath)
|
||||
? JSON.parse(fs.readFileSync(configPath, "utf8"))
|
||||
: {};
|
||||
|
||||
config.gateway = {
|
||||
...(config.gateway || {}),
|
||||
port,
|
||||
auth: { mode: "token", token },
|
||||
controlUi: { enabled: false },
|
||||
};
|
||||
config.agents = {
|
||||
...(config.agents || {}),
|
||||
defaults: {
|
||||
...(config.agents?.defaults || {}),
|
||||
model: { primary: "openai/gpt-4.1-mini" },
|
||||
},
|
||||
};
|
||||
config.models = {
|
||||
...(config.models || {}),
|
||||
providers: {
|
||||
...(config.models?.providers || {}),
|
||||
openai: {
|
||||
...(config.models?.providers?.openai || {}),
|
||||
apiKey: process.env.OPENAI_API_KEY,
|
||||
baseUrl: "https://api.openai.com/v1",
|
||||
models: [],
|
||||
},
|
||||
},
|
||||
};
|
||||
config.plugins = {
|
||||
...(config.plugins || {}),
|
||||
enabled: true,
|
||||
};
|
||||
|
||||
if (mode === "telegram") {
|
||||
config.channels = {
|
||||
...(config.channels || {}),
|
||||
telegram: {
|
||||
...(config.channels?.telegram || {}),
|
||||
enabled: true,
|
||||
dmPolicy: "disabled",
|
||||
groupPolicy: "disabled",
|
||||
},
|
||||
};
|
||||
}
|
||||
if (mode === "discord") {
|
||||
config.channels = {
|
||||
...(config.channels || {}),
|
||||
discord: {
|
||||
...(config.channels?.discord || {}),
|
||||
enabled: true,
|
||||
dmPolicy: "disabled",
|
||||
groupPolicy: "disabled",
|
||||
},
|
||||
};
|
||||
}
|
||||
if (mode === "slack") {
|
||||
config.channels = {
|
||||
...(config.channels || {}),
|
||||
slack: {
|
||||
...(config.channels?.slack || {}),
|
||||
enabled: true,
|
||||
},
|
||||
};
|
||||
}
|
||||
if (mode === "feishu") {
|
||||
config.channels = {
|
||||
...(config.channels || {}),
|
||||
feishu: {
|
||||
...(config.channels?.feishu || {}),
|
||||
enabled: true,
|
||||
},
|
||||
};
|
||||
}
|
||||
if (mode === "memory-lancedb") {
|
||||
config.plugins = {
|
||||
...(config.plugins || {}),
|
||||
enabled: true,
|
||||
allow: [...new Set([...(config.plugins?.allow || []), "memory-lancedb"])],
|
||||
slots: {
|
||||
...(config.plugins?.slots || {}),
|
||||
memory: "memory-lancedb",
|
||||
},
|
||||
entries: {
|
||||
...(config.plugins?.entries || {}),
|
||||
"memory-lancedb": {
|
||||
...(config.plugins?.entries?.["memory-lancedb"] || {}),
|
||||
enabled: true,
|
||||
config: {
|
||||
...(config.plugins?.entries?.["memory-lancedb"]?.config || {}),
|
||||
embedding: {
|
||||
...(config.plugins?.entries?.["memory-lancedb"]?.config?.embedding || {}),
|
||||
apiKey: process.env.OPENAI_API_KEY,
|
||||
model: "text-embedding-3-small",
|
||||
},
|
||||
dbPath: "~/.openclaw/memory/lancedb-e2e",
|
||||
autoCapture: false,
|
||||
autoRecall: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
fs.mkdirSync(path.dirname(configPath), { recursive: true });
|
||||
fs.writeFileSync(configPath, `${JSON.stringify(config, null, 2)}\n`, "utf8");
|
||||
NODE
|
||||
}
|
||||
|
||||
start_gateway() {
|
||||
local log_file="$1"
|
||||
local skip_sidecars="${2:-0}"
|
||||
@@ -347,12 +206,12 @@ assert_installed_once() {
|
||||
if [ "$count" -eq 1 ]; then
|
||||
return 0
|
||||
fi
|
||||
if [ "$count" -eq 0 ] && [ -n "$(find_external_dep_package "$dep_path")" ]; then
|
||||
if [ "$count" -eq 0 ] && [ -n "$(bundled_channel_find_external_dep_package "$dep_path")" ]; then
|
||||
return 0
|
||||
fi
|
||||
echo "expected one runtime deps install log or staged dependency sentinel for $channel, got $count log lines" >&2
|
||||
cat "$log_file" >&2
|
||||
find "$(stage_root)" -maxdepth 12 -type f | sort | head -120 >&2 || true
|
||||
find "$(bundled_channel_stage_root)" -maxdepth 12 -type f | sort | head -120 >&2 || true
|
||||
exit 1
|
||||
}
|
||||
|
||||
@@ -369,24 +228,13 @@ assert_not_installed() {
|
||||
assert_dep_sentinel() {
|
||||
local channel="$1"
|
||||
local dep_path="$2"
|
||||
local sentinel
|
||||
sentinel="$(find_external_dep_package "$dep_path")"
|
||||
if [ -z "$sentinel" ]; then
|
||||
echo "missing external dependency sentinel for $channel: $dep_path" >&2
|
||||
find "$(stage_root)" -maxdepth 12 -type f | sort | head -120 >&2 || true
|
||||
exit 1
|
||||
fi
|
||||
assert_package_dep_absent "$channel" "$dep_path"
|
||||
bundled_channel_assert_dep_available "$channel" "$dep_path" "$package_root"
|
||||
}
|
||||
|
||||
assert_no_dep_sentinel() {
|
||||
local channel="$1"
|
||||
local dep_path="$2"
|
||||
assert_package_dep_absent "$channel" "$dep_path"
|
||||
if [ -n "$(find_external_dep_package "$dep_path")" ]; then
|
||||
echo "external dependency sentinel should be absent before activation for $channel: $dep_path" >&2
|
||||
exit 1
|
||||
fi
|
||||
bundled_channel_assert_no_dep_available "$channel" "$dep_path" "$package_root"
|
||||
}
|
||||
|
||||
assert_no_install_stage() {
|
||||
@@ -400,14 +248,14 @@ assert_no_install_stage() {
|
||||
}
|
||||
|
||||
echo "Starting baseline gateway with OpenAI configured..."
|
||||
write_config baseline
|
||||
bundled_channel_write_config baseline
|
||||
start_gateway "/tmp/openclaw-$CHANNEL-baseline.log" 1
|
||||
wait_for_gateway_health "/tmp/openclaw-$CHANNEL-baseline.log"
|
||||
stop_gateway
|
||||
assert_no_dep_sentinel "$CHANNEL" "$DEP_SENTINEL"
|
||||
|
||||
echo "Enabling $CHANNEL by config edit, then restarting gateway..."
|
||||
write_config "$CHANNEL"
|
||||
bundled_channel_write_config "$CHANNEL"
|
||||
start_gateway "/tmp/openclaw-$CHANNEL-first.log"
|
||||
wait_for_gateway_health "/tmp/openclaw-$CHANNEL-first.log"
|
||||
assert_installed_once "/tmp/openclaw-$CHANNEL-first.log" "$CHANNEL" "$DEP_SENTINEL"
|
||||
|
||||
194
scripts/e2e/lib/bundled-channel/common.sh
Normal file
194
scripts/e2e/lib/bundled-channel/common.sh
Normal file
@@ -0,0 +1,194 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Container-side helpers shared by bundled channel Docker E2E scenarios.
|
||||
# These functions assume the OpenClaw package is installed globally inside the
|
||||
# test container and the scenario has exported HOME/OPENAI_API_KEY as needed.
|
||||
|
||||
bundled_channel_package_root() {
|
||||
printf "%s/openclaw" "$(npm root -g)"
|
||||
}
|
||||
|
||||
bundled_channel_stage_root() {
|
||||
printf "%s/.openclaw/plugin-runtime-deps" "$HOME"
|
||||
}
|
||||
|
||||
bundled_channel_find_external_dep_package() {
|
||||
local dep_path="$1"
|
||||
find "$(bundled_channel_stage_root)" -maxdepth 12 -path "*/node_modules/$dep_path/package.json" -type f -print -quit 2>/dev/null || true
|
||||
}
|
||||
|
||||
bundled_channel_assert_no_package_dep_available() {
|
||||
local channel="$1"
|
||||
local dep_path="$2"
|
||||
local root="${3:-$(bundled_channel_package_root)}"
|
||||
for candidate in \
|
||||
"$root/dist/extensions/$channel/node_modules/$dep_path/package.json" \
|
||||
"$root/dist/extensions/node_modules/$dep_path/package.json" \
|
||||
"$root/node_modules/$dep_path/package.json"; do
|
||||
if [ -f "$candidate" ]; then
|
||||
echo "packaged install should not mutate package tree for $channel: $candidate" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
if [ -f "$HOME/node_modules/$dep_path/package.json" ]; then
|
||||
echo "bundled runtime deps should not use HOME npm project for $channel: $HOME/node_modules/$dep_path/package.json" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
bundled_channel_assert_dep_available() {
|
||||
local channel="$1"
|
||||
local dep_path="$2"
|
||||
local root="${3:-$(bundled_channel_package_root)}"
|
||||
if [ -n "$(bundled_channel_find_external_dep_package "$dep_path")" ]; then
|
||||
bundled_channel_assert_no_package_dep_available "$channel" "$dep_path" "$root"
|
||||
return 0
|
||||
fi
|
||||
echo "missing dependency sentinel for $channel: $dep_path" >&2
|
||||
find "$root/dist/extensions/$channel" -maxdepth 3 -type f | sort | head -80 >&2 || true
|
||||
find "$root/node_modules" -maxdepth 3 -path "*/$dep_path/package.json" -type f -print >&2 || true
|
||||
find "$(bundled_channel_stage_root)" -maxdepth 12 -type f | sort | head -120 >&2 || true
|
||||
exit 1
|
||||
}
|
||||
|
||||
bundled_channel_assert_no_dep_available() {
|
||||
local channel="$1"
|
||||
local dep_path="$2"
|
||||
local root="${3:-$(bundled_channel_package_root)}"
|
||||
bundled_channel_assert_no_package_dep_available "$channel" "$dep_path" "$root"
|
||||
if [ -n "$(bundled_channel_find_external_dep_package "$dep_path")" ]; then
|
||||
echo "dependency sentinel should be absent before repair for $channel: $dep_path" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
bundled_channel_remove_runtime_dep() {
|
||||
local channel="$1"
|
||||
local dep_path="$2"
|
||||
local root="${3:-$(bundled_channel_package_root)}"
|
||||
rm -rf "$root/dist/extensions/$channel/node_modules"
|
||||
rm -rf "$root/dist/extensions/node_modules/$dep_path"
|
||||
rm -rf "$root/node_modules/$dep_path"
|
||||
rm -rf "$(bundled_channel_stage_root)"
|
||||
}
|
||||
|
||||
bundled_channel_write_config() {
|
||||
local mode="$1"
|
||||
node - <<'NODE' "$mode" "${TOKEN:?missing TOKEN}" "${PORT:?missing PORT}"
|
||||
const fs = require("node:fs");
|
||||
const path = require("node:path");
|
||||
|
||||
const mode = process.argv[2];
|
||||
const token = process.argv[3];
|
||||
const port = Number(process.argv[4]);
|
||||
const configPath = path.join(process.env.HOME, ".openclaw", "openclaw.json");
|
||||
const config = fs.existsSync(configPath)
|
||||
? JSON.parse(fs.readFileSync(configPath, "utf8"))
|
||||
: {};
|
||||
|
||||
config.gateway = {
|
||||
...(config.gateway || {}),
|
||||
port,
|
||||
auth: { mode: "token", token },
|
||||
controlUi: { enabled: false },
|
||||
};
|
||||
config.agents = {
|
||||
...(config.agents || {}),
|
||||
defaults: {
|
||||
...(config.agents?.defaults || {}),
|
||||
model: { primary: "openai/gpt-4.1-mini" },
|
||||
},
|
||||
};
|
||||
config.models = {
|
||||
...(config.models || {}),
|
||||
providers: {
|
||||
...(config.models?.providers || {}),
|
||||
openai: {
|
||||
...(config.models?.providers?.openai || {}),
|
||||
apiKey: process.env.OPENAI_API_KEY,
|
||||
baseUrl: "https://api.openai.com/v1",
|
||||
models: [],
|
||||
},
|
||||
},
|
||||
};
|
||||
config.plugins = {
|
||||
...(config.plugins || {}),
|
||||
enabled: true,
|
||||
};
|
||||
config.channels = {
|
||||
...(config.channels || {}),
|
||||
telegram: {
|
||||
...(config.channels?.telegram || {}),
|
||||
enabled: mode === "telegram",
|
||||
botToken: "123456:bundled-channel-update-token",
|
||||
dmPolicy: "disabled",
|
||||
groupPolicy: "disabled",
|
||||
},
|
||||
discord: {
|
||||
...(config.channels?.discord || {}),
|
||||
enabled: mode === "discord",
|
||||
dmPolicy: "disabled",
|
||||
groupPolicy: "disabled",
|
||||
},
|
||||
slack: {
|
||||
...(config.channels?.slack || {}),
|
||||
enabled: mode === "slack",
|
||||
botToken: "xoxb-bundled-channel-update-token",
|
||||
appToken: "xapp-bundled-channel-update-token",
|
||||
},
|
||||
feishu: {
|
||||
...(config.channels?.feishu || {}),
|
||||
enabled: mode === "feishu",
|
||||
},
|
||||
};
|
||||
if (mode === "memory-lancedb") {
|
||||
config.plugins = {
|
||||
...(config.plugins || {}),
|
||||
enabled: true,
|
||||
allow: [...new Set([...(config.plugins?.allow || []), "memory-lancedb"])],
|
||||
slots: {
|
||||
...(config.plugins?.slots || {}),
|
||||
memory: "memory-lancedb",
|
||||
},
|
||||
entries: {
|
||||
...(config.plugins?.entries || {}),
|
||||
"memory-lancedb": {
|
||||
...(config.plugins?.entries?.["memory-lancedb"] || {}),
|
||||
enabled: true,
|
||||
config: {
|
||||
...(config.plugins?.entries?.["memory-lancedb"]?.config || {}),
|
||||
embedding: {
|
||||
...(config.plugins?.entries?.["memory-lancedb"]?.config?.embedding || {}),
|
||||
apiKey: process.env.OPENAI_API_KEY,
|
||||
model: "text-embedding-3-small",
|
||||
},
|
||||
dbPath: process.env.OPENCLAW_BUNDLED_CHANNEL_MEMORY_DB_PATH || "~/.openclaw/memory/lancedb-e2e",
|
||||
autoCapture: false,
|
||||
autoRecall: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
if (mode === "acpx") {
|
||||
config.plugins = {
|
||||
...(config.plugins || {}),
|
||||
enabled: true,
|
||||
allow:
|
||||
Array.isArray(config.plugins?.allow) && config.plugins.allow.length > 0
|
||||
? [...new Set([...config.plugins.allow, "acpx"])]
|
||||
: config.plugins?.allow,
|
||||
entries: {
|
||||
...(config.plugins?.entries || {}),
|
||||
acpx: {
|
||||
...(config.plugins?.entries?.acpx || {}),
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
fs.mkdirSync(path.dirname(configPath), { recursive: true });
|
||||
fs.writeFileSync(configPath, `${JSON.stringify(config, null, 2)}\n`, "utf8");
|
||||
NODE
|
||||
}
|
||||
@@ -17,6 +17,7 @@ run_disabled_config_scenario() {
|
||||
set -euo pipefail
|
||||
|
||||
source scripts/lib/openclaw-e2e-instance.sh
|
||||
source scripts/e2e/lib/bundled-channel/common.sh
|
||||
openclaw_e2e_eval_test_state_from_b64 "${OPENCLAW_TEST_STATE_SCRIPT_B64:?missing OPENCLAW_TEST_STATE_SCRIPT_B64}"
|
||||
export NPM_CONFIG_PREFIX="$HOME/.npm-global"
|
||||
export PATH="$NPM_CONFIG_PREFIX/bin:$PATH"
|
||||
@@ -24,10 +25,6 @@ export OPENCLAW_NO_ONBOARD=1
|
||||
export OPENCLAW_PLUGIN_STAGE_DIR="$HOME/.openclaw/plugin-runtime-deps"
|
||||
mkdir -p "$OPENCLAW_PLUGIN_STAGE_DIR"
|
||||
|
||||
package_root() {
|
||||
printf "%s/openclaw" "$(npm root -g)"
|
||||
}
|
||||
|
||||
assert_dep_absent_everywhere() {
|
||||
local channel="$1"
|
||||
local dep_path="$2"
|
||||
@@ -99,7 +96,7 @@ echo "Installing mounted OpenClaw package..."
|
||||
package_tgz="${OPENCLAW_CURRENT_PACKAGE_TGZ:?missing OPENCLAW_CURRENT_PACKAGE_TGZ}"
|
||||
npm install -g "$package_tgz" --no-fund --no-audit >/tmp/openclaw-disabled-config-install.log 2>&1
|
||||
|
||||
root="$(package_root)"
|
||||
root="$(bundled_channel_package_root)"
|
||||
test -d "$root/dist/extensions/telegram"
|
||||
test -d "$root/dist/extensions/discord"
|
||||
test -d "$root/dist/extensions/slack"
|
||||
|
||||
@@ -17,20 +17,17 @@ run_load_failure_scenario() {
|
||||
set -euo pipefail
|
||||
|
||||
source scripts/lib/openclaw-e2e-instance.sh
|
||||
source scripts/e2e/lib/bundled-channel/common.sh
|
||||
openclaw_e2e_eval_test_state_from_b64 "${OPENCLAW_TEST_STATE_SCRIPT_B64:?missing OPENCLAW_TEST_STATE_SCRIPT_B64}"
|
||||
export NPM_CONFIG_PREFIX="$HOME/.npm-global"
|
||||
export PATH="$NPM_CONFIG_PREFIX/bin:$PATH"
|
||||
export OPENCLAW_NO_ONBOARD=1
|
||||
|
||||
package_root() {
|
||||
printf "%s/openclaw" "$(npm root -g)"
|
||||
}
|
||||
|
||||
echo "Installing mounted OpenClaw package..."
|
||||
package_tgz="${OPENCLAW_CURRENT_PACKAGE_TGZ:?missing OPENCLAW_CURRENT_PACKAGE_TGZ}"
|
||||
npm install -g "$package_tgz" --no-fund --no-audit >/tmp/openclaw-load-failure-install.log 2>&1
|
||||
|
||||
root="$(package_root)"
|
||||
root="$(bundled_channel_package_root)"
|
||||
plugin_dir="$root/dist/extensions/load-failure-alpha"
|
||||
mkdir -p "$plugin_dir"
|
||||
cat >"$plugin_dir/package.json" <<'JSON'
|
||||
|
||||
@@ -13,6 +13,7 @@ run_root_owned_global_scenario() {
|
||||
set -euo pipefail
|
||||
|
||||
source scripts/lib/openclaw-e2e-instance.sh
|
||||
source scripts/e2e/lib/bundled-channel/common.sh
|
||||
export HOME="/root"
|
||||
export OPENAI_API_KEY="sk-openclaw-bundled-channel-root-owned-e2e"
|
||||
export OPENCLAW_NO_ONBOARD=1
|
||||
@@ -24,10 +25,6 @@ CHANNEL="slack"
|
||||
DEP_SENTINEL="@slack/web-api"
|
||||
gateway_pid=""
|
||||
|
||||
package_root() {
|
||||
printf "%s/openclaw" "$(npm root -g)"
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
if [ -n "${gateway_pid:-}" ] && kill -0 "$gateway_pid" 2>/dev/null; then
|
||||
kill "$gateway_pid" 2>/dev/null || true
|
||||
@@ -44,7 +41,7 @@ if ! npm install -g "$package_tgz" --no-fund --no-audit >/tmp/openclaw-root-owne
|
||||
exit 1
|
||||
fi
|
||||
|
||||
root="$(package_root)"
|
||||
root="$(bundled_channel_package_root)"
|
||||
test -d "$root/dist/extensions/$CHANNEL"
|
||||
rm -rf "$root/dist/extensions/$CHANNEL/node_modules"
|
||||
chmod -R a-w "$root"
|
||||
|
||||
@@ -17,6 +17,7 @@ run_setup_entry_scenario() {
|
||||
set -euo pipefail
|
||||
|
||||
source scripts/lib/openclaw-e2e-instance.sh
|
||||
source scripts/e2e/lib/bundled-channel/common.sh
|
||||
openclaw_e2e_eval_test_state_from_b64 "${OPENCLAW_TEST_STATE_SCRIPT_B64:?missing OPENCLAW_TEST_STATE_SCRIPT_B64}"
|
||||
export NPM_CONFIG_PREFIX="$HOME/.npm-global"
|
||||
export PATH="$NPM_CONFIG_PREFIX/bin:$PATH"
|
||||
@@ -29,10 +30,6 @@ declare -A SETUP_ENTRY_DEP_SENTINELS=(
|
||||
[whatsapp]="@whiskeysockets/baileys"
|
||||
)
|
||||
|
||||
package_root() {
|
||||
printf "%s/openclaw" "$(npm root -g)"
|
||||
}
|
||||
|
||||
echo "Installing mounted OpenClaw package..."
|
||||
package_tgz="${OPENCLAW_CURRENT_PACKAGE_TGZ:?missing OPENCLAW_CURRENT_PACKAGE_TGZ}"
|
||||
if ! npm install -g "$package_tgz" --no-fund --no-audit >/tmp/openclaw-setup-entry-install.log 2>&1; then
|
||||
@@ -40,7 +37,7 @@ if ! npm install -g "$package_tgz" --no-fund --no-audit >/tmp/openclaw-setup-ent
|
||||
exit 1
|
||||
fi
|
||||
|
||||
root="$(package_root)"
|
||||
root="$(bundled_channel_package_root)"
|
||||
for channel in "${!SETUP_ENTRY_DEP_SENTINELS[@]}"; do
|
||||
dep_sentinel="${SETUP_ENTRY_DEP_SENTINELS[$channel]}"
|
||||
test -d "$root/dist/extensions/$channel"
|
||||
|
||||
@@ -19,25 +19,19 @@ run_update_scenario() {
|
||||
set -euo pipefail
|
||||
|
||||
source scripts/lib/openclaw-e2e-instance.sh
|
||||
source scripts/e2e/lib/bundled-channel/common.sh
|
||||
openclaw_e2e_eval_test_state_from_b64 "${OPENCLAW_TEST_STATE_SCRIPT_B64:?missing OPENCLAW_TEST_STATE_SCRIPT_B64}"
|
||||
export NPM_CONFIG_PREFIX="$HOME/.npm-global"
|
||||
export PATH="$NPM_CONFIG_PREFIX/bin:$PATH"
|
||||
export OPENAI_API_KEY="sk-openclaw-bundled-channel-update-e2e"
|
||||
export OPENCLAW_NO_ONBOARD=1
|
||||
export OPENCLAW_UPDATE_PACKAGE_SPEC=""
|
||||
export OPENCLAW_BUNDLED_CHANNEL_MEMORY_DB_PATH="~/.openclaw/memory/lancedb-update-e2e"
|
||||
|
||||
TOKEN="bundled-channel-update-token"
|
||||
PORT="18790"
|
||||
UPDATE_TARGETS="${OPENCLAW_BUNDLED_CHANNEL_UPDATE_TARGETS:-telegram,discord,slack,feishu,memory-lancedb,acpx}"
|
||||
|
||||
package_root() {
|
||||
printf "%s/openclaw" "$(npm root -g)"
|
||||
}
|
||||
|
||||
stage_root() {
|
||||
printf "%s/.openclaw/plugin-runtime-deps" "$HOME"
|
||||
}
|
||||
|
||||
poison_home_npm_project() {
|
||||
printf '{"name":"openclaw-home-prefix-poison","private":true}\n' >"$HOME/package.json"
|
||||
rm -rf "$HOME/node_modules"
|
||||
@@ -45,15 +39,10 @@ poison_home_npm_project() {
|
||||
chmod 500 "$HOME/node_modules"
|
||||
}
|
||||
|
||||
find_external_dep_package() {
|
||||
local dep_path="$1"
|
||||
find "$(stage_root)" -maxdepth 12 -path "*/node_modules/$dep_path/package.json" -type f -print -quit 2>/dev/null || true
|
||||
}
|
||||
|
||||
assert_no_unknown_stage_roots() {
|
||||
if find "$(stage_root)" -maxdepth 1 -type d -name 'openclaw-unknown-*' -print -quit 2>/dev/null | grep -q .; then
|
||||
if find "$(bundled_channel_stage_root)" -maxdepth 1 -type d -name 'openclaw-unknown-*' -print -quit 2>/dev/null | grep -q .; then
|
||||
echo "runtime deps created second-generation unknown stage roots" >&2
|
||||
find "$(stage_root)" -maxdepth 1 -type d -name 'openclaw-*' -print | sort >&2 || true
|
||||
find "$(bundled_channel_stage_root)" -maxdepth 1 -type d -name 'openclaw-*' -print | sort >&2 || true
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
@@ -69,214 +58,6 @@ process.stdout.write(String(JSON.parse(raw).version));
|
||||
NODE
|
||||
)"
|
||||
|
||||
write_config() {
|
||||
local mode="$1"
|
||||
node - <<'NODE' "$mode" "$TOKEN" "$PORT"
|
||||
const fs = require("node:fs");
|
||||
const path = require("node:path");
|
||||
|
||||
const mode = process.argv[2];
|
||||
const token = process.argv[3];
|
||||
const port = Number(process.argv[4]);
|
||||
const configPath = path.join(process.env.HOME, ".openclaw", "openclaw.json");
|
||||
const config = fs.existsSync(configPath)
|
||||
? JSON.parse(fs.readFileSync(configPath, "utf8"))
|
||||
: {};
|
||||
|
||||
config.gateway = {
|
||||
...(config.gateway || {}),
|
||||
port,
|
||||
auth: { mode: "token", token },
|
||||
controlUi: { enabled: false },
|
||||
};
|
||||
config.agents = {
|
||||
...(config.agents || {}),
|
||||
defaults: {
|
||||
...(config.agents?.defaults || {}),
|
||||
model: { primary: "openai/gpt-4.1-mini" },
|
||||
},
|
||||
};
|
||||
config.models = {
|
||||
...(config.models || {}),
|
||||
providers: {
|
||||
...(config.models?.providers || {}),
|
||||
openai: {
|
||||
...(config.models?.providers?.openai || {}),
|
||||
apiKey: process.env.OPENAI_API_KEY,
|
||||
baseUrl: "https://api.openai.com/v1",
|
||||
models: [],
|
||||
},
|
||||
},
|
||||
};
|
||||
config.plugins = {
|
||||
...(config.plugins || {}),
|
||||
enabled: true,
|
||||
};
|
||||
config.channels = {
|
||||
...(config.channels || {}),
|
||||
telegram: {
|
||||
...(config.channels?.telegram || {}),
|
||||
enabled: mode === "telegram",
|
||||
botToken: "123456:bundled-channel-update-token",
|
||||
dmPolicy: "disabled",
|
||||
groupPolicy: "disabled",
|
||||
},
|
||||
discord: {
|
||||
...(config.channels?.discord || {}),
|
||||
enabled: mode === "discord",
|
||||
dmPolicy: "disabled",
|
||||
groupPolicy: "disabled",
|
||||
},
|
||||
slack: {
|
||||
...(config.channels?.slack || {}),
|
||||
enabled: mode === "slack",
|
||||
botToken: "xoxb-bundled-channel-update-token",
|
||||
appToken: "xapp-bundled-channel-update-token",
|
||||
},
|
||||
feishu: {
|
||||
...(config.channels?.feishu || {}),
|
||||
enabled: mode === "feishu",
|
||||
},
|
||||
};
|
||||
if (mode === "memory-lancedb") {
|
||||
config.plugins = {
|
||||
...(config.plugins || {}),
|
||||
enabled: true,
|
||||
allow: [...new Set([...(config.plugins?.allow || []), "memory-lancedb"])],
|
||||
slots: {
|
||||
...(config.plugins?.slots || {}),
|
||||
memory: "memory-lancedb",
|
||||
},
|
||||
entries: {
|
||||
...(config.plugins?.entries || {}),
|
||||
"memory-lancedb": {
|
||||
...(config.plugins?.entries?.["memory-lancedb"] || {}),
|
||||
enabled: true,
|
||||
config: {
|
||||
...(config.plugins?.entries?.["memory-lancedb"]?.config || {}),
|
||||
embedding: {
|
||||
...(config.plugins?.entries?.["memory-lancedb"]?.config?.embedding || {}),
|
||||
apiKey: process.env.OPENAI_API_KEY,
|
||||
model: "text-embedding-3-small",
|
||||
},
|
||||
dbPath: "~/.openclaw/memory/lancedb-update-e2e",
|
||||
autoCapture: false,
|
||||
autoRecall: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
if (mode === "acpx") {
|
||||
config.plugins = {
|
||||
...(config.plugins || {}),
|
||||
enabled: true,
|
||||
allow:
|
||||
Array.isArray(config.plugins?.allow) && config.plugins.allow.length > 0
|
||||
? [...new Set([...config.plugins.allow, "acpx"])]
|
||||
: config.plugins?.allow,
|
||||
entries: {
|
||||
...(config.plugins?.entries || {}),
|
||||
acpx: {
|
||||
...(config.plugins?.entries?.acpx || {}),
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
fs.mkdirSync(path.dirname(configPath), { recursive: true });
|
||||
fs.writeFileSync(configPath, `${JSON.stringify(config, null, 2)}\n`, "utf8");
|
||||
NODE
|
||||
}
|
||||
|
||||
assert_dep_sentinel() {
|
||||
local channel="$1"
|
||||
local dep_path="$2"
|
||||
local root
|
||||
local sentinel
|
||||
root="$(package_root)"
|
||||
sentinel="$(find_external_dep_package "$dep_path")"
|
||||
if [ -z "$sentinel" ]; then
|
||||
echo "missing external dependency sentinel for $channel: $dep_path" >&2
|
||||
find "$(stage_root)" -maxdepth 12 -type f | sort | head -120 >&2 || true
|
||||
exit 1
|
||||
fi
|
||||
assert_no_package_dep_available "$channel" "$dep_path" "$root"
|
||||
}
|
||||
|
||||
assert_no_dep_sentinel() {
|
||||
local channel="$1"
|
||||
local dep_path="$2"
|
||||
local root
|
||||
root="$(package_root)"
|
||||
assert_no_package_dep_available "$channel" "$dep_path" "$root"
|
||||
if [ -n "$(find_external_dep_package "$dep_path")" ]; then
|
||||
echo "external dependency sentinel should be absent before repair for $channel: $dep_path" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
assert_no_package_dep_available() {
|
||||
local channel="$1"
|
||||
local dep_path="$2"
|
||||
local root="$3"
|
||||
for candidate in \
|
||||
"$root/dist/extensions/$channel/node_modules/$dep_path/package.json" \
|
||||
"$root/dist/extensions/node_modules/$dep_path/package.json" \
|
||||
"$root/node_modules/$dep_path/package.json"; do
|
||||
if [ -f "$candidate" ]; then
|
||||
echo "packaged install should not mutate package tree for $channel: $candidate" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
if [ -f "$HOME/node_modules/$dep_path/package.json" ]; then
|
||||
echo "bundled runtime deps should not use HOME npm project for $channel: $HOME/node_modules/$dep_path/package.json" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
assert_dep_available() {
|
||||
local channel="$1"
|
||||
local dep_path="$2"
|
||||
local root
|
||||
local sentinel
|
||||
root="$(package_root)"
|
||||
sentinel="$(find_external_dep_package "$dep_path")"
|
||||
if [ -n "$sentinel" ]; then
|
||||
assert_no_package_dep_available "$channel" "$dep_path" "$root"
|
||||
return 0
|
||||
fi
|
||||
echo "missing dependency sentinel for $channel: $dep_path" >&2
|
||||
find "$root/dist/extensions/$channel" -maxdepth 3 -type f | sort | head -80 >&2 || true
|
||||
find "$root/node_modules" -maxdepth 3 -path "*/$dep_path/package.json" -type f -print >&2 || true
|
||||
find "$(stage_root)" -maxdepth 12 -type f | sort | head -120 >&2 || true
|
||||
exit 1
|
||||
}
|
||||
|
||||
assert_no_dep_available() {
|
||||
local channel="$1"
|
||||
local dep_path="$2"
|
||||
local root
|
||||
root="$(package_root)"
|
||||
assert_no_package_dep_available "$channel" "$dep_path" "$root"
|
||||
if [ -n "$(find_external_dep_package "$dep_path")" ]; then
|
||||
echo "dependency sentinel should be absent before repair for $channel: $dep_path" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
remove_runtime_dep() {
|
||||
local channel="$1"
|
||||
local dep_path="$2"
|
||||
local root
|
||||
root="$(package_root)"
|
||||
rm -rf "$root/dist/extensions/$channel/node_modules"
|
||||
rm -rf "$root/dist/extensions/node_modules/$dep_path"
|
||||
rm -rf "$root/node_modules/$dep_path"
|
||||
rm -rf "$(stage_root)"
|
||||
}
|
||||
|
||||
assert_update_ok() {
|
||||
local json_file="$1"
|
||||
local expected_before="$2"
|
||||
@@ -337,95 +118,95 @@ echo "Update targets: $UPDATE_TARGETS"
|
||||
npm install -g "$package_tgz" --no-fund --no-audit >/tmp/openclaw-update-baseline-install.log 2>&1
|
||||
command -v openclaw >/dev/null
|
||||
poison_home_npm_project
|
||||
baseline_root="$(package_root)"
|
||||
baseline_root="$(bundled_channel_package_root)"
|
||||
test -d "$baseline_root/dist/extensions/telegram"
|
||||
test -d "$baseline_root/dist/extensions/feishu"
|
||||
test -d "$baseline_root/dist/extensions/acpx"
|
||||
|
||||
if should_run_update_target telegram; then
|
||||
echo "Replicating configured Telegram missing-runtime state..."
|
||||
write_config telegram
|
||||
assert_no_dep_available telegram grammy
|
||||
bundled_channel_write_config telegram
|
||||
bundled_channel_assert_no_dep_available telegram grammy
|
||||
set +e
|
||||
openclaw doctor --non-interactive >/tmp/openclaw-baseline-doctor.log 2>&1
|
||||
baseline_doctor_status=$?
|
||||
set -e
|
||||
echo "baseline doctor exited with $baseline_doctor_status"
|
||||
remove_runtime_dep telegram grammy
|
||||
assert_no_dep_available telegram grammy
|
||||
bundled_channel_remove_runtime_dep telegram grammy
|
||||
bundled_channel_assert_no_dep_available telegram grammy
|
||||
|
||||
echo "Updating from baseline to current candidate; candidate doctor must repair Telegram deps..."
|
||||
run_update_and_capture telegram /tmp/openclaw-update-telegram.json
|
||||
cat /tmp/openclaw-update-telegram.json
|
||||
assert_update_ok /tmp/openclaw-update-telegram.json "$candidate_version"
|
||||
assert_dep_available telegram grammy
|
||||
bundled_channel_assert_dep_available telegram grammy
|
||||
assert_no_unknown_stage_roots
|
||||
|
||||
echo "Mutating installed package: remove Telegram deps, then update-mode doctor repairs them..."
|
||||
remove_runtime_dep telegram grammy
|
||||
assert_no_dep_available telegram grammy
|
||||
bundled_channel_remove_runtime_dep telegram grammy
|
||||
bundled_channel_assert_no_dep_available telegram grammy
|
||||
if ! OPENCLAW_UPDATE_IN_PROGRESS=1 openclaw doctor --non-interactive >/tmp/openclaw-update-mode-doctor.log 2>&1; then
|
||||
echo "update-mode doctor failed while repairing Telegram deps" >&2
|
||||
cat /tmp/openclaw-update-mode-doctor.log >&2
|
||||
exit 1
|
||||
fi
|
||||
assert_dep_available telegram grammy
|
||||
bundled_channel_assert_dep_available telegram grammy
|
||||
assert_no_unknown_stage_roots
|
||||
fi
|
||||
|
||||
if should_run_update_target discord; then
|
||||
echo "Mutating config to Discord and rerunning same-version update path..."
|
||||
write_config discord
|
||||
remove_runtime_dep discord discord-api-types
|
||||
assert_no_dep_available discord discord-api-types
|
||||
bundled_channel_write_config discord
|
||||
bundled_channel_remove_runtime_dep discord discord-api-types
|
||||
bundled_channel_assert_no_dep_available discord discord-api-types
|
||||
run_update_and_capture discord /tmp/openclaw-update-discord.json
|
||||
cat /tmp/openclaw-update-discord.json
|
||||
assert_update_ok /tmp/openclaw-update-discord.json "$candidate_version"
|
||||
assert_dep_available discord discord-api-types
|
||||
bundled_channel_assert_dep_available discord discord-api-types
|
||||
fi
|
||||
|
||||
if should_run_update_target slack; then
|
||||
echo "Mutating config to Slack and rerunning same-version update path..."
|
||||
write_config slack
|
||||
remove_runtime_dep slack @slack/web-api
|
||||
assert_no_dep_available slack @slack/web-api
|
||||
bundled_channel_write_config slack
|
||||
bundled_channel_remove_runtime_dep slack @slack/web-api
|
||||
bundled_channel_assert_no_dep_available slack @slack/web-api
|
||||
run_update_and_capture slack /tmp/openclaw-update-slack.json
|
||||
cat /tmp/openclaw-update-slack.json
|
||||
assert_update_ok /tmp/openclaw-update-slack.json "$candidate_version"
|
||||
assert_dep_available slack @slack/web-api
|
||||
bundled_channel_assert_dep_available slack @slack/web-api
|
||||
fi
|
||||
|
||||
if should_run_update_target feishu; then
|
||||
echo "Mutating config to Feishu and rerunning same-version update path..."
|
||||
write_config feishu
|
||||
remove_runtime_dep feishu @larksuiteoapi/node-sdk
|
||||
assert_no_dep_available feishu @larksuiteoapi/node-sdk
|
||||
bundled_channel_write_config feishu
|
||||
bundled_channel_remove_runtime_dep feishu @larksuiteoapi/node-sdk
|
||||
bundled_channel_assert_no_dep_available feishu @larksuiteoapi/node-sdk
|
||||
run_update_and_capture feishu /tmp/openclaw-update-feishu.json
|
||||
cat /tmp/openclaw-update-feishu.json
|
||||
assert_update_ok /tmp/openclaw-update-feishu.json "$candidate_version"
|
||||
assert_dep_available feishu @larksuiteoapi/node-sdk
|
||||
bundled_channel_assert_dep_available feishu @larksuiteoapi/node-sdk
|
||||
fi
|
||||
|
||||
if should_run_update_target memory-lancedb; then
|
||||
echo "Mutating config to memory-lancedb and rerunning same-version update path..."
|
||||
write_config memory-lancedb
|
||||
remove_runtime_dep memory-lancedb @lancedb/lancedb
|
||||
assert_no_dep_available memory-lancedb @lancedb/lancedb
|
||||
bundled_channel_write_config memory-lancedb
|
||||
bundled_channel_remove_runtime_dep memory-lancedb @lancedb/lancedb
|
||||
bundled_channel_assert_no_dep_available memory-lancedb @lancedb/lancedb
|
||||
run_update_and_capture memory-lancedb /tmp/openclaw-update-memory-lancedb.json
|
||||
cat /tmp/openclaw-update-memory-lancedb.json
|
||||
assert_update_ok /tmp/openclaw-update-memory-lancedb.json "$candidate_version"
|
||||
assert_dep_available memory-lancedb @lancedb/lancedb
|
||||
bundled_channel_assert_dep_available memory-lancedb @lancedb/lancedb
|
||||
fi
|
||||
|
||||
if should_run_update_target acpx; then
|
||||
echo "Removing ACPX runtime package and rerunning same-version update path..."
|
||||
write_config acpx
|
||||
remove_runtime_dep acpx acpx
|
||||
assert_no_dep_available acpx acpx
|
||||
bundled_channel_write_config acpx
|
||||
bundled_channel_remove_runtime_dep acpx acpx
|
||||
bundled_channel_assert_no_dep_available acpx acpx
|
||||
run_update_and_capture acpx /tmp/openclaw-update-acpx.json
|
||||
cat /tmp/openclaw-update-acpx.json
|
||||
assert_update_ok /tmp/openclaw-update-acpx.json "$candidate_version"
|
||||
assert_dep_available acpx acpx
|
||||
bundled_channel_assert_dep_available acpx acpx
|
||||
fi
|
||||
|
||||
echo "bundled channel runtime deps Docker update E2E passed"
|
||||
|
||||
Reference in New Issue
Block a user