From 42de56cc22cb55b3fe07f879b254fc58eec48375 Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Mon, 27 Apr 2026 20:52:37 -0700 Subject: [PATCH] fix(ci): trust live docker harness scripts --- .../openclaw-live-and-e2e-checks-reusable.yml | 27 ++++++-- scripts/lib/docker-e2e-scenarios.mjs | 61 +++++++++++++------ scripts/test-docker-all.mjs | 6 +- scripts/test-live-acp-bind-docker.sh | 21 +++++-- scripts/test-live-cli-backend-docker.sh | 23 +++++-- scripts/test-live-codex-harness-docker.sh | 3 +- scripts/test-live-gateway-models-docker.sh | 21 +++++-- scripts/test-live-models-docker.sh | 21 +++++-- test/scripts/docker-build-helper.test.ts | 4 +- .../package-acceptance-workflow.test.ts | 47 +++++++++++++- 10 files changed, 188 insertions(+), 46 deletions(-) diff --git a/.github/workflows/openclaw-live-and-e2e-checks-reusable.yml b/.github/workflows/openclaw-live-and-e2e-checks-reusable.yml index 4bf5c1ee71c..155b25e2845 100644 --- a/.github/workflows/openclaw-live-and-e2e-checks-reusable.yml +++ b/.github/workflows/openclaw-live-and-e2e-checks-reusable.yml @@ -862,7 +862,7 @@ jobs: export OPENCLAW_DOCKER_ALL_TIMINGS_FILE=".artifacts/docker-tests/targeted-${{ steps.plan.outputs.artifact_suffix }}-timings.json" export OPENCLAW_DOCKER_ALL_PNPM_COMMAND="$(command -v pnpm)" if [[ "${{ steps.plan.outputs.needs_live_image }}" == "1" ]]; then - pnpm test:docker:live-build + OPENCLAW_LIVE_DOCKER_REPO_ROOT="$GITHUB_WORKSPACE" bash .release-harness/scripts/test-live-build-docker.sh fi export OPENCLAW_DOCKER_ALL_BUILD=0 @@ -1327,6 +1327,14 @@ jobs: ref: ${{ needs.validate_selected_ref.outputs.selected_sha }} fetch-depth: 1 + - name: Checkout trusted live Docker harness + if: contains(matrix.profiles, inputs.release_test_profile) + uses: actions/checkout@v6 + with: + ref: ${{ github.sha }} + fetch-depth: 1 + path: .release-harness + - name: Setup Node environment if: contains(matrix.profiles, inputs.release_test_profile) uses: ./.github/actions/setup-node-env @@ -1376,7 +1384,7 @@ jobs: - name: Run Docker live model sweep if: contains(matrix.profiles, inputs.release_test_profile) - run: pnpm test:docker:live-models + run: OPENCLAW_LIVE_DOCKER_REPO_ROOT="$GITHUB_WORKSPACE" bash .release-harness/scripts/test-live-models-docker.sh validate_live_models_docker_targeted: name: Docker live models (selected providers) @@ -1427,6 +1435,13 @@ jobs: ref: ${{ needs.validate_selected_ref.outputs.selected_sha }} fetch-depth: 1 + - name: Checkout trusted live Docker harness + uses: actions/checkout@v6 + with: + ref: ${{ github.sha }} + fetch-depth: 1 + path: .release-harness + - name: Setup Node environment uses: ./.github/actions/setup-node-env with: @@ -1534,7 +1549,7 @@ jobs: done - name: Run Docker live model sweep - run: pnpm test:docker:live-models + run: OPENCLAW_LIVE_DOCKER_REPO_ROOT="$GITHUB_WORKSPACE" bash .release-harness/scripts/test-live-models-docker.sh validate_live_provider_suites: needs: validate_selected_ref @@ -1715,21 +1730,21 @@ jobs: profiles: full - suite_id: live-gateway-docker label: Docker live gateway - command: pnpm test:docker:live-gateway + command: OPENCLAW_LIVE_DOCKER_REPO_ROOT="$GITHUB_WORKSPACE" bash .release-harness/scripts/test-live-gateway-models-docker.sh timeout_minutes: 120 needs_ffmpeg: false profile_env_only: false profiles: minimum stable full - suite_id: live-cli-backend-docker label: Docker live CLI backend - command: pnpm test:docker:live-cli-backend + command: OPENCLAW_LIVE_DOCKER_REPO_ROOT="$GITHUB_WORKSPACE" bash .release-harness/scripts/test-live-cli-backend-docker.sh timeout_minutes: 120 needs_ffmpeg: false profile_env_only: false profiles: stable full - suite_id: live-acp-bind-docker label: Docker live ACP bind - command: pnpm test:docker:live-acp-bind + command: OPENCLAW_LIVE_DOCKER_REPO_ROOT="$GITHUB_WORKSPACE" bash .release-harness/scripts/test-live-acp-bind-docker.sh timeout_minutes: 120 needs_ffmpeg: false profile_env_only: false diff --git a/scripts/lib/docker-e2e-scenarios.mjs b/scripts/lib/docker-e2e-scenarios.mjs index 5288fdb0014..415f071c88c 100644 --- a/scripts/lib/docker-e2e-scenarios.mjs +++ b/scripts/lib/docker-e2e-scenarios.mjs @@ -22,6 +22,11 @@ export const LIVE_RETRY_PATTERNS = [ const bundledChannelLaneCommand = "OPENCLAW_SKIP_DOCKER_BUILD=1 OPENCLAW_BUNDLED_CHANNEL_UPDATE_SCENARIO=0 OPENCLAW_BUNDLED_CHANNEL_ROOT_OWNED_SCENARIO=0 OPENCLAW_BUNDLED_CHANNEL_SETUP_ENTRY_SCENARIO=0 OPENCLAW_BUNDLED_CHANNEL_LOAD_FAILURE_SCENARIO=0 OPENCLAW_BUNDLED_CHANNEL_DISABLED_CONFIG_SCENARIO=0 pnpm test:docker:bundled-channel-deps"; +function liveDockerScriptCommand(script, envPrefix = "") { + const prefix = envPrefix ? `${envPrefix} ` : ""; + return `${prefix}OPENCLAW_SKIP_DOCKER_BUILD=1 bash -c 'harness="\${OPENCLAW_DOCKER_E2E_TRUSTED_HARNESS_DIR:-}"; if [ -z "$harness" ]; then if [ -d .release-harness/scripts ]; then harness=.release-harness; else harness=.; fi; fi; OPENCLAW_LIVE_DOCKER_REPO_ROOT="\${OPENCLAW_DOCKER_E2E_REPO_ROOT:-$PWD}" bash "$harness/scripts/${script}"'`; +} + function lane(name, command, options = {}) { return { cacheKey: options.cacheKey, @@ -170,19 +175,22 @@ const bundledPluginInstallUninstallLanes = Array.from( ); export const mainLanes = [ - liveLane("live-models", "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:live-models", { + liveLane("live-models", liveDockerScriptCommand("test-live-models-docker.sh"), { providers: ["claude-cli", "codex-cli", "google-gemini-cli"], timeoutMs: LIVE_PROFILE_TIMEOUT_MS, weight: 4, }), - liveLane("live-gateway", "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:live-gateway", { + liveLane("live-gateway", liveDockerScriptCommand("test-live-gateway-models-docker.sh"), { providers: ["claude-cli", "codex-cli", "google-gemini-cli"], timeoutMs: LIVE_PROFILE_TIMEOUT_MS, weight: 4, }), liveLane( "live-cli-backend-claude", - "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:live-cli-backend:claude", + liveDockerScriptCommand( + "test-live-cli-backend-docker.sh", + "OPENCLAW_LIVE_CLI_BACKEND_MODEL=claude-cli/claude-sonnet-4-6", + ), { cacheKey: "cli-backend-claude", provider: "claude-cli", @@ -193,7 +201,10 @@ export const mainLanes = [ ), liveLane( "live-cli-backend-gemini", - "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:live-cli-backend:gemini", + liveDockerScriptCommand( + "test-live-cli-backend-docker.sh", + "OPENCLAW_LIVE_CLI_BACKEND_MODEL=google-gemini-cli/gemini-3-flash-preview", + ), { cacheKey: "cli-backend-gemini", provider: "google-gemini-cli", @@ -281,9 +292,19 @@ export const tailLanes = [ "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:openai-web-search-minimal", { timeoutMs: 8 * 60 * 1000 }, ), + liveLane("live-codex-harness", liveDockerScriptCommand("test-live-codex-harness-docker.sh"), { + cacheKey: "codex-harness", + provider: "codex-cli", + resources: ["npm"], + timeoutMs: LIVE_ACP_TIMEOUT_MS, + weight: 3, + }), liveLane( - "live-codex-harness", - "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:live-codex-harness", + "live-codex-bind", + liveDockerScriptCommand( + "test-live-codex-harness-docker.sh", + "OPENCLAW_LIVE_CODEX_BIND=1 OPENCLAW_LIVE_CODEX_TEST_FILES=src/gateway/gateway-codex-bind.live.test.ts", + ), { cacheKey: "codex-harness", provider: "codex-cli", @@ -292,16 +313,12 @@ export const tailLanes = [ weight: 3, }, ), - liveLane("live-codex-bind", "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:live-codex-bind", { - cacheKey: "codex-harness", - provider: "codex-cli", - resources: ["npm"], - timeoutMs: LIVE_ACP_TIMEOUT_MS, - weight: 3, - }), liveLane( "live-cli-backend-codex", - "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:live-cli-backend:codex", + liveDockerScriptCommand( + "test-live-cli-backend-docker.sh", + "OPENCLAW_LIVE_CLI_BACKEND_MODEL=codex-cli/gpt-5.2", + ), { cacheKey: "cli-backend-codex", provider: "codex-cli", @@ -312,7 +329,7 @@ export const tailLanes = [ ), liveLane( "live-acp-bind-claude", - "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:live-acp-bind:claude", + liveDockerScriptCommand("test-live-acp-bind-docker.sh", "OPENCLAW_LIVE_ACP_BIND_AGENT=claude"), { cacheKey: "acp-bind-claude", provider: "claude-cli", @@ -323,7 +340,7 @@ export const tailLanes = [ ), liveLane( "live-acp-bind-codex", - "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:live-acp-bind:codex", + liveDockerScriptCommand("test-live-acp-bind-docker.sh", "OPENCLAW_LIVE_ACP_BIND_AGENT=codex"), { cacheKey: "acp-bind-codex", provider: "codex-cli", @@ -334,7 +351,10 @@ export const tailLanes = [ ), liveLane( "live-acp-bind-droid", - "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:live-acp-bind:droid", + liveDockerScriptCommand( + "test-live-acp-bind-docker.sh", + "OPENCLAW_LIVE_ACP_BIND_AGENT=droid OPENCLAW_LIVE_ACP_BIND_REQUIRE_TRANSCRIPT=1", + ), { cacheKey: "acp-bind-droid", provider: "droid", @@ -345,7 +365,7 @@ export const tailLanes = [ ), liveLane( "live-acp-bind-gemini", - "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:live-acp-bind:gemini", + liveDockerScriptCommand("test-live-acp-bind-docker.sh", "OPENCLAW_LIVE_ACP_BIND_AGENT=gemini"), { cacheKey: "acp-bind-gemini", provider: "google-gemini-cli", @@ -356,7 +376,10 @@ export const tailLanes = [ ), liveLane( "live-acp-bind-opencode", - "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:live-acp-bind:opencode", + liveDockerScriptCommand( + "test-live-acp-bind-docker.sh", + "OPENCLAW_LIVE_ACP_BIND_AGENT=opencode OPENCLAW_LIVE_ACP_BIND_REQUIRE_TRANSCRIPT=1", + ), { cacheKey: "acp-bind-opencode", provider: "opencode", diff --git a/scripts/test-docker-all.mjs b/scripts/test-docker-all.mjs index 33e4853e9e6..f1cb6e8fc82 100644 --- a/scripts/test-docker-all.mjs +++ b/scripts/test-docker-all.mjs @@ -268,6 +268,10 @@ function withResolvedPnpmCommand(command, env) { return command.replace(/(^|\s)pnpm(?=\s)/g, `$1${shellQuote(pnpmCommand)}`); } +function liveDockerHarnessScriptCommand(script) { + return `bash -c 'harness="\${OPENCLAW_DOCKER_E2E_TRUSTED_HARNESS_DIR:-}"; if [ -z "$harness" ]; then if [ -d .release-harness/scripts ]; then harness=.release-harness; else harness=.; fi; fi; OPENCLAW_LIVE_DOCKER_REPO_ROOT="\${OPENCLAW_DOCKER_E2E_REPO_ROOT:-$PWD}" bash "$harness/scripts/${script}"'`; +} + async function loadTimingStore(file, enabled) { if (!enabled) { return { enabled: false, file, lanes: {}, version: 1 }; @@ -1134,7 +1138,7 @@ async function main() { const buildEntries = []; if (scheduledLanes.some((poolLane) => poolLane.live)) { buildEntries.push({ - command: "pnpm test:docker:live-build", + command: liveDockerHarnessScriptCommand("test-live-build-docker.sh"), label: "shared live-test image once", phaseDetails: { imageKind: "live" }, phases, diff --git a/scripts/test-live-acp-bind-docker.sh b/scripts/test-live-acp-bind-docker.sh index a8e145cb127..aabe8b2aad4 100644 --- a/scripts/test-live-acp-bind-docker.sh +++ b/scripts/test-live-acp-bind-docker.sh @@ -1,8 +1,16 @@ #!/usr/bin/env bash set -euo pipefail -ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" -source "$ROOT_DIR/scripts/lib/live-docker-auth.sh" +SCRIPT_ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +ROOT_DIR="${OPENCLAW_LIVE_DOCKER_REPO_ROOT:-$SCRIPT_ROOT_DIR}" +ROOT_DIR="$(cd "$ROOT_DIR" && pwd)" +TRUSTED_HARNESS_DIR="${OPENCLAW_LIVE_DOCKER_TRUSTED_HARNESS_DIR:-$SCRIPT_ROOT_DIR}" +if [[ -z "$TRUSTED_HARNESS_DIR" || ! -d "$TRUSTED_HARNESS_DIR" ]]; then + echo "ERROR: trusted live Docker harness directory not found: ${TRUSTED_HARNESS_DIR:-}." >&2 + exit 1 +fi +TRUSTED_HARNESS_DIR="$(cd "$TRUSTED_HARNESS_DIR" && pwd)" +source "$TRUSTED_HARNESS_DIR/scripts/lib/live-docker-auth.sh" IMAGE_NAME="${OPENCLAW_IMAGE:-openclaw:local}" LIVE_IMAGE_NAME="${OPENCLAW_LIVE_IMAGE:-${IMAGE_NAME}-live}" CONFIG_DIR="${OPENCLAW_CONFIG_DIR:-$HOME/.openclaw}" @@ -13,6 +21,8 @@ TEMP_DIRS=() DOCKER_USER="${OPENCLAW_DOCKER_USER:-node}" DOCKER_HOME_MOUNT=() DOCKER_AUTH_PRESTAGED=0 +DOCKER_TRUSTED_HARNESS_CONTAINER_DIR="/trusted-harness" +DOCKER_TRUSTED_HARNESS_MOUNT=(-v "$TRUSTED_HARNESS_DIR":"$DOCKER_TRUSTED_HARNESS_CONTAINER_DIR":ro) openclaw_live_acp_bind_append_build_extension() { local extension="${1:?extension required}" @@ -213,7 +223,8 @@ NODE ;; esac tmp_dir="$(mktemp -d)" -source /src/scripts/lib/live-docker-stage.sh +trusted_scripts_dir="${OPENCLAW_LIVE_DOCKER_SCRIPTS_DIR:-/src/scripts}" +source "$trusted_scripts_dir/lib/live-docker-stage.sh" openclaw_live_stage_source_tree "$tmp_dir" openclaw_live_stage_node_modules "$tmp_dir" openclaw_live_link_runtime_tree "$tmp_dir" @@ -225,7 +236,7 @@ pnpm test:live src/gateway/gateway-acp-bind.live.test.ts EOF openclaw_live_acp_bind_append_build_extension acpx -"$ROOT_DIR/scripts/test-live-build-docker.sh" +OPENCLAW_LIVE_DOCKER_REPO_ROOT="$ROOT_DIR" "$TRUSTED_HARNESS_DIR/scripts/test-live-build-docker.sh" IFS=',' read -r -a ACP_AGENT_TOKENS <<<"$ACP_AGENT_LIST_RAW" ACP_AGENTS=() @@ -346,6 +357,7 @@ for ACP_AGENT in "${ACP_AGENTS[@]}"; do -e OPENCLAW_DOCKER_AUTH_PRESTAGED="$DOCKER_AUTH_PRESTAGED" \ -e OPENCLAW_DOCKER_AUTH_DIRS_RESOLVED="$AUTH_DIRS_CSV" \ -e OPENCLAW_DOCKER_AUTH_FILES_RESOLVED="$AUTH_FILES_CSV" \ + -e OPENCLAW_LIVE_DOCKER_SCRIPTS_DIR="${DOCKER_TRUSTED_HARNESS_CONTAINER_DIR}/scripts" \ -e OPENCLAW_LIVE_DOCKER_SOURCE_STAGE_MODE="${OPENCLAW_LIVE_DOCKER_SOURCE_STAGE_MODE:-copy}" \ -e OPENCLAW_LIVE_TEST=1 \ -e OPENCLAW_LIVE_ACP_BIND=1 \ @@ -353,6 +365,7 @@ for ACP_AGENT in "${ACP_AGENTS[@]}"; do -e OPENCLAW_LIVE_ACP_BIND_OPENCODE_MODEL="${OPENCLAW_LIVE_ACP_BIND_OPENCODE_MODEL:-opencode/kimi-k2.6}" \ -e OPENCLAW_LIVE_ACP_BIND_AGENT_COMMAND="$AGENT_COMMAND") openclaw_live_append_array DOCKER_RUN_ARGS DOCKER_HOME_MOUNT + openclaw_live_append_array DOCKER_RUN_ARGS DOCKER_TRUSTED_HARNESS_MOUNT DOCKER_RUN_ARGS+=(\ -v "$CACHE_HOME_DIR":/home/node/.cache \ -v "$ROOT_DIR":/src:ro \ diff --git a/scripts/test-live-cli-backend-docker.sh b/scripts/test-live-cli-backend-docker.sh index c4fcfc57700..b6f8ae1ca7f 100644 --- a/scripts/test-live-cli-backend-docker.sh +++ b/scripts/test-live-cli-backend-docker.sh @@ -1,8 +1,16 @@ #!/usr/bin/env bash set -euo pipefail -ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" -source "$ROOT_DIR/scripts/lib/live-docker-auth.sh" +SCRIPT_ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +ROOT_DIR="${OPENCLAW_LIVE_DOCKER_REPO_ROOT:-$SCRIPT_ROOT_DIR}" +ROOT_DIR="$(cd "$ROOT_DIR" && pwd)" +TRUSTED_HARNESS_DIR="${OPENCLAW_LIVE_DOCKER_TRUSTED_HARNESS_DIR:-$SCRIPT_ROOT_DIR}" +if [[ -z "$TRUSTED_HARNESS_DIR" || ! -d "$TRUSTED_HARNESS_DIR" ]]; then + echo "ERROR: trusted live Docker harness directory not found: ${TRUSTED_HARNESS_DIR:-}." >&2 + exit 1 +fi +TRUSTED_HARNESS_DIR="$(cd "$TRUSTED_HARNESS_DIR" && pwd)" +source "$TRUSTED_HARNESS_DIR/scripts/lib/live-docker-auth.sh" IMAGE_NAME="${OPENCLAW_IMAGE:-openclaw:local}" LIVE_IMAGE_NAME="${OPENCLAW_LIVE_IMAGE:-${IMAGE_NAME}-live}" CONFIG_DIR="${OPENCLAW_CONFIG_DIR:-$HOME/.openclaw}" @@ -19,6 +27,8 @@ DOCKER_USER="${OPENCLAW_DOCKER_USER:-node}" DOCKER_HOME_MOUNT=() DOCKER_EXTRA_ENV_FILES=() DOCKER_AUTH_PRESTAGED=0 +DOCKER_TRUSTED_HARNESS_CONTAINER_DIR="/trusted-harness" +DOCKER_TRUSTED_HARNESS_MOUNT=(-v "$TRUSTED_HARNESS_DIR":"$DOCKER_TRUSTED_HARNESS_CONTAINER_DIR":ro) if [[ -z "$CLI_PROVIDER" || "$CLI_PROVIDER" == "$CLI_MODEL" ]]; then CLI_PROVIDER="$DEFAULT_PROVIDER" @@ -375,7 +385,8 @@ WRAP fi fi tmp_dir="$(mktemp -d)" -source /src/scripts/lib/live-docker-stage.sh +trusted_scripts_dir="${OPENCLAW_LIVE_DOCKER_SCRIPTS_DIR:-/src/scripts}" +source "$trusted_scripts_dir/lib/live-docker-stage.sh" openclaw_live_stage_source_tree "$tmp_dir" # Use a writable node_modules overlay in the temp repo. Vite writes bundled # config artifacts under the nearest node_modules/.vite-temp path, and the @@ -386,12 +397,12 @@ openclaw_live_stage_state_dir "$tmp_dir/.openclaw-state" openclaw_live_prepare_staged_config cd "$tmp_dir" if [ "${OPENCLAW_LIVE_CLI_BACKEND_USE_CI_SAFE_CODEX_CONFIG:-0}" = "1" ]; then - node --import tsx /src/scripts/prepare-codex-ci-config.ts "$HOME/.codex/config.toml" "$tmp_dir" + node --import tsx "$trusted_scripts_dir/prepare-codex-ci-config.ts" "$HOME/.codex/config.toml" "$tmp_dir" fi pnpm test:live src/gateway/gateway-cli-backend.live.test.ts EOF -"$ROOT_DIR/scripts/test-live-build-docker.sh" +OPENCLAW_LIVE_DOCKER_REPO_ROOT="$ROOT_DIR" "$TRUSTED_HARNESS_DIR/scripts/test-live-build-docker.sh" echo "==> Run CLI backend live test in Docker" echo "==> Model: $CLI_MODEL" @@ -448,6 +459,7 @@ DOCKER_RUN_ARGS=(docker run --rm -t \ -e OPENCLAW_DOCKER_AUTH_PRESTAGED="$DOCKER_AUTH_PRESTAGED" \ -e OPENCLAW_DOCKER_AUTH_DIRS_RESOLVED="$AUTH_DIRS_CSV" \ -e OPENCLAW_DOCKER_AUTH_FILES_RESOLVED="$AUTH_FILES_CSV" \ + -e OPENCLAW_LIVE_DOCKER_SCRIPTS_DIR="${DOCKER_TRUSTED_HARNESS_CONTAINER_DIR}/scripts" \ -e OPENCLAW_LIVE_DOCKER_SOURCE_STAGE_MODE="${OPENCLAW_LIVE_DOCKER_SOURCE_STAGE_MODE:-copy}" \ -e OPENCLAW_LIVE_CLI_BACKEND_USE_CI_SAFE_CODEX_CONFIG="$CLI_USE_CI_SAFE_CODEX_CONFIG" \ -e OPENCLAW_LIVE_CLI_BACKEND_SETUP_TIMEOUT_SECONDS="$CLI_SETUP_TIMEOUT_SECONDS" \ @@ -474,6 +486,7 @@ DOCKER_RUN_ARGS=(docker run --rm -t \ -e OPENCLAW_LIVE_CLI_BACKEND_IMAGE_MODE="${OPENCLAW_LIVE_CLI_BACKEND_IMAGE_MODE:-}") openclaw_live_append_array DOCKER_RUN_ARGS DOCKER_HOME_MOUNT openclaw_live_append_array DOCKER_RUN_ARGS DOCKER_EXTRA_ENV_FILES +openclaw_live_append_array DOCKER_RUN_ARGS DOCKER_TRUSTED_HARNESS_MOUNT DOCKER_RUN_ARGS+=(\ -v "$CACHE_HOME_DIR":/home/node/.cache \ -v "$ROOT_DIR":/src:ro \ diff --git a/scripts/test-live-codex-harness-docker.sh b/scripts/test-live-codex-harness-docker.sh index ea70001cc09..1c236aa2010 100644 --- a/scripts/test-live-codex-harness-docker.sh +++ b/scripts/test-live-codex-harness-docker.sh @@ -4,7 +4,7 @@ set -euo pipefail SCRIPT_ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" ROOT_DIR="${OPENCLAW_LIVE_DOCKER_REPO_ROOT:-$SCRIPT_ROOT_DIR}" ROOT_DIR="$(cd "$ROOT_DIR" && pwd)" -TRUSTED_HARNESS_DIR="${OPENCLAW_LIVE_CODEX_TRUSTED_HARNESS_DIR:-$SCRIPT_ROOT_DIR}" +TRUSTED_HARNESS_DIR="${OPENCLAW_LIVE_DOCKER_TRUSTED_HARNESS_DIR:-${OPENCLAW_LIVE_CODEX_TRUSTED_HARNESS_DIR:-$SCRIPT_ROOT_DIR}}" if [[ -z "$TRUSTED_HARNESS_DIR" || ! -d "$TRUSTED_HARNESS_DIR" ]]; then echo "ERROR: trusted Codex harness directory not found: ${TRUSTED_HARNESS_DIR:-}." >&2 exit 1 @@ -258,6 +258,7 @@ DOCKER_RUN_ARGS=(docker run --rm -t \ -e OPENCLAW_LIVE_CODEX_HARNESS_SUBAGENT_PROBE="${OPENCLAW_LIVE_CODEX_HARNESS_SUBAGENT_PROBE:-1}" \ -e OPENCLAW_LIVE_CODEX_HARNESS_USE_CI_SAFE_CODEX_CONFIG="${OPENCLAW_LIVE_CODEX_HARNESS_USE_CI_SAFE_CODEX_CONFIG:-1}" \ -e OPENCLAW_LIVE_DOCKER_SCRIPTS_DIR="${DOCKER_TRUSTED_HARNESS_CONTAINER_DIR}/scripts" \ + -e OPENCLAW_LIVE_DOCKER_TRUSTED_HARNESS_DIR="$DOCKER_TRUSTED_HARNESS_CONTAINER_DIR" \ -e OPENCLAW_LIVE_CODEX_TRUSTED_HARNESS_DIR="$DOCKER_TRUSTED_HARNESS_CONTAINER_DIR" \ -e OPENCLAW_LIVE_CODEX_BIND="${OPENCLAW_LIVE_CODEX_BIND:-}" \ -e OPENCLAW_LIVE_CODEX_BIND_MODEL="${OPENCLAW_LIVE_CODEX_BIND_MODEL:-}" \ diff --git a/scripts/test-live-gateway-models-docker.sh b/scripts/test-live-gateway-models-docker.sh index c87edae40c9..68b71423c6a 100755 --- a/scripts/test-live-gateway-models-docker.sh +++ b/scripts/test-live-gateway-models-docker.sh @@ -1,8 +1,16 @@ #!/usr/bin/env bash set -euo pipefail -ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" -source "$ROOT_DIR/scripts/lib/live-docker-auth.sh" +SCRIPT_ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +ROOT_DIR="${OPENCLAW_LIVE_DOCKER_REPO_ROOT:-$SCRIPT_ROOT_DIR}" +ROOT_DIR="$(cd "$ROOT_DIR" && pwd)" +TRUSTED_HARNESS_DIR="${OPENCLAW_LIVE_DOCKER_TRUSTED_HARNESS_DIR:-$SCRIPT_ROOT_DIR}" +if [[ -z "$TRUSTED_HARNESS_DIR" || ! -d "$TRUSTED_HARNESS_DIR" ]]; then + echo "ERROR: trusted live Docker harness directory not found: ${TRUSTED_HARNESS_DIR:-}." >&2 + exit 1 +fi +TRUSTED_HARNESS_DIR="$(cd "$TRUSTED_HARNESS_DIR" && pwd)" +source "$TRUSTED_HARNESS_DIR/scripts/lib/live-docker-auth.sh" IMAGE_NAME="${OPENCLAW_IMAGE:-openclaw:local}" LIVE_IMAGE_NAME="${OPENCLAW_LIVE_IMAGE:-${IMAGE_NAME}-live}" CONFIG_DIR="${OPENCLAW_CONFIG_DIR:-$HOME/.openclaw}" @@ -12,6 +20,8 @@ DOCKER_USER="${OPENCLAW_DOCKER_USER:-node}" TEMP_DIRS=() DOCKER_HOME_MOUNT=() DOCKER_AUTH_PRESTAGED=0 +DOCKER_TRUSTED_HARNESS_CONTAINER_DIR="/trusted-harness" +DOCKER_TRUSTED_HARNESS_MOUNT=(-v "$TRUSTED_HARNESS_DIR":"$DOCKER_TRUSTED_HARNESS_CONTAINER_DIR":ro) cleanup_temp_dirs() { if ((${#TEMP_DIRS[@]} > 0)); then rm -rf "${TEMP_DIRS[@]}" @@ -139,7 +149,8 @@ if [ "${OPENCLAW_DOCKER_AUTH_PRESTAGED:-0}" != "1" ]; then fi fi tmp_dir="$(mktemp -d)" -source /src/scripts/lib/live-docker-stage.sh +trusted_scripts_dir="${OPENCLAW_LIVE_DOCKER_SCRIPTS_DIR:-/src/scripts}" +source "$trusted_scripts_dir/lib/live-docker-stage.sh" openclaw_live_stage_source_tree "$tmp_dir" openclaw_live_stage_node_modules "$tmp_dir" openclaw_live_link_runtime_tree "$tmp_dir" @@ -149,7 +160,7 @@ cd "$tmp_dir" pnpm test:live:gateway-profiles EOF -"$ROOT_DIR/scripts/test-live-build-docker.sh" +OPENCLAW_LIVE_DOCKER_REPO_ROOT="$ROOT_DIR" "$TRUSTED_HARNESS_DIR/scripts/test-live-build-docker.sh" echo "==> Run gateway live model tests (profile keys)" echo "==> Target: src/gateway/gateway-models.profiles.live.test.ts" @@ -167,6 +178,7 @@ DOCKER_RUN_ARGS=(docker run --rm -t \ -e OPENCLAW_DOCKER_AUTH_PRESTAGED="$DOCKER_AUTH_PRESTAGED" \ -e OPENCLAW_DOCKER_AUTH_DIRS_RESOLVED="$AUTH_DIRS_CSV" \ -e OPENCLAW_DOCKER_AUTH_FILES_RESOLVED="$AUTH_FILES_CSV" \ + -e OPENCLAW_LIVE_DOCKER_SCRIPTS_DIR="${DOCKER_TRUSTED_HARNESS_CONTAINER_DIR}/scripts" \ -e OPENCLAW_LIVE_DOCKER_SOURCE_STAGE_MODE="${OPENCLAW_LIVE_DOCKER_SOURCE_STAGE_MODE:-copy}" \ -e OPENCLAW_LIVE_TEST=1 \ -e OPENCLAW_LIVE_GATEWAY_MODELS="${OPENCLAW_LIVE_GATEWAY_MODELS:-modern}" \ @@ -176,6 +188,7 @@ DOCKER_RUN_ARGS=(docker run --rm -t \ -e OPENCLAW_LIVE_GATEWAY_STEP_TIMEOUT_MS="${OPENCLAW_LIVE_GATEWAY_STEP_TIMEOUT_MS:-45000}" \ -e OPENCLAW_LIVE_GATEWAY_MODEL_TIMEOUT_MS="${OPENCLAW_LIVE_GATEWAY_MODEL_TIMEOUT_MS:-90000}") openclaw_live_append_array DOCKER_RUN_ARGS DOCKER_HOME_MOUNT +openclaw_live_append_array DOCKER_RUN_ARGS DOCKER_TRUSTED_HARNESS_MOUNT DOCKER_RUN_ARGS+=(\ -v "$CACHE_HOME_DIR":/home/node/.cache \ -v "$ROOT_DIR":/src:ro \ diff --git a/scripts/test-live-models-docker.sh b/scripts/test-live-models-docker.sh index 37ccf3c2124..0c8e4ab2b03 100755 --- a/scripts/test-live-models-docker.sh +++ b/scripts/test-live-models-docker.sh @@ -1,13 +1,23 @@ #!/usr/bin/env bash set -euo pipefail -ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" -source "$ROOT_DIR/scripts/lib/live-docker-auth.sh" +SCRIPT_ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +ROOT_DIR="${OPENCLAW_LIVE_DOCKER_REPO_ROOT:-$SCRIPT_ROOT_DIR}" +ROOT_DIR="$(cd "$ROOT_DIR" && pwd)" +TRUSTED_HARNESS_DIR="${OPENCLAW_LIVE_DOCKER_TRUSTED_HARNESS_DIR:-$SCRIPT_ROOT_DIR}" +if [[ -z "$TRUSTED_HARNESS_DIR" || ! -d "$TRUSTED_HARNESS_DIR" ]]; then + echo "ERROR: trusted live Docker harness directory not found: ${TRUSTED_HARNESS_DIR:-}." >&2 + exit 1 +fi +TRUSTED_HARNESS_DIR="$(cd "$TRUSTED_HARNESS_DIR" && pwd)" +source "$TRUSTED_HARNESS_DIR/scripts/lib/live-docker-auth.sh" IMAGE_NAME="${OPENCLAW_IMAGE:-openclaw:local}" LIVE_IMAGE_NAME="${OPENCLAW_LIVE_IMAGE:-${IMAGE_NAME}-live}" PROFILE_FILE="${OPENCLAW_PROFILE_FILE:-$HOME/.profile}" DOCKER_USER="${OPENCLAW_DOCKER_USER:-node}" DOCKER_AUTH_PRESTAGED=0 +DOCKER_TRUSTED_HARNESS_CONTAINER_DIR="/trusted-harness" +DOCKER_TRUSTED_HARNESS_MOUNT=(-v "$TRUSTED_HARNESS_DIR":"$DOCKER_TRUSTED_HARNESS_CONTAINER_DIR":ro) openclaw_live_truthy() { case "${1:-}" in @@ -169,7 +179,8 @@ if [ "${OPENCLAW_DOCKER_AUTH_PRESTAGED:-0}" != "1" ]; then fi fi tmp_dir="$(mktemp -d)" -source /src/scripts/lib/live-docker-stage.sh +trusted_scripts_dir="${OPENCLAW_LIVE_DOCKER_SCRIPTS_DIR:-/src/scripts}" +source "$trusted_scripts_dir/lib/live-docker-stage.sh" openclaw_live_stage_source_tree "$tmp_dir" openclaw_live_stage_node_modules "$tmp_dir" openclaw_live_link_runtime_tree "$tmp_dir" @@ -179,7 +190,7 @@ cd "$tmp_dir" pnpm test:live:models-profiles EOF -"$ROOT_DIR/scripts/test-live-build-docker.sh" +OPENCLAW_LIVE_DOCKER_REPO_ROOT="$ROOT_DIR" "$TRUSTED_HARNESS_DIR/scripts/test-live-build-docker.sh" echo "==> Run live model tests (profile keys)" echo "==> Target: src/agents/models.profiles.live.test.ts" @@ -198,6 +209,7 @@ DOCKER_RUN_ARGS=(docker run --rm -t \ -e OPENCLAW_DOCKER_AUTH_PRESTAGED="$DOCKER_AUTH_PRESTAGED" \ -e OPENCLAW_DOCKER_AUTH_DIRS_RESOLVED="$AUTH_DIRS_CSV" \ -e OPENCLAW_DOCKER_AUTH_FILES_RESOLVED="$AUTH_FILES_CSV" \ + -e OPENCLAW_LIVE_DOCKER_SCRIPTS_DIR="${DOCKER_TRUSTED_HARNESS_CONTAINER_DIR}/scripts" \ -e OPENCLAW_LIVE_DOCKER_SOURCE_STAGE_MODE="${OPENCLAW_LIVE_DOCKER_SOURCE_STAGE_MODE:-copy}" \ -e OPENCLAW_LIVE_TEST=1 \ -e OPENCLAW_LIVE_MODELS="${OPENCLAW_LIVE_MODELS:-modern}" \ @@ -209,6 +221,7 @@ DOCKER_RUN_ARGS=(docker run --rm -t \ -e OPENCLAW_LIVE_GATEWAY_PROVIDERS="${OPENCLAW_LIVE_GATEWAY_PROVIDERS:-}" \ -e OPENCLAW_LIVE_GATEWAY_MAX_MODELS="${OPENCLAW_LIVE_GATEWAY_MAX_MODELS:-}") openclaw_live_append_array DOCKER_RUN_ARGS DOCKER_HOME_MOUNT +openclaw_live_append_array DOCKER_RUN_ARGS DOCKER_TRUSTED_HARNESS_MOUNT DOCKER_RUN_ARGS+=(\ -v "$CACHE_HOME_DIR":/home/node/.cache \ -v "$ROOT_DIR":/src:ro \ diff --git a/test/scripts/docker-build-helper.test.ts b/test/scripts/docker-build-helper.test.ts index 85195f1b502..fe89167b684 100644 --- a/test/scripts/docker-build-helper.test.ts +++ b/test/scripts/docker-build-helper.test.ts @@ -66,7 +66,9 @@ describe("docker build helper", () => { expect(liveBuild).toContain("docker image inspect"); expect(liveBuild).toContain("docker pull"); expect(liveBuild).toContain("Live-test image not available; building"); - expect(liveCliBackend).toContain('"$ROOT_DIR/scripts/test-live-build-docker.sh"'); + expect(liveCliBackend).toContain( + 'OPENCLAW_LIVE_DOCKER_REPO_ROOT="$ROOT_DIR" "$TRUSTED_HARNESS_DIR/scripts/test-live-build-docker.sh"', + ); expect(liveCliBackend).not.toContain( 'echo "==> Reuse live-test image: $LIVE_IMAGE_NAME (OPENCLAW_SKIP_DOCKER_BUILD=1)"', ); diff --git a/test/scripts/package-acceptance-workflow.test.ts b/test/scripts/package-acceptance-workflow.test.ts index b096e3a9329..8528ee8920a 100644 --- a/test/scripts/package-acceptance-workflow.test.ts +++ b/test/scripts/package-acceptance-workflow.test.ts @@ -129,15 +129,46 @@ describe("package artifact reuse", () => { expect(workflow).toContain("if: matrix.needs_ffmpeg"); }); - it("runs the Codex Docker live harness from trusted helper scripts", () => { + it("runs Docker live harnesses from trusted helper scripts", () => { const workflow = readFileSync(LIVE_E2E_WORKFLOW, "utf8"); + const scenarios = readFileSync("scripts/lib/docker-e2e-scenarios.mjs", "utf8"); + const scheduler = readFileSync("scripts/test-docker-all.mjs", "utf8"); const harness = readFileSync("scripts/test-live-codex-harness-docker.sh", "utf8"); + const sharedLiveScripts = [ + readFileSync("scripts/test-live-models-docker.sh", "utf8"), + readFileSync("scripts/test-live-gateway-models-docker.sh", "utf8"), + readFileSync("scripts/test-live-cli-backend-docker.sh", "utf8"), + readFileSync("scripts/test-live-acp-bind-docker.sh", "utf8"), + ]; const build = readFileSync("scripts/test-live-build-docker.sh", "utf8"); const stage = readFileSync("scripts/lib/live-docker-stage.sh", "utf8"); + expect(workflow).toContain( + 'run: OPENCLAW_LIVE_DOCKER_REPO_ROOT="$GITHUB_WORKSPACE" bash .release-harness/scripts/test-live-models-docker.sh', + ); + expect(workflow).toContain( + 'command: OPENCLAW_LIVE_DOCKER_REPO_ROOT="$GITHUB_WORKSPACE" bash .release-harness/scripts/test-live-gateway-models-docker.sh', + ); + expect(workflow).toContain( + 'command: OPENCLAW_LIVE_DOCKER_REPO_ROOT="$GITHUB_WORKSPACE" bash .release-harness/scripts/test-live-cli-backend-docker.sh', + ); + expect(workflow).toContain( + 'command: OPENCLAW_LIVE_DOCKER_REPO_ROOT="$GITHUB_WORKSPACE" bash .release-harness/scripts/test-live-acp-bind-docker.sh', + ); expect(workflow).toContain( 'command: OPENCLAW_LIVE_DOCKER_REPO_ROOT="$GITHUB_WORKSPACE" bash .release-harness/scripts/test-live-codex-harness-docker.sh', ); + expect(scenarios).toContain("function liveDockerScriptCommand"); + expect(scenarios).toContain( + "if [ -d .release-harness/scripts ]; then harness=.release-harness", + ); + expect(scenarios).toMatch(/liveDockerScriptCommand\(\s*"test-live-models-docker\.sh"/u); + expect(scenarios).toMatch(/liveDockerScriptCommand\(\s*"test-live-gateway-models-docker\.sh"/u); + expect(scenarios).toMatch(/liveDockerScriptCommand\(\s*"test-live-cli-backend-docker\.sh"/u); + expect(scenarios).toMatch(/liveDockerScriptCommand\(\s*"test-live-acp-bind-docker\.sh"/u); + expect(scenarios).toMatch(/liveDockerScriptCommand\(\s*"test-live-codex-harness-docker\.sh"/u); + expect(scheduler).toContain("function liveDockerHarnessScriptCommand"); + expect(scheduler).toContain('liveDockerHarnessScriptCommand("test-live-build-docker.sh")'); expect(harness).toContain('source "$TRUSTED_HARNESS_DIR/scripts/lib/live-docker-auth.sh"'); expect(harness).not.toContain('source "$ROOT_DIR/scripts/lib/live-docker-auth.sh"'); expect(harness).toContain( @@ -148,6 +179,20 @@ describe("package artifact reuse", () => { ); expect(harness).toContain('node --import tsx "$trusted_scripts_dir/prepare-codex-ci-auth.ts"'); expect(harness).toContain('source "$trusted_scripts_dir/lib/live-docker-stage.sh"'); + for (const script of sharedLiveScripts) { + expect(script).toContain('source "$TRUSTED_HARNESS_DIR/scripts/lib/live-docker-auth.sh"'); + expect(script).not.toContain('source "$ROOT_DIR/scripts/lib/live-docker-auth.sh"'); + expect(script).toContain( + 'OPENCLAW_LIVE_DOCKER_REPO_ROOT="$ROOT_DIR" "$TRUSTED_HARNESS_DIR/scripts/test-live-build-docker.sh"', + ); + expect(script).toContain('source "$trusted_scripts_dir/lib/live-docker-stage.sh"'); + expect(script).toContain( + '-e OPENCLAW_LIVE_DOCKER_SCRIPTS_DIR="${DOCKER_TRUSTED_HARNESS_CONTAINER_DIR}/scripts"', + ); + expect(script).toContain( + "openclaw_live_append_array DOCKER_RUN_ARGS DOCKER_TRUSTED_HARNESS_MOUNT", + ); + } expect(build).toContain('ROOT_DIR="${OPENCLAW_LIVE_DOCKER_REPO_ROOT:-$SCRIPT_ROOT_DIR}"'); expect(build).toContain('source "$SCRIPT_ROOT_DIR/scripts/lib/docker-build.sh"'); expect(stage).toContain(