mirror of
https://github.com/openclaw/openclaw.git
synced 2026-10-03 17:53:39 +00:00
## What Problem This Solves Repository scripts retain private copies of shared tooling helpers and an unused translation subprocess runner, increasing maintenance work across sibling scripts. ## User Impact Internal tooling cleanup: existing flags, diagnostics, generated bytes, JSON formats, exit codes, and cleanup policies stay the same. The build wrapper additionally detects forbidden dynamic-import warnings when compiler output splits the warning marker across chunks. ## Why This Change Was Made - Remove the retired i18n process runner. Its final production caller moved to the shared formatter in #95534; retain real CLI/privacy/provider tests and run their subprocesses with the existing dependency. - Share Docker resource, signal-trap, platform, workspace-staging, and plugin-selection helpers while retaining each caller's environment precedence and lifecycle decisions. - Reuse existing E2E fixture JSON/assertion and package-path helpers; retain distinct write modes, recovery ordering, and mounted/frozen harness contracts. - Share macOS guest desktop-user/home resolution through its existing owner while preserving per-caller timeout policy. - Route release/mobile flags through the existing version flag specifications, preserving split-only values, duplicate/mode rules, help timing, and error text. Reuse existing retry sleep and comparator owners and remove an unreachable iOS output branch. - Reuse existing guard entrypoint, failure-trailer, diagnostic-line, and metadata-normalization owners. Measured reduction: **732 net production lines**, with test changes counted separately. PR tooling and its protected import closure, CI planners/shards, baselines, and generated artifacts are unchanged. ## Fixes Found Along the Way The tsdown scanner checked each raw output chunk for its warning marker. It now checks the existing combined-line buffer. The regression exercises every split inside the marker and fails on the original source for the intended missing-warning assertion. ## Evidence - Blacksmith Testbox `tbx_01m3tp4sabwd0807kj9jkqqmbr`: frozen dependency install and candidate source-byte verification; all nine changed test files and 59 selected sibling files passed, including real CLI, package fixture, recovery, Docker harness, and Parallels transport contracts. - Generated channel metadata is byte-identical; differential metadata normalization cases passed. - SDK surface check passed. Independent isolated Codex review completed with no P0–P2 findings. - Initial changed checks caught an invalid direct source import in a proposed snapshot-distance consolidation; that independent change was withdrawn. No boundary exception was added. The final changed checks pass, including script/test lint and Docker shell/scheduler checks; madge reports 0 cycles and the runtime import check reports 0 cycles. - Per-file single-worker wall time for changed suites: translation 9.92s; Docker helper 38.88s; live Docker auth 1.71s; mobile ref 2.36s; mobile release 6.23s; release preparation 2.42s; version 2.04s; installer 15.01s; tsdown 2.70s. The existing Docker helper suite executes shell/process cleanup and container-command boundary fixtures; new assertions reuse those fixtures. New parser/scanner cases use no sleeps or polling. Hosted CI will be verified against this PR's exact pushed head. No live deployment or release was performed.
283 lines
8 KiB
Bash
283 lines
8 KiB
Bash
#!/usr/bin/env bash
|
|
#
|
|
# Shared helpers for Docker E2E scripts that keep a named container running
|
|
# while polling readiness from the host.
|
|
|
|
DOCKER_E2E_CONTAINER_LIB_DIR="${BASH_SOURCE[0]}"
|
|
if [[ "$DOCKER_E2E_CONTAINER_LIB_DIR" == */* ]]; then
|
|
DOCKER_E2E_CONTAINER_LIB_DIR="${DOCKER_E2E_CONTAINER_LIB_DIR%/*}"
|
|
else
|
|
DOCKER_E2E_CONTAINER_LIB_DIR=.
|
|
fi
|
|
DOCKER_E2E_CONTAINER_LIB_DIR="$(cd "$DOCKER_E2E_CONTAINER_LIB_DIR" && pwd)"
|
|
source "$DOCKER_E2E_CONTAINER_LIB_DIR/docker-e2e-resource-diagnostics.sh"
|
|
|
|
docker_e2e_timeout_bin() {
|
|
if command -v timeout >/dev/null 2>&1; then
|
|
printf '%s\n' timeout
|
|
elif command -v gtimeout >/dev/null 2>&1; then
|
|
printf '%s\n' gtimeout
|
|
else
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
docker_e2e_timeout_cmd() {
|
|
local timeout_value="$1"
|
|
shift
|
|
local timeout_bin
|
|
if ! timeout_bin="$(docker_e2e_timeout_bin)"; then
|
|
if command -v node >/dev/null 2>&1; then
|
|
echo "timeout command not found; using Node watchdog for Docker command timeout ${timeout_value}" >&2
|
|
# Keep argv unchanged: Node historically parses option-like timeout values.
|
|
local helper_source="$DOCKER_E2E_CONTAINER_LIB_DIR/docker-e2e-watchdog.mjs"
|
|
helper_source="${helper_source//\\/\\\\}"
|
|
helper_source="${helper_source//\"/\\\"}"
|
|
helper_source="${helper_source//$'\n'/\\n}"
|
|
helper_source="${helper_source//$'\r'/\\r}"
|
|
node --input-type=module -e "
|
|
import { pathToFileURL } from 'node:url';
|
|
const { runWatchdog } = await import(pathToFileURL(\"$helper_source\").href);
|
|
const [, timeoutValue, ...args] = process.argv;
|
|
await runWatchdog('docker', timeoutValue, args);
|
|
" "$timeout_value" "$@"
|
|
return "$?"
|
|
fi
|
|
echo "timeout command not found; cannot bound Docker command after ${timeout_value}" >&2
|
|
return 127
|
|
fi
|
|
if "$timeout_bin" --kill-after=1s 1s true >/dev/null 2>&1; then
|
|
"$timeout_bin" --kill-after=30s "$timeout_value" "$@"
|
|
else
|
|
"$timeout_bin" "$timeout_value" "$@"
|
|
fi
|
|
}
|
|
|
|
docker_e2e_docker_cmd() {
|
|
local timeout_value="${DOCKER_COMMAND_TIMEOUT:-600s}"
|
|
if [ "${1:-}" = "run" ]; then
|
|
shift
|
|
docker_e2e_docker_run_resource_args "$@" || return $?
|
|
docker_e2e_docker_run_with_resource_diagnostics "$timeout_value" "$@"
|
|
# A bare return in an EXIT trap can restore the trap's original status.
|
|
return "$?"
|
|
fi
|
|
docker_e2e_timeout_cmd "$timeout_value" docker "$@"
|
|
}
|
|
|
|
docker_e2e_cleanup_container_run() {
|
|
docker_e2e_docker_cmd rm -f "$1" >/dev/null 2>&1 || true
|
|
rm -f "$2"
|
|
}
|
|
|
|
docker_e2e_docker_run_cmd() {
|
|
local timeout_value="${DOCKER_COMMAND_TIMEOUT:-${OPENCLAW_DOCKER_E2E_RUN_TIMEOUT:-3600s}}"
|
|
if [ "${1:-}" = "run" ]; then
|
|
shift
|
|
docker_e2e_docker_run_resource_args "$@" || return $?
|
|
docker_e2e_docker_run_with_resource_diagnostics "$timeout_value" "$@"
|
|
return "$?"
|
|
fi
|
|
docker_e2e_timeout_cmd "$timeout_value" docker "$@"
|
|
}
|
|
|
|
docker_e2e_resource_limits_disabled() {
|
|
case "${OPENCLAW_DOCKER_E2E_DISABLE_RESOURCE_LIMITS:-}" in
|
|
1 | true | TRUE | yes | YES | on | ON)
|
|
return 0
|
|
;;
|
|
esac
|
|
return 1
|
|
}
|
|
|
|
docker_e2e_resource_value_disabled() {
|
|
case "${1:-}" in
|
|
"" | 0 | none | NONE | off | OFF | false | FALSE)
|
|
return 0
|
|
;;
|
|
esac
|
|
return 1
|
|
}
|
|
|
|
docker_e2e_detect_available_cpus() {
|
|
local available="${1:-${OPENCLAW_DOCKER_E2E_AVAILABLE_CPUS:-}}"
|
|
if [ -n "$available" ]; then
|
|
printf '%s\n' "$available"
|
|
return 0
|
|
fi
|
|
if command -v nproc >/dev/null 2>&1; then
|
|
nproc
|
|
return 0
|
|
fi
|
|
if command -v getconf >/dev/null 2>&1; then
|
|
getconf _NPROCESSORS_ONLN
|
|
return 0
|
|
fi
|
|
return 1
|
|
}
|
|
|
|
docker_e2e_resolve_cpus() {
|
|
local requested="$1"
|
|
local available=""
|
|
available="$(docker_e2e_detect_available_cpus "${2:-}" 2>/dev/null || true)"
|
|
if [[ "$requested" =~ ^[0-9]+$ ]] && [[ "$available" =~ ^[0-9]+$ ]] && [ "$requested" -gt "$available" ]; then
|
|
printf '%s\n' "$available"
|
|
return 0
|
|
fi
|
|
printf '%s\n' "$requested"
|
|
}
|
|
|
|
docker_e2e_run_arg_present() {
|
|
local option="$1"
|
|
shift
|
|
local arg
|
|
for arg in "$@"; do
|
|
if [ "$arg" = "$option" ] || [[ "$arg" == "$option="* ]]; then
|
|
return 0
|
|
fi
|
|
case "$option:$arg" in
|
|
--memory:-m | --memory:-m=*)
|
|
return 0
|
|
;;
|
|
esac
|
|
done
|
|
return 1
|
|
}
|
|
|
|
docker_e2e_resolve_pids_limit() {
|
|
local pids_limit="$1"
|
|
local env_name="${2:-OPENCLAW_DOCKER_E2E_PIDS_LIMIT}"
|
|
if [[ ! "$pids_limit" =~ ^[0-9]+$ ]] || (( 10#$pids_limit < 1 )); then
|
|
echo "invalid $env_name: $pids_limit" >&2
|
|
return 2
|
|
fi
|
|
printf '%s\n' "$((10#$pids_limit))"
|
|
}
|
|
|
|
docker_e2e_docker_run_resource_args() {
|
|
DOCKER_E2E_RUN_RESOURCE_ARGS=()
|
|
docker_e2e_append_update_check_suppression "$@"
|
|
if docker_e2e_resource_limits_disabled; then
|
|
return 0
|
|
fi
|
|
|
|
local memory="${OPENCLAW_DOCKER_E2E_MEMORY:-8g}"
|
|
local cpus="${OPENCLAW_DOCKER_E2E_CPUS:-16}"
|
|
local pids_limit="${OPENCLAW_DOCKER_E2E_PIDS_LIMIT:-2048}"
|
|
cpus="$(docker_e2e_resolve_cpus "$cpus")"
|
|
|
|
if ! docker_e2e_resource_value_disabled "$memory" && ! docker_e2e_run_arg_present --memory "$@"; then
|
|
DOCKER_E2E_RUN_RESOURCE_ARGS+=(--memory "$memory")
|
|
fi
|
|
if ! docker_e2e_resource_value_disabled "$cpus" && ! docker_e2e_run_arg_present --cpus "$@"; then
|
|
DOCKER_E2E_RUN_RESOURCE_ARGS+=(--cpus "$cpus")
|
|
fi
|
|
if ! docker_e2e_resource_value_disabled "$pids_limit" && ! docker_e2e_run_arg_present --pids-limit "$@"; then
|
|
pids_limit="$(docker_e2e_resolve_pids_limit "$pids_limit")" || return $?
|
|
DOCKER_E2E_RUN_RESOURCE_ARGS+=(--pids-limit "$pids_limit")
|
|
fi
|
|
|
|
}
|
|
|
|
docker_e2e_run_env_present() {
|
|
local name="$1"
|
|
shift
|
|
local arg
|
|
local expect_value=0
|
|
for arg in "$@"; do
|
|
if [ "$expect_value" = 1 ]; then
|
|
expect_value=0
|
|
case "$arg" in
|
|
"$name" | "$name"=*)
|
|
return 0
|
|
;;
|
|
esac
|
|
continue
|
|
fi
|
|
case "$arg" in
|
|
-e | --env)
|
|
expect_value=1
|
|
;;
|
|
-e"$name" | -e"$name"=* | --env="$name" | --env="$name"=*)
|
|
return 0
|
|
;;
|
|
esac
|
|
done
|
|
return 1
|
|
}
|
|
|
|
# CI containers are not installations. The runner's own CI variable does not
|
|
# cross into `docker run`, so without this every E2E container reports a daily
|
|
# update check and drowns real operators in the telemetry aggregates. Callers
|
|
# that exercise update behavior pass their own value and keep it.
|
|
docker_e2e_append_update_check_suppression() {
|
|
if docker_e2e_run_env_present OPENCLAW_NO_AUTO_UPDATE "$@"; then
|
|
return 0
|
|
fi
|
|
DOCKER_E2E_RUN_RESOURCE_ARGS+=(-e OPENCLAW_NO_AUTO_UPDATE=1)
|
|
}
|
|
|
|
docker_e2e_container_running() {
|
|
local container_name="$1"
|
|
[ "$(docker_e2e_docker_cmd inspect -f '{{.State.Running}}' "$container_name" 2>/dev/null || echo false)" = "true" ]
|
|
}
|
|
|
|
docker_e2e_container_exec_bash() {
|
|
local container_name="$1"
|
|
shift
|
|
docker_e2e_docker_cmd exec "$container_name" bash -lc "$*"
|
|
}
|
|
|
|
docker_e2e_wait_for_proof() {
|
|
local container_name="$1"
|
|
local attempts="$2"
|
|
for _ in $(seq 1 "$attempts"); do
|
|
if docker exec "$container_name" test -f /tmp/openclaw-proof-ready; then
|
|
return 0
|
|
fi
|
|
if [ "$(docker inspect --format '{{.State.Running}}' "$container_name")" != "true" ]; then
|
|
docker logs "$container_name" >&2
|
|
return 1
|
|
fi
|
|
sleep 1
|
|
done
|
|
docker logs "$container_name" >&2
|
|
return 1
|
|
}
|
|
|
|
docker_e2e_wait_container_bash() {
|
|
local container_name="$1"
|
|
shift
|
|
docker_e2e_wait_container_bash_while_running "$container_name" "$container_name" "$@"
|
|
}
|
|
|
|
docker_e2e_wait_container_bash_while_running() {
|
|
local running_container_name="$1"
|
|
local exec_container_name="$2"
|
|
local attempts="$3"
|
|
local sleep_seconds="$4"
|
|
shift 4
|
|
local probe="$*"
|
|
|
|
for _ in $(seq 1 "$attempts"); do
|
|
if ! docker_e2e_container_running "$running_container_name"; then
|
|
return 1
|
|
fi
|
|
if docker_e2e_container_exec_bash "$exec_container_name" "$probe" >/dev/null 2>&1; then
|
|
return 0
|
|
fi
|
|
sleep "$sleep_seconds"
|
|
done
|
|
return 1
|
|
}
|
|
|
|
docker_e2e_tail_container_file_if_running() {
|
|
local container_name="$1"
|
|
local file_path="$2"
|
|
local lines="${3:-120}"
|
|
if docker_e2e_container_running "$container_name"; then
|
|
docker_e2e_container_exec_bash "$container_name" "tail -n $lines $file_path" || true
|
|
else
|
|
docker_e2e_docker_cmd logs "$container_name" 2>&1 | tail -n "$lines" || true
|
|
fi
|
|
}
|