mirror of
https://github.com/openclaw/openclaw.git
synced 2026-10-22 15:06:57 +00:00
Share repeated localization, CI and Docker planning, packaging, build, and validation mechanics behind explicit variant parameters. Remove 500 net production lines while preserving commands, output, selection order, timing budgets, and cleanup behavior. Replace internal scheduler source-spelling assertions with exact generated-command and captured-argument assertions in existing test cases. Keep the PR tooling import closure and concurrent deslop work untouched. Validation: independent review; localization and CI/Docker differential comparisons; all 997 script test files exercised with local failures retained in the PR evidence; focused base/head comparisons; current-head changed checks, affected-module tests, suppression checks, and zero-cycle checks. Hosted CI passes on the exact published head.
281 lines
7.9 KiB
Bash
281 lines
7.9 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() {
|
|
docker_e2e_docker_with_timeout "${DOCKER_COMMAND_TIMEOUT:-600s}" "$@"
|
|
}
|
|
|
|
docker_e2e_docker_run_cmd() {
|
|
docker_e2e_docker_with_timeout "${DOCKER_COMMAND_TIMEOUT:-${OPENCLAW_DOCKER_E2E_RUN_TIMEOUT:-3600s}}" "$@"
|
|
}
|
|
|
|
docker_e2e_docker_with_timeout() {
|
|
local timeout_value="$1"
|
|
shift
|
|
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_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
|
|
}
|