mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-12 21:53:00 -06:00
test(qa): cover CLI distribution workflows (#118948)
This commit is contained in:
@@ -1782,6 +1782,7 @@
|
||||
"test:docker:live-models:gemini": "OPENCLAW_LIVE_PROVIDERS=google-gemini-cli OPENCLAW_LIVE_MODELS=google-gemini-cli/gemini-3.1-pro-preview bash scripts/test-live-models-docker.sh",
|
||||
"test:docker:live:all": "node scripts/run-with-env.mjs OPENCLAW_DOCKER_ALL_LIVE_MODE=only -- node scripts/test-docker-all.mjs",
|
||||
"test:docker:local:all": "node scripts/run-with-env.mjs OPENCLAW_DOCKER_ALL_LIVE_MODE=skip -- node scripts/test-docker-all.mjs",
|
||||
"test:docker:cli-installer-distribution": "bash scripts/e2e/cli-installer-distribution-docker.sh",
|
||||
"test:docker:mcp-channels": "bash scripts/e2e/mcp-channels-docker.sh",
|
||||
"test:docker:mcp-code-mode-gateway": "bash scripts/e2e/mcp-code-mode-gateway-docker.sh",
|
||||
"test:docker:multi-node-update": "bash scripts/e2e/multi-node-update-docker.sh",
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
title: CLI installer distribution
|
||||
|
||||
scenario:
|
||||
id: cli-installer-distribution
|
||||
surface: cli
|
||||
category: cli.cli-setup
|
||||
coverage:
|
||||
primary:
|
||||
- cli.local-prefix-install
|
||||
- cli.source-checkout-install
|
||||
secondary:
|
||||
- cli.installer-scripts
|
||||
objective: Verify the hosted installer installs an exact package candidate and the CLI installer builds an exact source checkout into a dedicated Node and OpenClaw prefix.
|
||||
successCriteria:
|
||||
- "`install.sh` validates the supported Node runtime, installs the exact package candidate, honors no-onboard mode, and exposes a PATH-resolved CLI."
|
||||
- "`install-cli.sh` provisions prefix-owned Node and an OpenClaw wrapper without using the system prefix."
|
||||
- The source checkout is pinned to the exact tested commit, builds successfully, and the wrapper targets its built entrypoint.
|
||||
- The installed source CLI serves help and version and reports git install kind through update status.
|
||||
docsRefs:
|
||||
- docs/install/index.md
|
||||
- docs/install/updating.md
|
||||
codeRefs:
|
||||
- scripts/install.sh
|
||||
- scripts/install-cli.sh
|
||||
- scripts/e2e/cli-installer-distribution-docker.sh
|
||||
execution:
|
||||
kind: script
|
||||
path: test/e2e/qa-lab/runtime/docker-e2e-lane.ts
|
||||
summary: Runs exact-candidate hosted-package and dedicated-prefix source-checkout installer proofs in clean Docker containers.
|
||||
timeoutMs: 1800000
|
||||
args:
|
||||
- --lane
|
||||
- cli-installer-distribution
|
||||
@@ -0,0 +1,26 @@
|
||||
title: CLI launcher runtime and recovery
|
||||
|
||||
scenario:
|
||||
id: cli-launcher-runtime
|
||||
surface: cli
|
||||
category: cli.cli-setup
|
||||
coverage:
|
||||
primary:
|
||||
- cli.cli-entrypoint
|
||||
- cli.supported-node-runtime
|
||||
objective: Verify the actual OpenClaw launcher accepts supported runtimes, rejects unsupported runtimes, serves help and version, and reports source-build recovery guidance.
|
||||
successCriteria:
|
||||
- The copied launcher executes under every supported Node minimum and future supported majors.
|
||||
- Below-floor and unsupported Node versions exit before loading the CLI with the documented runtime requirement.
|
||||
- "`--help` and `--version` execute through the launcher fast path."
|
||||
- A source checkout without built output reports the supported recovery command.
|
||||
docsRefs:
|
||||
- docs/install/index.md
|
||||
- docs/help/troubleshooting.md
|
||||
codeRefs:
|
||||
- openclaw.mjs
|
||||
- test/openclaw-launcher.e2e.test.ts
|
||||
execution:
|
||||
kind: vitest
|
||||
path: test/openclaw-launcher.e2e.test.ts
|
||||
summary: Executes copied launcher processes across supported and rejected runtime versions, CLI fast paths, and missing-build recovery.
|
||||
@@ -5,16 +5,19 @@ scenario:
|
||||
surface: containers
|
||||
category: containers.image-release-and-validation
|
||||
coverage:
|
||||
primary:
|
||||
- cli.package-manager-installs
|
||||
secondary:
|
||||
- containers.docker-e2e-package-artifact-generation
|
||||
- cli.package-manager-installs
|
||||
objective: Build the real OpenClaw package artifact, install and execute it in the scheduler-selected bare container image, and record exact package, image, and container identities.
|
||||
- cli.cli-entrypoint
|
||||
objective: Build the real OpenClaw package artifact, install it globally with npm, pnpm, and Bun in clean containers, execute each manager-created PATH entrypoint, and record exact identities.
|
||||
successCriteria:
|
||||
- The canonical Docker package helper builds and validates the real npm tarball.
|
||||
- The scheduler prepares the package and bare target image through the shared planner and catalog.
|
||||
- A clean target container installs the tarball with npm and executes the installed OpenClaw CLI.
|
||||
- The installed package name and version match the tarball metadata.
|
||||
- Evidence records the package digest and version, bare image ID, target container ID, installed package root, and executed CLI version.
|
||||
- Clean target containers install the same tarball globally with npm, pnpm, and Bun.
|
||||
- Each package manager creates a PATH-resolved OpenClaw executable that serves help and the candidate version.
|
||||
- Every installed package version matches the tarball metadata.
|
||||
- Evidence records the package digest and version, bare image ID, manager container IDs, package roots, executable paths, and CLI versions.
|
||||
docsRefs:
|
||||
- docs/install/updating.md
|
||||
- docs/help/testing.md
|
||||
@@ -27,7 +30,7 @@ scenario:
|
||||
execution:
|
||||
kind: script
|
||||
path: test/e2e/qa-lab/runtime/docker-artifact-proof.ts
|
||||
summary: Builds the real package artifact, installs and executes it in a clean target container, and records artifact identities.
|
||||
summary: Builds one real package artifact, installs and executes it through npm, pnpm, and Bun PATH entrypoints in clean containers, and records artifact identities.
|
||||
timeoutMs: 1800000
|
||||
args:
|
||||
- --artifact-base
|
||||
|
||||
@@ -7,14 +7,15 @@ scenario:
|
||||
coverage:
|
||||
primary:
|
||||
- cli.install-kind-switching
|
||||
- cli.update-status-and-rpc
|
||||
secondary:
|
||||
- cli.update-channels
|
||||
objective: Verify package-to-git and git-to-package update switching plus machine-readable update status in Docker.
|
||||
- cli.update-status-and-rpc
|
||||
objective: Verify stable, beta, and dev channel selection, package-to-git and git-to-package switching, and machine-readable update status in Docker.
|
||||
successCriteria:
|
||||
- The package install resolves the exact candidate through a hermetic beta dist-tag fixture, persists beta, and remains a package install.
|
||||
- The package install updates to a package-derived git dev fixture.
|
||||
- The git install updates back to the package tarball.
|
||||
- "`openclaw update status --json` reports the expected git and package status after each switch."
|
||||
- The beta, dev, and stable selections persist in canonical config.
|
||||
- "`openclaw update status --json` reports the expected package, git, and package status after each switch."
|
||||
docsRefs:
|
||||
- docs/cli/update.md
|
||||
- docs/install/updating.md
|
||||
@@ -25,7 +26,7 @@ scenario:
|
||||
execution:
|
||||
kind: script
|
||||
path: test/e2e/qa-lab/runtime/docker-e2e-lane.ts
|
||||
summary: Runs the existing update-channel-switch Docker E2E lane and records QA Lab evidence for install-kind switching and update status coverage.
|
||||
summary: Runs stable, beta, and dev update selections through the existing Docker E2E lane and records install-kind, persisted-channel, and update-status evidence.
|
||||
args:
|
||||
- --lane
|
||||
- update-channel-switch
|
||||
|
||||
@@ -177,12 +177,35 @@ NODE
|
||||
fi
|
||||
|
||||
echo "==> OpenClaw version through Bun global install"
|
||||
run_with_timeout "$COMMAND_TIMEOUT_MS" "$openclaw_bin" --version
|
||||
local openclaw_version
|
||||
openclaw_version="$(run_with_timeout "$COMMAND_TIMEOUT_MS" "$openclaw_bin" --version)"
|
||||
printf "%s\n" "$openclaw_version"
|
||||
|
||||
echo "==> OpenClaw help through Bun global install"
|
||||
run_with_timeout "$COMMAND_TIMEOUT_MS" "$openclaw_bin" --help >/dev/null
|
||||
|
||||
echo "==> OpenClaw image providers through Bun global install"
|
||||
local providers_json
|
||||
providers_json="$(run_with_timeout "$COMMAND_TIMEOUT_MS" "$openclaw_bin" infer image providers --json)"
|
||||
OPENCLAW_IMAGE_PROVIDERS_JSON="$providers_json" node scripts/e2e/lib/bun-global-install/assertions.mjs assert-image-providers
|
||||
|
||||
if [ -n "${OPENCLAW_BUN_GLOBAL_SMOKE_PROOF_PATH:-}" ]; then
|
||||
node --input-type=module - \
|
||||
"$OPENCLAW_BUN_GLOBAL_SMOKE_PROOF_PATH" \
|
||||
"$bun_path" \
|
||||
"$openclaw_bin" \
|
||||
"$openclaw_version" <<'NODE'
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
|
||||
const [, , proofPath, bunPath, openclawPath, openclawVersion] = process.argv;
|
||||
fs.mkdirSync(path.dirname(proofPath), { recursive: true });
|
||||
fs.writeFileSync(
|
||||
proofPath,
|
||||
`${JSON.stringify({ bunPath, openclawPath, openclawVersion }, null, 2)}\n`,
|
||||
);
|
||||
NODE
|
||||
fi
|
||||
}
|
||||
|
||||
main "$@"
|
||||
|
||||
+164
@@ -0,0 +1,164 @@
|
||||
#!/usr/bin/env bash
|
||||
# Proves hosted npm installation plus dedicated-prefix source installation.
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
source "$ROOT_DIR/scripts/lib/docker-e2e-image.sh"
|
||||
|
||||
IMAGE_NAME="$(docker_e2e_resolve_image "openclaw-cli-installer-distribution:local")"
|
||||
PACKAGE_TGZ="$(
|
||||
docker_e2e_prepare_package_tgz cli-installer-distribution "${OPENCLAW_CURRENT_PACKAGE_TGZ:-}"
|
||||
)"
|
||||
HOSTED_PROOF_CONTAINER="openclaw-hosted-installer-proof-$$"
|
||||
SOURCE_PROOF_CONTAINER="openclaw-source-installer-proof-$$"
|
||||
SOURCE_BUNDLE="$(mktemp "${TMPDIR:-/tmp}/openclaw-source.XXXXXX.bundle")"
|
||||
SOURCE_PROOF_SCRIPT="$(mktemp "${TMPDIR:-/tmp}/openclaw-source-proof.XXXXXX.sh")"
|
||||
SOURCE_SHA="$(git -C "$ROOT_DIR" rev-parse HEAD)"
|
||||
SOURCE_MEMORY="${OPENCLAW_CLI_INSTALLER_SOURCE_MEMORY:-16g}"
|
||||
|
||||
cleanup() {
|
||||
docker_e2e_docker_cmd rm -f \
|
||||
"$HOSTED_PROOF_CONTAINER" \
|
||||
"$SOURCE_PROOF_CONTAINER" >/dev/null 2>&1 || true
|
||||
docker_e2e_cleanup_package_tgz "$PACKAGE_TGZ"
|
||||
rm -f "$SOURCE_BUNDLE" "$SOURCE_PROOF_SCRIPT"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
git -C "$ROOT_DIR" bundle create "$SOURCE_BUNDLE" HEAD
|
||||
cat >"$SOURCE_PROOF_SCRIPT" <<'SOURCE_PROOF'
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
test -r "$0"
|
||||
test -x "$0"
|
||||
command -v curl >/dev/null
|
||||
git clone -q /tmp/openclaw-source.bundle /tmp/openclaw-source
|
||||
git -C /tmp/openclaw-source checkout -q --detach "$OPENCLAW_SOURCE_SHA"
|
||||
bash /tmp/openclaw-source/scripts/install-cli.sh \
|
||||
--install-method git \
|
||||
--git-dir /tmp/openclaw-source \
|
||||
--version "$OPENCLAW_SOURCE_SHA" \
|
||||
--no-git-update \
|
||||
--prefix /tmp/openclaw-prefix \
|
||||
--node-version 24.15.0 \
|
||||
--no-onboard
|
||||
|
||||
prefix_node=/tmp/openclaw-prefix/tools/node/bin/node
|
||||
prefix_cli=/tmp/openclaw-prefix/bin/openclaw
|
||||
test -x "$prefix_node"
|
||||
test -x "$prefix_cli"
|
||||
grep -Fq "exec \"$prefix_node\"" "$prefix_cli"
|
||||
grep -Fq "/tmp/openclaw-source/dist/entry.js" "$prefix_cli"
|
||||
export PATH="/tmp/openclaw-prefix/bin:$PATH"
|
||||
test "$(command -v openclaw)" = "$prefix_cli"
|
||||
test "$(git -C /tmp/openclaw-source rev-parse HEAD)" = "$OPENCLAW_SOURCE_SHA"
|
||||
openclaw_version="$(openclaw --version)"
|
||||
openclaw --help >/tmp/openclaw-help
|
||||
test -s /tmp/openclaw-help
|
||||
status_json="$(openclaw update status --json)"
|
||||
STATUS_JSON="$status_json" node -e "
|
||||
const status = JSON.parse(process.env.STATUS_JSON);
|
||||
if (status.update?.installKind !== \"git\") {
|
||||
throw new Error(\`expected git install kind, got \${status.update?.installKind}\`);
|
||||
}
|
||||
"
|
||||
printf "prefixNode=%s@%s\n" "$prefix_node" "$("$prefix_node" --version)"
|
||||
printf "prefixOpenClaw=%s@%s\n" "$prefix_cli" "$openclaw_version"
|
||||
printf "sourceHead=%s installKind=git\n" "$OPENCLAW_SOURCE_SHA"
|
||||
printf "sourceOnboard=disabled\n"
|
||||
touch /tmp/openclaw-proof-ready
|
||||
exec sleep infinity
|
||||
SOURCE_PROOF
|
||||
chmod 0555 "$SOURCE_PROOF_SCRIPT"
|
||||
|
||||
docker_e2e_build_or_reuse \
|
||||
"$IMAGE_NAME" \
|
||||
cli-installer-distribution \
|
||||
"$ROOT_DIR/scripts/e2e/Dockerfile" \
|
||||
"$ROOT_DIR" \
|
||||
bare
|
||||
|
||||
echo "==> Hosted install.sh exact-candidate proof"
|
||||
docker_e2e_docker_run_cmd run -d \
|
||||
--name "$HOSTED_PROOF_CONTAINER" \
|
||||
-e HOME=/tmp/openclaw-hosted-home \
|
||||
-e OPENCLAW_NO_ONBOARD=1 \
|
||||
-e OPENCLAW_NO_PROMPT=1 \
|
||||
-v "$PACKAGE_TGZ:/tmp/openclaw-current.tgz:ro" \
|
||||
-v "$ROOT_DIR/scripts/install.sh:/tmp/install.sh:ro" \
|
||||
"$IMAGE_NAME" \
|
||||
bash -lc '
|
||||
set -euo pipefail
|
||||
mkdir -p "$HOME"
|
||||
bash /tmp/install.sh \
|
||||
--install-method npm \
|
||||
--version file:/tmp/openclaw-current.tgz \
|
||||
--no-onboard \
|
||||
--no-prompt
|
||||
source "$HOME/.bashrc"
|
||||
hash -r
|
||||
openclaw_path="$(command -v openclaw)"
|
||||
test -n "$openclaw_path"
|
||||
node_path="$(command -v node)"
|
||||
node_version="$(node --version)"
|
||||
openclaw_version="$(openclaw --version)"
|
||||
openclaw --help >/tmp/openclaw-help
|
||||
test -s /tmp/openclaw-help
|
||||
printf "hostedNode=%s@%s\n" "$node_path" "$node_version"
|
||||
printf "hostedOpenClaw=%s@%s\n" "$openclaw_path" "$openclaw_version"
|
||||
printf "hostedOnboard=disabled\n"
|
||||
touch /tmp/openclaw-proof-ready
|
||||
exec sleep infinity
|
||||
' >/dev/null
|
||||
|
||||
# A full source install builds every workspace package; the shared 8g cap OOMs before wrapper creation.
|
||||
echo "==> install-cli.sh dedicated-prefix source-checkout proof"
|
||||
docker_e2e_docker_run_cmd run -d \
|
||||
--name "$SOURCE_PROOF_CONTAINER" \
|
||||
--memory "$SOURCE_MEMORY" \
|
||||
-e HOME=/tmp/openclaw-source-home \
|
||||
-e OPENCLAW_NO_ONBOARD=1 \
|
||||
-e OPENCLAW_NO_PROMPT=1 \
|
||||
-e COREPACK_ENABLE_DOWNLOAD_PROMPT=0 \
|
||||
-e "OPENCLAW_SOURCE_SHA=$SOURCE_SHA" \
|
||||
--user root \
|
||||
-v "$SOURCE_BUNDLE:/tmp/openclaw-source.bundle:ro" \
|
||||
-v "$SOURCE_PROOF_SCRIPT:/tmp/source-proof.sh:ro" \
|
||||
"$IMAGE_NAME" \
|
||||
bash -lc '
|
||||
set -euo pipefail
|
||||
apt-get update
|
||||
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends curl
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
command -v curl >/dev/null
|
||||
install -d -o appuser -g appuser "$HOME"
|
||||
exec runuser -u appuser -- env \
|
||||
HOME="$HOME" \
|
||||
OPENCLAW_NO_ONBOARD="$OPENCLAW_NO_ONBOARD" \
|
||||
OPENCLAW_NO_PROMPT="$OPENCLAW_NO_PROMPT" \
|
||||
COREPACK_ENABLE_DOWNLOAD_PROMPT="$COREPACK_ENABLE_DOWNLOAD_PROMPT" \
|
||||
OPENCLAW_SOURCE_SHA="$OPENCLAW_SOURCE_SHA" \
|
||||
bash /tmp/source-proof.sh
|
||||
' >/dev/null
|
||||
|
||||
wait_for_proof() {
|
||||
local container_name="$1"
|
||||
for _ in $(seq 1 1200); do
|
||||
if docker exec "$container_name" test -f /tmp/openclaw-proof-ready; then
|
||||
docker logs "$container_name"
|
||||
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
|
||||
}
|
||||
|
||||
wait_for_proof "$HOSTED_PROOF_CONTAINER"
|
||||
wait_for_proof "$SOURCE_PROOF_CONTAINER"
|
||||
echo "CLI installer distribution proof passed."
|
||||
@@ -7,62 +7,157 @@ source "$ROOT_DIR/scripts/lib/docker-e2e-image.sh"
|
||||
IMAGE_NAME="$(docker_e2e_resolve_image "openclaw-docker-e2e-bare:local")"
|
||||
PACKAGE_TGZ="$(docker_e2e_prepare_package_tgz docker-package-install "${OPENCLAW_CURRENT_PACKAGE_TGZ:-}")"
|
||||
IDENTITY_PATH="${OPENCLAW_DOCKER_ARTIFACT_IDENTITY_PATH:-$ROOT_DIR/.artifacts/docker-tests/docker-package-install-identities.json}"
|
||||
CONTAINER_NAME="openclaw-package-proof-$$"
|
||||
NPM_PROOF_CONTAINER="openclaw-package-npm-proof-$$"
|
||||
PNPM_PROOF_CONTAINER="openclaw-package-pnpm-proof-$$"
|
||||
BUN_PROOF_CONTAINER="openclaw-package-bun-proof-$$"
|
||||
DOCKER_RUN_TIMEOUT="${OPENCLAW_DOCKER_PACKAGE_INSTALL_RUN_TIMEOUT:-120s}"
|
||||
BUN_HARNESS_DIR="$(mktemp -d "${TMPDIR:-/tmp}/openclaw-bun-harness.XXXXXX")"
|
||||
|
||||
cleanup() {
|
||||
docker_e2e_docker_cmd rm -f "$CONTAINER_NAME" >/dev/null 2>&1 || true
|
||||
docker_e2e_docker_cmd rm -f \
|
||||
"$NPM_PROOF_CONTAINER" \
|
||||
"$PNPM_PROOF_CONTAINER" \
|
||||
"$BUN_PROOF_CONTAINER" >/dev/null 2>&1 || true
|
||||
docker_e2e_cleanup_package_tgz "$PACKAGE_TGZ"
|
||||
rm -rf "$BUN_HARNESS_DIR"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
docker_e2e_build_or_reuse "$IMAGE_NAME" docker-package-install "$ROOT_DIR/scripts/e2e/Dockerfile" "$ROOT_DIR" bare
|
||||
|
||||
echo "Installing the real OpenClaw package artifact in the target container..."
|
||||
for harness_path in \
|
||||
scripts/e2e/bun-global-install-smoke.sh \
|
||||
scripts/e2e/lib/bun-global-install/assertions.mjs \
|
||||
scripts/lib/docker-e2e-container.sh \
|
||||
scripts/lib/docker-e2e-logs.sh \
|
||||
scripts/lib/docker-e2e-package.sh \
|
||||
scripts/lib/docker-e2e-resource-diagnostics.sh; do
|
||||
mkdir -p "$BUN_HARNESS_DIR/$(dirname "$harness_path")"
|
||||
cp "$ROOT_DIR/$harness_path" "$BUN_HARNESS_DIR/$harness_path"
|
||||
done
|
||||
chmod -R a+rX "$BUN_HARNESS_DIR"
|
||||
|
||||
echo "Installing the real OpenClaw package artifact with npm..."
|
||||
DOCKER_COMMAND_TIMEOUT="$DOCKER_RUN_TIMEOUT" docker_e2e_docker_run_cmd run -d \
|
||||
--name "$CONTAINER_NAME" \
|
||||
--name "$NPM_PROOF_CONTAINER" \
|
||||
-v "$PACKAGE_TGZ:/tmp/openclaw-current.tgz:ro" \
|
||||
"$IMAGE_NAME" \
|
||||
bash -lc '
|
||||
set -euo pipefail
|
||||
npm install -g --prefix /tmp/openclaw-proof /tmp/openclaw-current.tgz --no-fund --no-audit
|
||||
export PATH="/tmp/openclaw-proof/bin:$PATH"
|
||||
package_root=/tmp/openclaw-proof/lib/node_modules/openclaw
|
||||
"$package_root/openclaw.mjs" --version > /tmp/openclaw-version
|
||||
"$package_root/openclaw.mjs" --help > /tmp/openclaw-help
|
||||
test "$(command -v openclaw)" = "/tmp/openclaw-proof/bin/openclaw"
|
||||
openclaw --version > /tmp/openclaw-version
|
||||
openclaw --help > /tmp/openclaw-help
|
||||
test -s /tmp/openclaw-help
|
||||
touch /tmp/openclaw-proof-ready
|
||||
exec sleep infinity
|
||||
' >/dev/null
|
||||
|
||||
for _ in $(seq 1 240); do
|
||||
if docker exec "$CONTAINER_NAME" test -f /tmp/openclaw-proof-ready; then
|
||||
break
|
||||
fi
|
||||
if [ "$(docker inspect --format '{{.State.Running}}' "$CONTAINER_NAME")" != "true" ]; then
|
||||
docker logs "$CONTAINER_NAME" >&2
|
||||
echo "Installing the real OpenClaw package artifact with pnpm..."
|
||||
DOCKER_COMMAND_TIMEOUT="$DOCKER_RUN_TIMEOUT" docker_e2e_docker_run_cmd run -d \
|
||||
--name "$PNPM_PROOF_CONTAINER" \
|
||||
-v "$PACKAGE_TGZ:/tmp/openclaw-current.tgz:ro" \
|
||||
"$IMAGE_NAME" \
|
||||
bash -lc '
|
||||
set -euo pipefail
|
||||
export PNPM_HOME=/tmp/pnpm-home
|
||||
export PATH="$PNPM_HOME:$PATH"
|
||||
corepack prepare pnpm@11.15.1 --activate
|
||||
pnpm config set global-bin-dir "$PNPM_HOME"
|
||||
pnpm config set global-dir /tmp/pnpm-global
|
||||
pnpm add --global --allow-build=openclaw /tmp/openclaw-current.tgz
|
||||
test "$(command -v openclaw)" = "$PNPM_HOME/openclaw"
|
||||
package_root="$(pnpm root --global)/openclaw"
|
||||
printf "%s\n" "$package_root" > /tmp/openclaw-package-root
|
||||
openclaw --version > /tmp/openclaw-version
|
||||
openclaw --help > /tmp/openclaw-help
|
||||
test -s /tmp/openclaw-help
|
||||
touch /tmp/openclaw-proof-ready
|
||||
exec sleep infinity
|
||||
' >/dev/null
|
||||
|
||||
echo "Installing the real OpenClaw package artifact with Bun..."
|
||||
DOCKER_COMMAND_TIMEOUT="$DOCKER_RUN_TIMEOUT" docker_e2e_docker_run_cmd run -d \
|
||||
--name "$BUN_PROOF_CONTAINER" \
|
||||
-v "$PACKAGE_TGZ:/tmp/openclaw-current.tgz:ro" \
|
||||
-v "$BUN_HARNESS_DIR:/repo:ro" \
|
||||
"$IMAGE_NAME" \
|
||||
bash -lc '
|
||||
set -euo pipefail
|
||||
npm install -g --prefix /tmp/bun-runtime bun@1.3.14 --no-fund --no-audit
|
||||
cd /repo
|
||||
BUN_BIN=/tmp/bun-runtime/bin/bun \
|
||||
OPENCLAW_BUN_GLOBAL_SMOKE_HOST_BUILD=0 \
|
||||
OPENCLAW_BUN_GLOBAL_SMOKE_PACKAGE_TGZ=/tmp/openclaw-current.tgz \
|
||||
OPENCLAW_BUN_GLOBAL_SMOKE_PROOF_PATH=/tmp/openclaw-bun-proof.json \
|
||||
bash scripts/e2e/bun-global-install-smoke.sh
|
||||
touch /tmp/openclaw-proof-ready
|
||||
exec sleep infinity
|
||||
' >/dev/null
|
||||
|
||||
wait_for_proof() {
|
||||
local container_name="$1"
|
||||
for _ in $(seq 1 240); 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
|
||||
}
|
||||
|
||||
for container_name in "$NPM_PROOF_CONTAINER" "$PNPM_PROOF_CONTAINER" "$BUN_PROOF_CONTAINER"; do
|
||||
wait_for_proof "$container_name"
|
||||
done
|
||||
|
||||
NPM_PACKAGE_ROOT="/tmp/openclaw-proof/lib/node_modules/openclaw"
|
||||
NPM_INSTALLED_VERSION="$(docker exec "$NPM_PROOF_CONTAINER" cat /tmp/openclaw-version | tr -d '\r\n')"
|
||||
PNPM_PACKAGE_ROOT="$(docker exec "$PNPM_PROOF_CONTAINER" cat /tmp/openclaw-package-root | tr -d '\r\n')"
|
||||
PNPM_INSTALLED_VERSION="$(docker exec "$PNPM_PROOF_CONTAINER" cat /tmp/openclaw-version | tr -d '\r\n')"
|
||||
BUN_OPENCLAW_PATH="$(
|
||||
docker exec "$BUN_PROOF_CONTAINER" \
|
||||
node -p 'JSON.parse(require("node:fs").readFileSync("/tmp/openclaw-bun-proof.json", "utf8")).openclawPath'
|
||||
)"
|
||||
BUN_INSTALLED_VERSION="$(
|
||||
docker exec "$BUN_PROOF_CONTAINER" \
|
||||
node -p 'JSON.parse(require("node:fs").readFileSync("/tmp/openclaw-bun-proof.json", "utf8")).openclawVersion'
|
||||
)"
|
||||
PACKAGE_VERSION="$(docker exec "$NPM_PROOF_CONTAINER" node -p "require('$NPM_PACKAGE_ROOT/package.json').version")"
|
||||
for installed_version in "$NPM_INSTALLED_VERSION" "$PNPM_INSTALLED_VERSION" "$BUN_INSTALLED_VERSION"; do
|
||||
if [[ "$installed_version" != *"$PACKAGE_VERSION"* ]]; then
|
||||
echo "installed CLI output $installed_version does not contain package version $PACKAGE_VERSION" >&2
|
||||
exit 1
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
docker exec "$CONTAINER_NAME" test -f /tmp/openclaw-proof-ready
|
||||
|
||||
INSTALLED_VERSION="$(docker exec "$CONTAINER_NAME" cat /tmp/openclaw-version | tr -d '\r\n')"
|
||||
PACKAGE_ROOT="/tmp/openclaw-proof/lib/node_modules/openclaw"
|
||||
PACKAGE_VERSION="$(docker exec "$CONTAINER_NAME" node -p "require('$PACKAGE_ROOT/package.json').version")"
|
||||
if [[ "$INSTALLED_VERSION" != *"$PACKAGE_VERSION"* ]]; then
|
||||
echo "installed CLI output $INSTALLED_VERSION does not contain package version $PACKAGE_VERSION" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
node --import tsx "$ROOT_DIR/scripts/e2e/lib/docker-artifact-proof/write-identities.ts" \
|
||||
--scenario docker-package-install \
|
||||
--output "$IDENTITY_PATH" \
|
||||
--image "$IMAGE_NAME" \
|
||||
--package "$PACKAGE_TGZ" \
|
||||
--container "target=$CONTAINER_NAME" \
|
||||
--detail "target:installedPackageRoot=$PACKAGE_ROOT" \
|
||||
--detail "target:installedPackageVersion=$PACKAGE_VERSION" \
|
||||
--detail "target:openclawVersion=$INSTALLED_VERSION" \
|
||||
--detail "target:helpCommand=passed"
|
||||
--container "npm=$NPM_PROOF_CONTAINER" \
|
||||
--container "pnpm=$PNPM_PROOF_CONTAINER" \
|
||||
--container "bun=$BUN_PROOF_CONTAINER" \
|
||||
--detail "npm:installedPackageRoot=$NPM_PACKAGE_ROOT" \
|
||||
--detail "npm:installedPackageVersion=$PACKAGE_VERSION" \
|
||||
--detail "npm:openclawVersion=$NPM_INSTALLED_VERSION" \
|
||||
--detail "npm:openclawPath=/tmp/openclaw-proof/bin/openclaw" \
|
||||
--detail "npm:helpCommand=passed" \
|
||||
--detail "pnpm:installedPackageRoot=$PNPM_PACKAGE_ROOT" \
|
||||
--detail "pnpm:installedPackageVersion=$PACKAGE_VERSION" \
|
||||
--detail "pnpm:openclawVersion=$PNPM_INSTALLED_VERSION" \
|
||||
--detail "pnpm:openclawPath=/tmp/pnpm-home/openclaw" \
|
||||
--detail "pnpm:helpCommand=passed" \
|
||||
--detail "bun:installedPackageVersion=$PACKAGE_VERSION" \
|
||||
--detail "bun:openclawVersion=$BUN_INSTALLED_VERSION" \
|
||||
--detail "bun:openclawPath=$BUN_OPENCLAW_PATH" \
|
||||
--detail "bun:helpCommand=passed"
|
||||
|
||||
echo "Package artifact container proof passed."
|
||||
echo "npm, pnpm, and Bun package artifact proofs passed."
|
||||
|
||||
@@ -30,6 +30,21 @@ function normalizeUpstreamRegistry(raw) {
|
||||
}
|
||||
|
||||
const upstreamRegistry = normalizeUpstreamRegistry(process.env.OPENCLAW_NPM_REGISTRY_UPSTREAM);
|
||||
const distTagOverrides = new Map(
|
||||
(process.env.OPENCLAW_NPM_REGISTRY_DIST_TAGS ?? "")
|
||||
.split(",")
|
||||
.map((entry) => entry.trim())
|
||||
.filter(Boolean)
|
||||
.map((entry) => {
|
||||
const separator = entry.indexOf("=");
|
||||
if (separator <= 0 || separator === entry.length - 1) {
|
||||
throw new Error(
|
||||
"OPENCLAW_NPM_REGISTRY_DIST_TAGS must contain comma-separated tag=version entries",
|
||||
);
|
||||
}
|
||||
return [entry.slice(0, separator).trim(), entry.slice(separator + 1).trim()];
|
||||
}),
|
||||
);
|
||||
// Match other E2E package-download budgets while keeping public-registry hops
|
||||
// inside the install deadline and decoded bodies inside a fixed memory budget.
|
||||
const UPSTREAM_REQUEST_TIMEOUT_MS = 120_000;
|
||||
@@ -88,7 +103,10 @@ for (let index = 0; index < packageArgs.length; index += 3) {
|
||||
|
||||
const metadataFor = (entry, baseUrl) => ({
|
||||
name: entry.packageName,
|
||||
"dist-tags": { latest: entry.latestVersion },
|
||||
"dist-tags": {
|
||||
latest: entry.latestVersion,
|
||||
...Object.fromEntries(distTagOverrides),
|
||||
},
|
||||
versions: Object.fromEntries(
|
||||
[...entry.versions.entries()].map(([version, versionEntry]) => [
|
||||
version,
|
||||
|
||||
@@ -8,7 +8,7 @@ const controlUiHtml = "<!doctype html><title>fixture</title>\n";
|
||||
|
||||
function usage() {
|
||||
console.error(
|
||||
"usage: assertions.mjs <prepare-git-fixture|write-control-ui|assert-update|assert-config-channel|assert-status-kind> [...]",
|
||||
"usage: assertions.mjs <prepare-git-fixture|write-control-ui|assert-update|assert-config-channel|assert-status-kind|assert-installed-version> [...]",
|
||||
);
|
||||
process.exit(2);
|
||||
}
|
||||
@@ -163,8 +163,8 @@ function assertUpdate(channel) {
|
||||
if (channel === "dev" && payload.mode !== "git") {
|
||||
throw new Error(`expected dev update mode git, got ${payload.mode}`);
|
||||
}
|
||||
if (channel === "stable" && !["npm", "pnpm", "bun"].includes(payload.mode)) {
|
||||
throw new Error(`expected package-manager mode after stable switch, got ${payload.mode}`);
|
||||
if (["stable", "beta"].includes(channel) && !["npm", "pnpm", "bun"].includes(payload.mode)) {
|
||||
throw new Error(`expected package-manager mode after ${channel} switch, got ${payload.mode}`);
|
||||
}
|
||||
if (payload.postUpdate?.plugins && payload.postUpdate.plugins.status !== "ok") {
|
||||
throw new Error(
|
||||
@@ -196,6 +196,15 @@ function assertStatusKind(kind) {
|
||||
}
|
||||
}
|
||||
|
||||
function assertInstalledVersion(root, expectedVersion) {
|
||||
const manifest = readJson(path.join(root, "package.json"));
|
||||
if (manifest.version !== expectedVersion) {
|
||||
throw new Error(
|
||||
`expected installed openclaw ${expectedVersion}, got ${String(manifest.version)}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
switch (command) {
|
||||
case "prepare-git-fixture":
|
||||
prepareGitFixture(args[0] ?? "/tmp/openclaw-git");
|
||||
@@ -212,6 +221,9 @@ switch (command) {
|
||||
case "assert-status-kind":
|
||||
assertStatusKind(args[0]);
|
||||
break;
|
||||
case "assert-installed-version":
|
||||
assertInstalledVersion(args[0], args[1]);
|
||||
break;
|
||||
default:
|
||||
usage();
|
||||
}
|
||||
|
||||
@@ -93,11 +93,69 @@ export OPENCLAW_PACKAGE_ACCEPTANCE_LEGACY_COMPAT
|
||||
command -v openclaw >/dev/null
|
||||
openclaw_e2e_enable_openclaw_cli_timeout
|
||||
|
||||
registry_port_file=/tmp/openclaw-update-channel-registry.port
|
||||
registry_log=/tmp/openclaw-update-channel-registry.log
|
||||
registry_pid=""
|
||||
cleanup_registry() {
|
||||
if [ -n "$registry_pid" ]; then
|
||||
kill "$registry_pid" 2>/dev/null || true
|
||||
wait "$registry_pid" 2>/dev/null || true
|
||||
fi
|
||||
}
|
||||
trap cleanup_registry EXIT
|
||||
rm -f "$registry_port_file"
|
||||
OPENCLAW_NPM_REGISTRY_DIST_TAGS="latest=0.0.0,beta=$package_version" \
|
||||
OPENCLAW_NPM_REGISTRY_UPSTREAM=https://registry.npmjs.org \
|
||||
node scripts/e2e/lib/plugins/npm-registry-server.mjs \
|
||||
"$registry_port_file" \
|
||||
openclaw \
|
||||
"$package_version" \
|
||||
"$package_tgz" \
|
||||
>"$registry_log" 2>&1 &
|
||||
registry_pid="$!"
|
||||
for _ in $(seq 1 100); do
|
||||
if [ -s "$registry_port_file" ]; then
|
||||
break
|
||||
fi
|
||||
if ! kill -0 "$registry_pid" 2>/dev/null; then
|
||||
openclaw_e2e_print_log "$registry_log" >&2
|
||||
exit 1
|
||||
fi
|
||||
sleep 0.1
|
||||
done
|
||||
if [ ! -s "$registry_port_file" ]; then
|
||||
openclaw_e2e_print_log "$registry_log" >&2
|
||||
echo "Timed out waiting for update-channel npm fixture registry." >&2
|
||||
exit 1
|
||||
fi
|
||||
export NPM_CONFIG_REGISTRY="http://127.0.0.1:$(cat "$registry_port_file")"
|
||||
export npm_config_registry="$NPM_CONFIG_REGISTRY"
|
||||
|
||||
openclaw_e2e_eval_test_state_from_b64 "${OPENCLAW_TEST_STATE_SCRIPT_B64:?missing OPENCLAW_TEST_STATE_SCRIPT_B64}"
|
||||
|
||||
export OPENCLAW_GIT_DIR="$git_root"
|
||||
export OPENCLAW_UPDATE_DEV_TARGET_REF="$fixture_sha"
|
||||
|
||||
echo "==> package stable -> package beta channel"
|
||||
set +e
|
||||
beta_json="$(openclaw update --channel beta --yes --json --no-restart)"
|
||||
beta_status=$?
|
||||
set -e
|
||||
printf "%s\n" "$beta_json"
|
||||
if [ "$beta_status" -ne 0 ]; then
|
||||
exit "$beta_status"
|
||||
fi
|
||||
UPDATE_JSON="$beta_json" node scripts/e2e/lib/update-channel-switch/assertions.mjs assert-update beta
|
||||
node scripts/e2e/lib/update-channel-switch/assertions.mjs assert-config-channel beta
|
||||
node scripts/e2e/lib/update-channel-switch/assertions.mjs \
|
||||
assert-installed-version \
|
||||
/tmp/npm-prefix/lib/node_modules/openclaw \
|
||||
"$package_version"
|
||||
|
||||
status_json="$(openclaw update status --json)"
|
||||
printf "%s\n" "$status_json"
|
||||
STATUS_JSON="$status_json" node scripts/e2e/lib/update-channel-switch/assertions.mjs assert-status-kind package
|
||||
|
||||
echo "==> package -> git dev channel"
|
||||
set +e
|
||||
dev_json="$(openclaw update --channel dev --yes --json --no-restart)"
|
||||
|
||||
@@ -313,6 +313,15 @@ export const mainLanes = [
|
||||
timeoutMs: 20 * 60 * 1000,
|
||||
weight: 3,
|
||||
}),
|
||||
npmLane(
|
||||
"cli-installer-distribution",
|
||||
"OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:cli-installer-distribution",
|
||||
{
|
||||
stateScenario: "empty",
|
||||
timeoutMs: 30 * 60 * 1000,
|
||||
weight: 3,
|
||||
},
|
||||
),
|
||||
npmLane(
|
||||
"docker-package-install",
|
||||
"OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:package-install",
|
||||
|
||||
@@ -13,6 +13,7 @@ describe("QA Docker E2E lane fixture", () => {
|
||||
expect(listQaDockerE2eLaneNames()).toEqual(
|
||||
expect.arrayContaining([
|
||||
"agent-bundle-mcp-tools",
|
||||
"cli-installer-distribution",
|
||||
"codex-on-demand",
|
||||
"system-agent-first-run",
|
||||
"gateway-network",
|
||||
@@ -48,6 +49,9 @@ describe("QA Docker E2E lane fixture", () => {
|
||||
expect(resolveQaDockerE2eLane("codex-on-demand", {}).script).toBe(
|
||||
"scripts/e2e/codex-on-demand-docker.sh",
|
||||
);
|
||||
expect(resolveQaDockerE2eLane("cli-installer-distribution", {}).script).toBe(
|
||||
"scripts/e2e/cli-installer-distribution-docker.sh",
|
||||
);
|
||||
|
||||
const updateMigration = resolveQaDockerE2eLane("update-migration", {
|
||||
OPENCLAW_UPGRADE_SURVIVOR_BASELINE_SPEC: "openclaw@custom",
|
||||
|
||||
@@ -31,6 +31,9 @@ const QA_DOCKER_E2E_LANES = {
|
||||
"codex-on-demand": {
|
||||
script: "scripts/e2e/codex-on-demand-docker.sh",
|
||||
},
|
||||
"cli-installer-distribution": {
|
||||
script: "scripts/e2e/cli-installer-distribution-docker.sh",
|
||||
},
|
||||
"system-agent-first-run": {
|
||||
script: "scripts/e2e/system-agent-first-run-docker.sh",
|
||||
},
|
||||
|
||||
@@ -209,6 +209,28 @@ describe("scripts/test-docker-all scheduler", () => {
|
||||
expect(result.stderr).not.toContain("at ");
|
||||
});
|
||||
|
||||
it("selects the CLI installer distribution lane through the scheduler catalog", () => {
|
||||
const result = spawnSync(process.execPath, ["scripts/test-docker-all.mjs"], {
|
||||
cwd: process.cwd(),
|
||||
encoding: "utf8",
|
||||
env: {
|
||||
...process.env,
|
||||
OPENCLAW_DOCKER_ALL_BUILD: "0",
|
||||
OPENCLAW_DOCKER_ALL_DRY_RUN: "1",
|
||||
OPENCLAW_DOCKER_ALL_LANES: "cli-installer-distribution",
|
||||
OPENCLAW_DOCKER_ALL_PREFLIGHT: "0",
|
||||
OPENCLAW_DOCKER_ALL_TIMINGS: "0",
|
||||
},
|
||||
});
|
||||
|
||||
expect(result.status, result.stderr).toBe(0);
|
||||
expect(result.stdout).toContain("Selected lanes: cli-installer-distribution");
|
||||
expect(result.stdout).toContain(
|
||||
"cli-installer-distribution(w=3 r=docker,npm timeout=1800s image=bare state=empty)",
|
||||
);
|
||||
expect(result.stdout).toContain("Dry run complete");
|
||||
});
|
||||
|
||||
it("reuses only registry-backed images in generated workflow reruns", () => {
|
||||
const localCommand = githubWorkflowRerunCommand(["install-e2e"], "a".repeat(40), {
|
||||
GITHUB_REF_NAME: "full-release-validation-temp-deleted",
|
||||
|
||||
@@ -15,6 +15,7 @@ const DOCKER_E2E_PACKAGE_HELPER_PATH = "scripts/lib/docker-e2e-package.sh";
|
||||
const DOCKER_E2E_IMAGE_HELPER_PATH = "scripts/lib/docker-e2e-image.sh";
|
||||
const DOCKER_E2E_SCENARIOS_PATH = "scripts/lib/docker-e2e-scenarios.mjs";
|
||||
const COMPOSE_SETUP_E2E_PATH = "scripts/e2e/compose-setup.sh";
|
||||
const CLI_INSTALLER_DISTRIBUTION_E2E_PATH = "scripts/e2e/cli-installer-distribution-docker.sh";
|
||||
const DOCKER_PACKAGE_INSTALL_E2E_PATH = "scripts/e2e/docker-package-install.sh";
|
||||
const INSTALL_E2E_RUNNER_PATH = "scripts/docker/install-sh-e2e/run.sh";
|
||||
const CLEANUP_DOCKER_SMOKE_PATH = "scripts/test-cleanup-docker.sh";
|
||||
@@ -3518,8 +3519,8 @@ heartbeat_elapsed="\${BASH_REMATCH[1]}"
|
||||
expect(composeRunner).toContain('docker_e2e_docker_cmd rm -f "$CLI_NAME"');
|
||||
|
||||
const packageRunner = readFileSync(DOCKER_PACKAGE_INSTALL_E2E_PATH, "utf8");
|
||||
expect(packageRunner).not.toMatch(/(^|\n)\s*docker rm -f "\$CONTAINER_NAME"/u);
|
||||
expect(packageRunner).toContain('docker_e2e_docker_cmd rm -f "$CONTAINER_NAME"');
|
||||
expect(packageRunner).not.toMatch(/(^|\n)\s*docker rm -f/u);
|
||||
expect(packageRunner).toContain("docker_e2e_docker_cmd rm -f");
|
||||
expect(packageRunner).toContain(
|
||||
'DOCKER_RUN_TIMEOUT="${OPENCLAW_DOCKER_PACKAGE_INSTALL_RUN_TIMEOUT:-120s}"',
|
||||
);
|
||||
@@ -3534,6 +3535,66 @@ heartbeat_elapsed="\${BASH_REMATCH[1]}"
|
||||
}
|
||||
});
|
||||
|
||||
it("executes each CLI distribution boundary instead of promoting metadata", () => {
|
||||
const installerRunner = readFileSync(CLI_INSTALLER_DISTRIBUTION_E2E_PATH, "utf8");
|
||||
const packageRunner = readFileSync(DOCKER_PACKAGE_INSTALL_E2E_PATH, "utf8");
|
||||
const updateRunner = readFileSync(UPDATE_CHANNEL_SWITCH_DOCKER_E2E_PATH, "utf8");
|
||||
|
||||
expectTextToIncludeAll(packageRunner, [
|
||||
"npm install -g --prefix /tmp/openclaw-proof",
|
||||
"pnpm add --global --allow-build=openclaw",
|
||||
"bun@1.3.14",
|
||||
'test "$(command -v openclaw)" = "/tmp/openclaw-proof/bin/openclaw"',
|
||||
'test "$(command -v openclaw)" = "$PNPM_HOME/openclaw"',
|
||||
"OPENCLAW_BUN_GLOBAL_SMOKE_PROOF_PATH",
|
||||
'BUN_HARNESS_DIR="$(mktemp -d',
|
||||
"chmod -R a+rX",
|
||||
'-v "$BUN_HARNESS_DIR:/repo:ro"',
|
||||
'--container "npm=$NPM_PROOF_CONTAINER"',
|
||||
'--container "pnpm=$PNPM_PROOF_CONTAINER"',
|
||||
'--container "bun=$BUN_PROOF_CONTAINER"',
|
||||
]);
|
||||
expect(packageRunner).not.toContain('-v "$ROOT_DIR:/repo:ro"');
|
||||
expectTextToIncludeAll(installerRunner, [
|
||||
"bash /tmp/install.sh",
|
||||
"--version file:/tmp/openclaw-current.tgz",
|
||||
'source "$HOME/.bashrc"',
|
||||
"hash -r",
|
||||
"bash /tmp/openclaw-source/scripts/install-cli.sh",
|
||||
"--install-method git",
|
||||
"--prefix /tmp/openclaw-prefix",
|
||||
"--node-version 24.15.0",
|
||||
"apt-get install -y --no-install-recommends curl",
|
||||
"command -v curl >/dev/null",
|
||||
'chmod 0555 "$SOURCE_PROOF_SCRIPT"',
|
||||
'SOURCE_MEMORY="${OPENCLAW_CLI_INSTALLER_SOURCE_MEMORY:-16g}"',
|
||||
'--memory "$SOURCE_MEMORY"',
|
||||
"runuser -u appuser",
|
||||
'test -r "$0"',
|
||||
'test -x "$0"',
|
||||
'grep -Fq "/tmp/openclaw-source/dist/entry.js" "$prefix_cli"',
|
||||
"openclaw update status --json",
|
||||
"expected git install kind",
|
||||
]);
|
||||
expect(installerRunner.match(/--memory "\$SOURCE_MEMORY"/gu)).toHaveLength(1);
|
||||
expect(installerRunner.indexOf('--memory "$SOURCE_MEMORY"')).toBeGreaterThan(
|
||||
installerRunner.indexOf('echo "==> install-cli.sh dedicated-prefix source-checkout proof"'),
|
||||
);
|
||||
expectTextToIncludeAll(updateRunner, [
|
||||
"openclaw update --channel beta",
|
||||
'OPENCLAW_NPM_REGISTRY_DIST_TAGS="latest=0.0.0,beta=$package_version"',
|
||||
"OPENCLAW_NPM_REGISTRY_UPSTREAM=https://registry.npmjs.org",
|
||||
"assert-update beta",
|
||||
"assert-config-channel beta",
|
||||
"assert-installed-version",
|
||||
"assert-status-kind package",
|
||||
"openclaw update --channel dev",
|
||||
"openclaw update --channel stable",
|
||||
]);
|
||||
expect(updateRunner).toContain("openclaw update --channel beta --yes --json --no-restart");
|
||||
expect(updateRunner).not.toContain("openclaw update --channel beta --tag");
|
||||
});
|
||||
|
||||
it("routes the gateway network client through the timeout-aware run helper", () => {
|
||||
const runner = readFileSync(GATEWAY_NETWORK_DOCKER_E2E_PATH, "utf8");
|
||||
expect(runner).toContain(
|
||||
@@ -3856,6 +3917,7 @@ heartbeat_elapsed="\${BASH_REMATCH[1]}"
|
||||
"Package $package_version must support gateway install --wrapper.",
|
||||
);
|
||||
expect(updateChannel).toContain("assert-config-channel dev");
|
||||
expect(updateChannel).toContain("assert-config-channel beta");
|
||||
expect(updateChannelAssertions).toContain("expected persisted update.channel ${channel}");
|
||||
expect(pluginsAssertions).toContain("expected modern installRecords in installed plugin index");
|
||||
});
|
||||
@@ -4346,7 +4408,8 @@ heartbeat_elapsed="\${BASH_REMATCH[1]}"
|
||||
]);
|
||||
|
||||
expectTextToIncludeAll(npmRegistry, [
|
||||
'"dist-tags": { latest: entry.latestVersion }',
|
||||
"OPENCLAW_NPM_REGISTRY_DIST_TAGS",
|
||||
"Object.fromEntries(distTagOverrides)",
|
||||
"existing.latestVersion = version",
|
||||
"packageArgs.length % 3",
|
||||
]);
|
||||
|
||||
@@ -244,12 +244,22 @@ describe("scripts/lib/docker-e2e-plan", () => {
|
||||
expect(lane.command).toContain('bash "$harness/scripts/e2e/codex-npm-plugin-live-docker.sh"');
|
||||
});
|
||||
|
||||
it("plans package-backed Compose and package artifact proofs", () => {
|
||||
it("plans package-backed installer, Compose, and package artifact proofs", () => {
|
||||
const plan = planFor({
|
||||
selectedLaneNames: ["compose-setup", "docker-package-install"],
|
||||
selectedLaneNames: ["cli-installer-distribution", "compose-setup", "docker-package-install"],
|
||||
});
|
||||
|
||||
expect(plan.lanes.map(summarizeLane)).toEqual([
|
||||
{
|
||||
command: "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:cli-installer-distribution",
|
||||
imageKind: "bare",
|
||||
live: false,
|
||||
name: "cli-installer-distribution",
|
||||
resources: ["docker", "npm"],
|
||||
stateScenario: "empty",
|
||||
timeoutMs: 1_800_000,
|
||||
weight: 3,
|
||||
},
|
||||
{
|
||||
command: "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:compose-setup",
|
||||
imageKind: "functional",
|
||||
|
||||
@@ -775,6 +775,10 @@ ${command}
|
||||
],
|
||||
{
|
||||
cwd: process.cwd(),
|
||||
env: {
|
||||
...process.env,
|
||||
OPENCLAW_NPM_REGISTRY_DIST_TAGS: "latest=0.0.0,beta=2026.7.1-beta.3",
|
||||
},
|
||||
stdio: ["ignore", "pipe", "pipe"],
|
||||
},
|
||||
);
|
||||
@@ -787,6 +791,10 @@ ${command}
|
||||
const metadata = JSON.parse(response.body);
|
||||
|
||||
expect(response.statusCode).toBe(200);
|
||||
expect(metadata["dist-tags"]).toEqual({
|
||||
latest: "0.0.0",
|
||||
beta: "2026.7.1-beta.3",
|
||||
});
|
||||
expect(metadata.versions["2026.7.1-beta.3"].dependencies).toEqual({
|
||||
"@openclaw/ai": "2026.7.1-beta.3",
|
||||
zod: "4.3.6",
|
||||
|
||||
@@ -1608,6 +1608,8 @@ describe("bun global install smoke", () => {
|
||||
expect(script).toContain("--output-name openclaw-current.tgz");
|
||||
expect(script).not.toContain("npm pack --ignore-scripts --json --pack-destination");
|
||||
expect(script).toContain('"$bun_path" install -g "$PACKAGE_TGZ" --no-progress');
|
||||
expect(script).toContain('"$openclaw_bin" --help');
|
||||
expect(script).toContain("OPENCLAW_BUN_GLOBAL_SMOKE_PROOF_PATH");
|
||||
expect(script).toContain("infer image providers --json");
|
||||
expect(script).toContain("assert-image-providers");
|
||||
expect(assertions).toContain("image providers output is missing bundled provider");
|
||||
|
||||
@@ -763,6 +763,10 @@ describe("scripts/test-projects changed-target routing", () => {
|
||||
"test/scripts/docker-e2e-plan.test.ts",
|
||||
"test/scripts/package-acceptance-workflow.test.ts",
|
||||
],
|
||||
"scripts/e2e/cli-installer-distribution-docker.sh": [
|
||||
"test/scripts/docker-build-helper.test.ts",
|
||||
"test/scripts/docker-e2e-plan.test.ts",
|
||||
],
|
||||
"scripts/e2e/update-channel-switch-docker.sh": [
|
||||
"test/scripts/docker-build-helper.test.ts",
|
||||
"test/scripts/docker-e2e-plan.test.ts",
|
||||
|
||||
Reference in New Issue
Block a user