mirror of
https://github.com/openclaw/openclaw.git
synced 2026-10-03 01:29:56 +00:00
* perf(ci): deduplicate post-convergence first-hop proof * test(ci): cover canonical first-hop lane admission
177 lines
8.3 KiB
Bash
Executable file
177 lines
8.3 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
# Bash 5.3+ can deadlock writing heredoc pipes on macOS before the reader starts.
|
|
if [[ ${OSTYPE:-} == darwin* && $BASH != /bin/bash ]] && ((BASH_VERSINFO[0] > 5 || (BASH_VERSINFO[0] == 5 && BASH_VERSINFO[1] >= 3))); then
|
|
exec /bin/bash "$0" "$@"
|
|
fi
|
|
# Proves the selected first-hop method and a fresh updater without legacy chunks.
|
|
set -euo pipefail
|
|
|
|
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
source "$ROOT_DIR/scripts/lib/docker-e2e-image.sh"
|
|
|
|
if [ "${OPENCLAW_QA_ALLOW_UPDATE_FIRST_HOP:-0}" != "1" ]; then
|
|
echo "blocked destructive package self-update; set OPENCLAW_QA_ALLOW_UPDATE_FIRST_HOP=1 to run" >&2
|
|
exit 2
|
|
fi
|
|
|
|
IMAGE_NAME="$(
|
|
docker_e2e_resolve_image \
|
|
"openclaw-update-first-hop-compat-e2e" \
|
|
OPENCLAW_UPDATE_FIRST_HOP_E2E_IMAGE
|
|
)"
|
|
SKIP_BUILD="${OPENCLAW_UPDATE_FIRST_HOP_E2E_SKIP_BUILD:-0}"
|
|
# Run 36506342273 (hosted 4-vCPU): 1558s before the final candidate hop
|
|
# + projected 560s hop + ~5s assertions ~= 2125s; x ~1.5 => 3200s per source.
|
|
DOCKER_RUN_TIMEOUT="${OPENCLAW_UPDATE_FIRST_HOP_DOCKER_RUN_TIMEOUT:-3200s}"
|
|
# Space- or comma-separated recorded release versions; empty runs every recorded source.
|
|
SOURCE_VERSION_FILTER="${OPENCLAW_UPDATE_FIRST_HOP_SOURCE_VERSIONS:-}"
|
|
SCENARIO="${OPENCLAW_UPDATE_FIRST_HOP_SCENARIO:-all}"
|
|
ARTIFACT_DIR="${OPENCLAW_UPDATE_FIRST_HOP_ARTIFACT_DIR:-$ROOT_DIR/.artifacts/update-first-hop-compat${SOURCE_VERSION_FILTER:+-${SOURCE_VERSION_FILTER//[ ,]/-}}}"
|
|
SOURCE_PACKAGE="${OPENCLAW_UPDATE_FIRST_HOP_SOURCE_PACKAGE_TGZ:-}"
|
|
FIXTURE_ROOT="$(mktemp -d "${TMPDIR:-/tmp}/openclaw-update-first-hop.XXXXXX")"
|
|
PACKAGE_TGZ=""
|
|
FIXTURE_HELPER="$ROOT_DIR/scripts/e2e/lib/update-first-hop-package-fixtures.mjs"
|
|
|
|
cleanup() {
|
|
local exit_status="$?"
|
|
trap - EXIT
|
|
docker_e2e_cleanup_package_tgz "${PACKAGE_TGZ:-}"
|
|
rm -rf "$FIXTURE_ROOT"
|
|
exit "$exit_status"
|
|
}
|
|
trap cleanup EXIT
|
|
|
|
mkdir -p "$ARTIFACT_DIR" "$FIXTURE_ROOT/source" "$FIXTURE_ROOT/packages"
|
|
chmod -R a+rwX "$ARTIFACT_DIR" || true
|
|
|
|
if [ -n "$SOURCE_PACKAGE" ] && [ ! -f "$SOURCE_PACKAGE" ]; then
|
|
echo "source package tarball does not exist: $SOURCE_PACKAGE" >&2
|
|
exit 2
|
|
fi
|
|
if [ -n "$SOURCE_PACKAGE" ] && [ -n "$SOURCE_VERSION_FILTER" ]; then
|
|
echo "an explicit source tarball cannot be combined with OPENCLAW_UPDATE_FIRST_HOP_SOURCE_VERSIONS" >&2
|
|
exit 2
|
|
fi
|
|
case "$SCENARIO" in
|
|
all | source | missing-load-path) ;;
|
|
*)
|
|
echo "invalid first-hop scenario: $SCENARIO" >&2
|
|
exit 2
|
|
;;
|
|
esac
|
|
if [ "$SCENARIO" = "missing-load-path" ] && { [ -n "$SOURCE_PACKAGE" ] || [ -n "$SOURCE_VERSION_FILTER" ]; }; then
|
|
echo "the missing-load-path scenario does not accept a historical source" >&2
|
|
exit 2
|
|
fi
|
|
|
|
PACKAGE_TGZ="$(
|
|
docker_e2e_prepare_package_tgz \
|
|
update-first-hop-compat \
|
|
"${OPENCLAW_UPDATE_FIRST_HOP_CANDIDATE_PACKAGE_TGZ:-}"
|
|
)"
|
|
# Published updaters can intentionally skip same-version payloads. Relabel only
|
|
# fixture metadata so every source performs a real hop with the candidate's bridges.
|
|
FIRST_HOP_TGZ="$FIXTURE_ROOT/first-hop.tgz"
|
|
node "$FIXTURE_HELPER" first-hop-tarball "$PACKAGE_TGZ" "$FIRST_HOP_TGZ" 0 \
|
|
>"$ARTIFACT_DIR/first-hop-fixture.json"
|
|
node "$FIXTURE_HELPER" future-tarball "$FIRST_HOP_TGZ" "$FIXTURE_ROOT/future.tgz" 1 \
|
|
>"$ARTIFACT_DIR/second-hop-fixture.json"
|
|
ADMISSION_PROTOCOL="$(tar -xOf "$PACKAGE_TGZ" package/package.json | node -pe 'JSON.parse(require("node:fs").readFileSync(0, "utf8")).openclaw?.updateAdmissionProtocol ?? ""')"
|
|
if [ "$ADMISSION_PROTOCOL" = "1" ] && [ "$SCENARIO" != "missing-load-path" ]; then
|
|
node "$FIXTURE_HELPER" unsupported-admission-tarball "$FIXTURE_ROOT/future.tgz" \
|
|
"$FIXTURE_ROOT/unsupported-admission.tgz" 2 >"$ARTIFACT_DIR/unsupported-admission-fixture.json"
|
|
fi
|
|
docker_e2e_package_mount_args "$FIRST_HOP_TGZ" /tmp/openclaw-update-first-hop-candidate.tgz
|
|
if [ "$ADMISSION_PROTOCOL" = "1" ] && [ "$SCENARIO" != "missing-load-path" ]; then
|
|
DOCKER_E2E_PACKAGE_ARGS+=(-v "$FIXTURE_ROOT/unsupported-admission.tgz:/tmp/openclaw-update-first-hop-unsupported-admission.tgz:ro")
|
|
fi
|
|
|
|
mkdir -p "$FIXTURE_ROOT/packages/original"
|
|
tar -xzf "$PACKAGE_TGZ" -C "$FIXTURE_ROOT/packages/original"
|
|
|
|
docker_e2e_build_or_reuse \
|
|
"$IMAGE_NAME" \
|
|
update-first-hop-compat \
|
|
"$ROOT_DIR/scripts/e2e/Dockerfile" \
|
|
"$ROOT_DIR" \
|
|
bare \
|
|
"$SKIP_BUILD"
|
|
|
|
SOURCE_VERSIONS=("")
|
|
if [ "$SCENARIO" != "missing-load-path" ] && [ -z "$SOURCE_PACKAGE" ]; then
|
|
SOURCE_VERSIONS=()
|
|
node "$FIXTURE_HELPER" sources "$FIXTURE_ROOT/packages/original/package" \
|
|
"$SOURCE_VERSION_FILTER" >"$FIXTURE_ROOT/source-versions.txt"
|
|
while IFS= read -r version; do
|
|
SOURCE_VERSIONS+=("$version")
|
|
done <"$FIXTURE_ROOT/source-versions.txt"
|
|
fi
|
|
|
|
for version in "${SOURCE_VERSIONS[@]}"; do
|
|
lane_artifact_dir="$ARTIFACT_DIR"
|
|
source_package="${SOURCE_PACKAGE:-$FIRST_HOP_TGZ}"
|
|
expected_missing_chunk=""
|
|
negative_tgz="$FIRST_HOP_TGZ"
|
|
if [ "$SCENARIO" != "missing-load-path" ]; then
|
|
if [ -n "$version" ]; then
|
|
lane_artifact_dir="$ARTIFACT_DIR/$version"
|
|
mkdir -p "$lane_artifact_dir"
|
|
cp "$ARTIFACT_DIR/second-hop-fixture.json" "$lane_artifact_dir/second-hop-fixture.json"
|
|
npm pack "openclaw@$version" --ignore-scripts --json --min-release-age=0 \
|
|
--pack-destination "$FIXTURE_ROOT/source" >"$lane_artifact_dir/source-pack.json"
|
|
source_package="$FIXTURE_ROOT/source/$(
|
|
node "$FIXTURE_HELPER" pack-filename "$lane_artifact_dir/source-pack.json"
|
|
)"
|
|
fi
|
|
node "$FIXTURE_HELPER" source "$FIXTURE_ROOT/packages/original/package" \
|
|
"$source_package" "$version" >"$lane_artifact_dir/source.json"
|
|
expected_missing_chunk="$(node -e '
|
|
const source = JSON.parse(require("node:fs").readFileSync(process.argv[1], "utf8"));
|
|
process.stdout.write(source.expectedMissingChunk ?? "");
|
|
' "$lane_artifact_dir/source.json")"
|
|
negative_tgz="$FIXTURE_ROOT/negative-${version:-explicit}.tgz"
|
|
node "$FIXTURE_HELPER" negative-tarball "$FIRST_HOP_TGZ" "$negative_tgz" \
|
|
"$expected_missing_chunk" >"$lane_artifact_dir/negative-fixture.json"
|
|
{
|
|
printf 'source=%s\n' "$source_package"
|
|
printf 'original_candidate=%s\n' "$PACKAGE_TGZ"
|
|
printf 'candidate=%s\n' "$FIRST_HOP_TGZ"
|
|
printf 'expected_missing_chunk=%s\n' "$expected_missing_chunk"
|
|
shasum -a 256 "$source_package" "$PACKAGE_TGZ" "$FIRST_HOP_TGZ" "$negative_tgz" "$FIXTURE_ROOT/future.tgz"
|
|
if [ "$ADMISSION_PROTOCOL" = "1" ]; then
|
|
shasum -a 256 "$FIXTURE_ROOT/unsupported-admission.tgz"
|
|
fi
|
|
printf '\nsource_build_info=' && tar -xOf "$source_package" package/dist/build-info.json
|
|
printf '\noriginal_candidate_build_info=' && tar -xOf "$PACKAGE_TGZ" package/dist/build-info.json
|
|
printf '\ncandidate_build_info=' && tar -xOf "$FIRST_HOP_TGZ" package/dist/build-info.json
|
|
printf '\nfuture_build_info=' && tar -xOf "$FIXTURE_ROOT/future.tgz" package/dist/build-info.json
|
|
} >"$lane_artifact_dir/inputs.txt"
|
|
fi
|
|
chmod a+rwx "$lane_artifact_dir"
|
|
|
|
echo "Running packaged updater first-hop compatibility Docker E2E (${version:-explicit source})..."
|
|
docker_e2e_run_with_harness \
|
|
-e OPENCLAW_QA_ALLOW_UPDATE_FIRST_HOP=1 \
|
|
-e OPENCLAW_UPDATE_FIRST_HOP_ARTIFACT_DIR=/tmp/openclaw-update-first-hop-artifacts \
|
|
-e OPENCLAW_UPDATE_FIRST_HOP_EXPECTED_MISSING_CHUNK="$expected_missing_chunk" \
|
|
-e OPENCLAW_UPDATE_FIRST_HOP_ADMISSION_PROTOCOL="$ADMISSION_PROTOCOL" \
|
|
-e OPENCLAW_UPDATE_FIRST_HOP_SCENARIO="$SCENARIO" \
|
|
-v "$lane_artifact_dir:/tmp/openclaw-update-first-hop-artifacts" \
|
|
-v "$(docker_e2e_abs_path "$source_package"):/tmp/openclaw-update-first-hop-source.tgz:ro" \
|
|
"${DOCKER_E2E_PACKAGE_ARGS[@]}" \
|
|
-v "$PACKAGE_TGZ:/tmp/openclaw-update-first-hop-original.tgz:ro" \
|
|
-v "$negative_tgz:/tmp/openclaw-update-first-hop-negative.tgz:ro" \
|
|
-v "$FIXTURE_ROOT/future.tgz:/tmp/openclaw-update-first-hop-future.tgz:ro" \
|
|
"$IMAGE_NAME" \
|
|
timeout --kill-after=30s "$DOCKER_RUN_TIMEOUT" \
|
|
bash scripts/e2e/lib/upgrade-survivor/update-first-hop-compat.sh
|
|
done
|
|
|
|
if [ "$SCENARIO" != "missing-load-path" ] && [ -z "$SOURCE_PACKAGE" ]; then
|
|
node -e '
|
|
const fs = require("node:fs"), path = require("node:path");
|
|
const [root, ...versions] = process.argv.slice(1);
|
|
const sources = versions.map(version => JSON.parse(fs.readFileSync(path.join(root, version, "summary.json"), "utf8")));
|
|
fs.writeFileSync(path.join(root, "summary.json"), `${JSON.stringify({ sources }, null, 2)}\n`);
|
|
' "$ARTIFACT_DIR" "${SOURCE_VERSIONS[@]}"
|
|
fi
|