mirror of
https://github.com/openclaw/openclaw.git
synced 2026-09-28 14:12:28 +08:00
fix: exercise same-version candidates in updater compatibility tests (#142811)
* fix: exercise same-version candidates in updater compatibility tests * refactor: keep negative updater fixture packer private
This commit is contained in:
@@ -364,9 +364,12 @@ candidate's bridges. Existing older compatibility aliases remain separately
|
||||
owned by their original upgrade contracts.
|
||||
|
||||
The default `update-first-hop-compat` lane runs each recorded release against the
|
||||
candidate, with separate artifacts per version. It requires the installed build
|
||||
identity and a restarted service, including for same-version or lower-version
|
||||
explicit candidate tarballs. The 2026.9.1 negative control demonstrates the
|
||||
candidate, with separate artifacts per version. Published updaters may correctly
|
||||
skip a same-version tarball, so the lane stamps only test-artifact version metadata:
|
||||
first hop `2026.9.99-first-hop.0` retains compatibility bridges; second hop
|
||||
`2026.9.99-first-hop.1` removes them. The original candidate stays unchanged, and
|
||||
transformation receipts bind package digests and every changed or removed member.
|
||||
Both hops still require the exact installed build identity and a restarted service. The 2026.9.1 negative control demonstrates the
|
||||
missing restart import; releases that already preload that helper record the
|
||||
negative control as not applicable while retaining the positive first-hop and
|
||||
bridge-free future-hop checks. An explicit source tarball still selects one
|
||||
|
||||
@@ -176,9 +176,7 @@ function futureFixtureVersion(sequence) {
|
||||
return FUTURE_FIXTURE_VERSION.replace(/0$/, String(sequence));
|
||||
}
|
||||
|
||||
export function markFutureUpdateFixture(packageRoot, sequence = 0) {
|
||||
const version = futureFixtureVersion(sequence);
|
||||
removeLegacyUpdateCompatChunks(packageRoot);
|
||||
function stampFixtureVersion(packageRoot, version) {
|
||||
const paths = resolveFixturePaths(packageRoot);
|
||||
const packageJson = readJson(paths.packageJson);
|
||||
const buildInfo = readJson(paths.buildInfo);
|
||||
@@ -189,6 +187,34 @@ export function markFutureUpdateFixture(packageRoot, sequence = 0) {
|
||||
writeJson(paths.buildInfo, buildInfo);
|
||||
}
|
||||
|
||||
export function markFutureUpdateFixture(packageRoot, sequence = 0) {
|
||||
const version = futureFixtureVersion(sequence);
|
||||
removeLegacyUpdateCompatChunks(packageRoot);
|
||||
stampFixtureVersion(packageRoot, version);
|
||||
}
|
||||
|
||||
function packageMembers(root) {
|
||||
const members = new Map();
|
||||
const visit = (directory) => {
|
||||
for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {
|
||||
const file = path.join(directory, entry.name);
|
||||
if (entry.isDirectory()) {
|
||||
visit(file);
|
||||
} else {
|
||||
const bytes = entry.isSymbolicLink()
|
||||
? `link:${fs.readlinkSync(file)}`
|
||||
: fs.readFileSync(file);
|
||||
members.set(
|
||||
path.relative(root, file).split(path.sep).join("/"),
|
||||
createHash("sha256").update(bytes).digest("hex"),
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
visit(root);
|
||||
return new Map([...members].toSorted(([left], [right]) => left.localeCompare(right)));
|
||||
}
|
||||
|
||||
function packTransformedFixture(candidateTarball, outputTarball, transform) {
|
||||
const source = path.resolve(candidateTarball);
|
||||
const output = path.resolve(outputTarball);
|
||||
@@ -200,7 +226,9 @@ function packTransformedFixture(candidateTarball, outputTarball, transform) {
|
||||
execFileSync("tar", ["-xzf", source, "-C", root]);
|
||||
const packageRoot = path.join(root, "package");
|
||||
const sourceVersion = readJson(path.join(packageRoot, "package.json")).version;
|
||||
const before = packageMembers(packageRoot);
|
||||
transform(packageRoot);
|
||||
const after = packageMembers(packageRoot);
|
||||
execFileSync("tar", ["-czf", output, "-C", root, "package"], {
|
||||
env: { ...process.env, COPYFILE_DISABLE: "1" },
|
||||
});
|
||||
@@ -209,12 +237,45 @@ function packTransformedFixture(candidateTarball, outputTarball, transform) {
|
||||
targetVersion: readJson(path.join(packageRoot, "package.json")).version,
|
||||
sourceSha256: createHash("sha256").update(fs.readFileSync(source)).digest("hex"),
|
||||
targetSha256: createHash("sha256").update(fs.readFileSync(output)).digest("hex"),
|
||||
members: {
|
||||
sourceSha256: createHash("sha256")
|
||||
.update(JSON.stringify([...before]))
|
||||
.digest("hex"),
|
||||
targetSha256: createHash("sha256")
|
||||
.update(JSON.stringify([...after]))
|
||||
.digest("hex"),
|
||||
changes: [...new Set([...before.keys(), ...after.keys()])]
|
||||
.toSorted((left, right) => left.localeCompare(right))
|
||||
.filter((name) => before.get(name) !== after.get(name))
|
||||
.map((name) => ({
|
||||
path: name,
|
||||
before: before.get(name) ?? null,
|
||||
after: after.get(name) ?? null,
|
||||
})),
|
||||
},
|
||||
};
|
||||
} finally {
|
||||
fs.rmSync(root, { recursive: true, force: true });
|
||||
}
|
||||
}
|
||||
|
||||
export function packFirstHopUpdateFixture(candidateTarball, outputTarball, sequence = 0) {
|
||||
const version = futureFixtureVersion(sequence);
|
||||
return {
|
||||
method: "candidate-same-schema-first-hop-fixture",
|
||||
...packTransformedFixture(candidateTarball, outputTarball, (root) => {
|
||||
stampFixtureVersion(root, version);
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
function packNegativeUpdateFixture(candidateTarball, outputTarball) {
|
||||
return {
|
||||
method: "candidate-missing-compatibility-fixture",
|
||||
...packTransformedFixture(candidateTarball, outputTarball, removeLegacyUpdateCompatChunks),
|
||||
};
|
||||
}
|
||||
|
||||
export function packFutureUpdateFixture(candidateTarball, outputTarball, sequence = 0) {
|
||||
return {
|
||||
method: "candidate-same-schema-self-update-fixture",
|
||||
@@ -273,11 +334,19 @@ function main() {
|
||||
return;
|
||||
}
|
||||
if (
|
||||
(mode === "future-tarball" || mode === "future-runtime-tarball") &&
|
||||
(mode === "first-hop-tarball" ||
|
||||
mode === "negative-tarball" ||
|
||||
mode === "future-tarball" ||
|
||||
mode === "future-runtime-tarball") &&
|
||||
packageRoot &&
|
||||
outputTarball
|
||||
) {
|
||||
const pack = mode === "future-tarball" ? packFutureUpdateFixture : packFutureRuntimeFixture;
|
||||
const pack = {
|
||||
"first-hop-tarball": packFirstHopUpdateFixture,
|
||||
"negative-tarball": packNegativeUpdateFixture,
|
||||
"future-tarball": packFutureUpdateFixture,
|
||||
"future-runtime-tarball": packFutureRuntimeFixture,
|
||||
}[mode];
|
||||
process.stdout.write(
|
||||
`${JSON.stringify(pack(packageRoot, outputTarball, sequence === undefined ? 0 : Number(sequence)), null, 2)}\n`,
|
||||
);
|
||||
@@ -285,7 +354,7 @@ function main() {
|
||||
}
|
||||
if (!packageRoot || (mode !== "negative" && mode !== "future")) {
|
||||
throw new Error(
|
||||
"usage: update-first-hop-package-fixtures.mjs <negative|future> <package-root> OR <future-tarball|future-runtime-tarball> <source.tgz> <new-output.tgz> [sequence0–9]",
|
||||
"usage: update-first-hop-package-fixtures.mjs <negative|future> <package-root> OR <first-hop-tarball|negative-tarball|future-tarball|future-runtime-tarball> <source.tgz> <new-output.tgz> [sequence0–9]",
|
||||
);
|
||||
}
|
||||
if (mode === "negative") {
|
||||
|
||||
@@ -12,6 +12,7 @@ fi
|
||||
|
||||
SOURCE_PACKAGE=/tmp/openclaw-update-first-hop-source.tgz
|
||||
CANDIDATE_PACKAGE=/tmp/openclaw-update-first-hop-candidate.tgz
|
||||
ORIGINAL_CANDIDATE_PACKAGE=/tmp/openclaw-update-first-hop-original.tgz
|
||||
NEGATIVE_PACKAGE=/tmp/openclaw-update-first-hop-negative.tgz
|
||||
FUTURE_PACKAGE=/tmp/openclaw-update-first-hop-future.tgz
|
||||
ARTIFACT_DIR="${OPENCLAW_UPDATE_FIRST_HOP_ARTIFACT_DIR:-/tmp/openclaw-update-first-hop-artifacts}"
|
||||
@@ -30,7 +31,7 @@ export npm_config_audit=false
|
||||
export npm_config_fund=false
|
||||
export npm_config_loglevel=error
|
||||
|
||||
for package_path in "$SOURCE_PACKAGE" "$CANDIDATE_PACKAGE" "$NEGATIVE_PACKAGE" "$FUTURE_PACKAGE" "$ARTIFACT_DIR/source.json"; do
|
||||
for package_path in "$SOURCE_PACKAGE" "$CANDIDATE_PACKAGE" "$ORIGINAL_CANDIDATE_PACKAGE" "$NEGATIVE_PACKAGE" "$FUTURE_PACKAGE" "$ARTIFACT_DIR/source.json"; do
|
||||
if [ ! -f "$package_path" ]; then
|
||||
echo "missing package input: $package_path" >&2
|
||||
exit 2
|
||||
@@ -38,7 +39,7 @@ for package_path in "$SOURCE_PACKAGE" "$CANDIDATE_PACKAGE" "$NEGATIVE_PACKAGE" "
|
||||
done
|
||||
mkdir -p "$ARTIFACT_DIR"
|
||||
source_version="$(tar -xOf "$SOURCE_PACKAGE" package/package.json | node -pe 'JSON.parse(require("node:fs").readFileSync(0, "utf8")).version')"
|
||||
candidate_version="$(tar -xOf "$CANDIDATE_PACKAGE" package/package.json | node -pe 'JSON.parse(require("node:fs").readFileSync(0, "utf8")).version')"
|
||||
candidate_source_version="$(tar -xOf "$ORIGINAL_CANDIDATE_PACKAGE" package/package.json | node -pe 'JSON.parse(require("node:fs").readFileSync(0, "utf8")).version')"
|
||||
|
||||
package_root() {
|
||||
printf '%s/lib/node_modules/openclaw\n' "$npm_config_prefix"
|
||||
@@ -196,7 +197,7 @@ run_positive_hops() {
|
||||
|
||||
run_update "$lane-first" "$CANDIDATE_PACKAGE"
|
||||
assert_installed_build "$CANDIDATE_PACKAGE" "$ARTIFACT_DIR/$lane-first-build-info.json"
|
||||
if [ "$candidate_version" = "2026.9.3" ]; then
|
||||
if [ "$candidate_source_version" = "2026.9.3" ]; then
|
||||
node scripts/e2e/lib/external-package-transition.mjs schema 16 \
|
||||
>"$ARTIFACT_DIR/$lane-first-shared-schema.json"
|
||||
fi
|
||||
|
||||
@@ -50,17 +50,19 @@ PACKAGE_TGZ="$(
|
||||
update-first-hop-compat \
|
||||
"${OPENCLAW_UPDATE_FIRST_HOP_CANDIDATE_PACKAGE_TGZ:-}"
|
||||
)"
|
||||
docker_e2e_package_mount_args "$PACKAGE_TGZ" /tmp/openclaw-update-first-hop-candidate.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" negative-tarball "$FIRST_HOP_TGZ" "$FIXTURE_ROOT/negative.tgz" \
|
||||
>"$ARTIFACT_DIR/negative-fixture.json"
|
||||
node "$FIXTURE_HELPER" future-tarball "$FIRST_HOP_TGZ" "$FIXTURE_ROOT/future.tgz" 1 \
|
||||
>"$ARTIFACT_DIR/second-hop-fixture.json"
|
||||
docker_e2e_package_mount_args "$FIRST_HOP_TGZ" /tmp/openclaw-update-first-hop-candidate.tgz
|
||||
|
||||
mkdir -p "$FIXTURE_ROOT/packages/negative" "$FIXTURE_ROOT/packages/future"
|
||||
tar -xzf "$PACKAGE_TGZ" -C "$FIXTURE_ROOT/packages/negative"
|
||||
tar -xzf "$PACKAGE_TGZ" -C "$FIXTURE_ROOT/packages/future"
|
||||
node "$FIXTURE_HELPER" negative "$FIXTURE_ROOT/packages/negative/package"
|
||||
node "$FIXTURE_HELPER" future "$FIXTURE_ROOT/packages/future/package"
|
||||
COPYFILE_DISABLE=1 tar --no-xattrs -czf "$FIXTURE_ROOT/negative.tgz" \
|
||||
-C "$FIXTURE_ROOT/packages/negative" package
|
||||
COPYFILE_DISABLE=1 tar --no-xattrs -czf "$FIXTURE_ROOT/future.tgz" \
|
||||
-C "$FIXTURE_ROOT/packages/future" package
|
||||
mkdir -p "$FIXTURE_ROOT/packages/original"
|
||||
tar -xzf "$PACKAGE_TGZ" -C "$FIXTURE_ROOT/packages/original"
|
||||
|
||||
docker_e2e_build_or_reuse \
|
||||
"$IMAGE_NAME" \
|
||||
@@ -73,7 +75,7 @@ docker_e2e_build_or_reuse \
|
||||
SOURCE_VERSIONS=("")
|
||||
if [ -z "$SOURCE_PACKAGE" ]; then
|
||||
SOURCE_VERSIONS=()
|
||||
node "$FIXTURE_HELPER" sources "$FIXTURE_ROOT/packages/negative/package" \
|
||||
node "$FIXTURE_HELPER" sources "$FIXTURE_ROOT/packages/original/package" \
|
||||
>"$FIXTURE_ROOT/source-versions.txt"
|
||||
while IFS= read -r version; do
|
||||
SOURCE_VERSIONS+=("$version")
|
||||
@@ -95,7 +97,7 @@ for version in "${SOURCE_VERSIONS[@]}"; do
|
||||
' "$lane_artifact_dir/source-pack.json")"
|
||||
fi
|
||||
chmod a+rwx "$lane_artifact_dir"
|
||||
node "$FIXTURE_HELPER" source "$FIXTURE_ROOT/packages/negative/package" \
|
||||
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"));
|
||||
@@ -103,11 +105,13 @@ for version in "${SOURCE_VERSIONS[@]}"; do
|
||||
' "$lane_artifact_dir/source.json")"
|
||||
{
|
||||
printf 'source=%s\n' "$source_package"
|
||||
printf 'candidate=%s\n' "$PACKAGE_TGZ"
|
||||
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" "$FIXTURE_ROOT/negative.tgz" "$FIXTURE_ROOT/future.tgz"
|
||||
shasum -a 256 "$source_package" "$PACKAGE_TGZ" "$FIRST_HOP_TGZ" "$FIXTURE_ROOT/negative.tgz" "$FIXTURE_ROOT/future.tgz"
|
||||
printf '\nsource_build_info=' && tar -xOf "$source_package" package/dist/build-info.json
|
||||
printf '\ncandidate_build_info=' && tar -xOf "$PACKAGE_TGZ" 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"
|
||||
|
||||
@@ -119,6 +123,7 @@ for version in "${SOURCE_VERSIONS[@]}"; do
|
||||
-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 "$FIXTURE_ROOT/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" \
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
LEGACY_UPDATE_COMPAT_CHUNKS,
|
||||
listFirstHopSourceVersions,
|
||||
markFutureUpdateFixture,
|
||||
packFirstHopUpdateFixture,
|
||||
packFutureUpdateFixture,
|
||||
removeLegacyUpdateCompatChunks,
|
||||
} from "../../scripts/e2e/lib/update-first-hop-package-fixtures.mjs";
|
||||
@@ -234,7 +235,11 @@ describe("first-hop package fixtures", () => {
|
||||
const original = fs.readFileSync(candidate);
|
||||
const receipts = [0, 1].map((sequence) => {
|
||||
const output = path.join(root, `future-${sequence}.tgz`);
|
||||
const receipt = packFutureUpdateFixture(candidate, output, sequence);
|
||||
const input = sequence === 0 ? candidate : path.join(root, "future-0.tgz");
|
||||
const receipt =
|
||||
sequence === 0
|
||||
? packFirstHopUpdateFixture(input, output, sequence)
|
||||
: packFutureUpdateFixture(input, output, sequence);
|
||||
const pkg = JSON.parse(
|
||||
execFileSync("tar", ["-xOf", output, "package/package.json"], { encoding: "utf8" }),
|
||||
);
|
||||
@@ -243,7 +248,40 @@ describe("first-hop package fixtures", () => {
|
||||
expect(
|
||||
execFileSync("tar", ["-xOf", output, "package/dist/index.js"], { encoding: "utf8" }),
|
||||
).toBe("export {};\n");
|
||||
expect(receipt.sourceVersion).toBe("2026.8.1");
|
||||
expect(receipt.sourceVersion).toBe(sequence === 0 ? "2026.8.1" : FUTURE_FIXTURE_VERSION);
|
||||
expect(receipt.members.changes.map((entry: { path: string }) => entry.path)).toEqual(
|
||||
[
|
||||
"dist/build-info.json",
|
||||
"package.json",
|
||||
...(sequence === 0
|
||||
? []
|
||||
: [
|
||||
"dist/postinstall-inventory.json",
|
||||
...LEGACY_UPDATE_COMPAT_CHUNKS.map((name) => `dist/${name}`),
|
||||
]),
|
||||
].toSorted((left, right) => left.localeCompare(right)),
|
||||
);
|
||||
const members = execFileSync("tar", ["-tzf", output], { encoding: "utf8" });
|
||||
for (const change of receipt.members.changes) {
|
||||
const entry = `package/${change.path}`;
|
||||
const beforeBytes = execFileSync("tar", ["-xOf", input, entry]);
|
||||
expect(change.before).toBe(createHash("sha256").update(beforeBytes).digest("hex"));
|
||||
expect(change.after).toBe(
|
||||
members.split("\n").includes(entry)
|
||||
? createHash("sha256")
|
||||
.update(execFileSync("tar", ["-xOf", output, entry]))
|
||||
.digest("hex")
|
||||
: null,
|
||||
);
|
||||
}
|
||||
for (const bridge of LEGACY_UPDATE_COMPAT_CHUNKS) {
|
||||
expect(members.includes(`package/dist/${bridge}`)).toBe(sequence === 0);
|
||||
if (sequence === 0) {
|
||||
expect(execFileSync("tar", ["-xOf", output, `package/dist/${bridge}`])).toEqual(
|
||||
execFileSync("tar", ["-xOf", candidate, `package/dist/${bridge}`]),
|
||||
);
|
||||
}
|
||||
}
|
||||
const unpacked = path.join(root, `unpacked-${sequence}`);
|
||||
fs.mkdirSync(unpacked);
|
||||
execFileSync("tar", ["-xzf", output, "-C", unpacked]);
|
||||
@@ -265,6 +303,7 @@ describe("first-hop package fixtures", () => {
|
||||
"2026.9.99-first-hop.1",
|
||||
]);
|
||||
expect(new Set(receipts.map((receipt) => receipt.targetSha256)).size).toBe(2);
|
||||
expect(receipts[1]?.sourceSha256).toBe(receipts[0]?.targetSha256);
|
||||
expect(fs.readFileSync(candidate)).toEqual(original);
|
||||
expect(() => packFutureUpdateFixture(candidate, candidate)).toThrow("new tarball path");
|
||||
expect(fs.readFileSync(candidate)).toEqual(original);
|
||||
@@ -444,12 +483,22 @@ describe("first-hop package fixtures", () => {
|
||||
`#!${process.execPath}
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { execFileSync } from "node:child_process";
|
||||
if (process.argv[2] === "run") {
|
||||
const args = process.argv.slice(3);
|
||||
fs.appendFileSync(process.env.DOCKER_ARGS_FILE, JSON.stringify(args) + "\\n");
|
||||
const artifact = args.find(arg => arg.endsWith(":/tmp/openclaw-update-first-hop-artifacts")).split(":")[0];
|
||||
const source = JSON.parse(fs.readFileSync(path.join(artifact, "source.json"), "utf8"));
|
||||
fs.writeFileSync(path.join(artifact, "summary.json"), JSON.stringify({ source }));
|
||||
const inspect = (name) => {
|
||||
const mount = args.find(arg => arg.endsWith(":/tmp/openclaw-update-first-hop-" + name + ".tgz:ro"));
|
||||
if (!mount) return undefined;
|
||||
const archive = mount.split(":")[0];
|
||||
const manifest = JSON.parse(execFileSync("tar", ["-xOf", archive, "package/package.json"], { encoding: "utf8" }));
|
||||
const entries = execFileSync("tar", ["-tzf", archive], { encoding: "utf8" }).trim().split("\\n");
|
||||
return { version: manifest.version, entries };
|
||||
};
|
||||
fs.writeFileSync(path.join(artifact, "summary.json"), JSON.stringify({ source,
|
||||
candidate: inspect("candidate"), negative: inspect("negative"), future: inspect("future"), original: inspect("original") }));
|
||||
}
|
||||
`,
|
||||
{ mode: 0o755 },
|
||||
@@ -493,12 +542,46 @@ process.stdout.write(JSON.stringify([{ filename }]));
|
||||
.split("\n")
|
||||
.map((line) => JSON.parse(line));
|
||||
expect(invocations).toHaveLength(sourceMode === "explicit" ? 1 : 3);
|
||||
const firstFixture = JSON.parse(
|
||||
fs.readFileSync(path.join(root, "artifacts/first-hop-fixture.json"), "utf8"),
|
||||
);
|
||||
const negativeFixture = JSON.parse(
|
||||
fs.readFileSync(path.join(root, "artifacts/negative-fixture.json"), "utf8"),
|
||||
);
|
||||
const secondFixture = JSON.parse(
|
||||
fs.readFileSync(path.join(root, "artifacts/second-hop-fixture.json"), "utf8"),
|
||||
);
|
||||
expect(firstFixture.sourceSha256).toBe(
|
||||
createHash("sha256").update(fs.readFileSync(tarball)).digest("hex"),
|
||||
);
|
||||
expect(firstFixture.members.changes.map((entry: { path: string }) => entry.path)).toEqual([
|
||||
"dist/build-info.json",
|
||||
"package.json",
|
||||
]);
|
||||
expect(negativeFixture.sourceSha256).toBe(firstFixture.targetSha256);
|
||||
expect(secondFixture.sourceSha256).toBe(firstFixture.targetSha256);
|
||||
expect(negativeFixture.sourceVersion).toBe(negativeFixture.targetVersion);
|
||||
const recorded = JSON.parse(
|
||||
fs.readFileSync(path.join(root, "artifacts/summary.json"), "utf8"),
|
||||
);
|
||||
const packages = sourceMode === "recorded" ? recorded.sources : [recorded];
|
||||
for (const artifact of packages) {
|
||||
expect(artifact.candidate.version).toBe("2026.9.99-first-hop.0");
|
||||
expect(artifact.negative.version).toBe("2026.9.99-first-hop.0");
|
||||
expect(artifact.future.version).toBe("2026.9.99-first-hop.1");
|
||||
expect(artifact.original.version).toBe("2026.8.1");
|
||||
for (const bridge of LEGACY_UPDATE_COMPAT_CHUNKS) {
|
||||
expect(artifact.candidate.entries).toContain(`package/dist/${bridge}`);
|
||||
expect(artifact.negative.entries).not.toContain(`package/dist/${bridge}`);
|
||||
expect(artifact.future.entries).not.toContain(`package/dist/${bridge}`);
|
||||
}
|
||||
}
|
||||
for (const args of invocations) {
|
||||
expect(args[args.indexOf("--entrypoint") + 1]).toBe(
|
||||
"/opt/openclaw-e2e/scripts/e2e/lib/prepublish-plugin-registry.sh",
|
||||
);
|
||||
expect(args).toContain(`${registry}:/tmp/openclaw-prepublish-plugin-registry:ro`);
|
||||
expect(args).toContain(`${tarball}:/tmp/openclaw-update-first-hop-candidate.tgz:ro`);
|
||||
expect(args).toContain(`${tarball}:/tmp/openclaw-update-first-hop-original.tgz:ro`);
|
||||
expect(args).toContain("OPENCLAW_PREPUBLISH_PLUGIN_REGISTRY_CANDIDATE_VERSION=2026.8.1");
|
||||
expect(args).toContain("bash");
|
||||
expect(args).toContain("scripts/e2e/lib/upgrade-survivor/update-first-hop-compat.sh");
|
||||
|
||||
Reference in New Issue
Block a user