fix(release): keep install smoke tarballs local

This commit is contained in:
Vincent Koc 2026-06-17 02:57:41 +02:00
parent 53accb122d
commit dd7376fdcb
No known key found for this signature in database
2 changed files with 93 additions and 22 deletions

View file

@ -151,6 +151,28 @@ console.log(
' "$baseline_pack_json_file" "$update_pack_json_file"
}
read_pack_tarball_filename() {
local pack_json_file="$1"
node -e '
const fs = require("node:fs");
const path = require("node:path");
const raw = fs.readFileSync(process.argv[1], "utf8") || "[]";
const parsed = JSON.parse(raw);
const last = Array.isArray(parsed) ? parsed.at(-1) : null;
const filename = typeof last?.filename === "string" ? last.filename.trim() : "";
if (
!filename.endsWith(".tgz") ||
filename.includes("\0") ||
filename !== path.basename(filename) ||
filename !== path.win32.basename(filename)
) {
console.error(`ERROR: npm pack reported unsafe tarball filename ${JSON.stringify(filename)}`);
process.exit(1);
}
process.stdout.write(filename);
' "$pack_json_file"
}
SMOKE_IMAGE="${OPENCLAW_INSTALL_SMOKE_IMAGE:-openclaw-install-smoke:local}"
NONROOT_IMAGE="${OPENCLAW_INSTALL_NONROOT_IMAGE:-openclaw-install-nonroot:local}"
SMOKE_PLATFORM="$(resolve_default_smoke_platform)"
@ -298,17 +320,7 @@ prepare_update_tarball() {
node scripts/check-package-dist-imports.mjs "$ROOT_DIR"
quiet_npm pack --ignore-scripts --json --pack-destination "$UPDATE_DIR" >"$pack_json_file"
fi
UPDATE_TGZ_FILE="$(
node -e '
const raw = require("node:fs").readFileSync(process.argv[1], "utf8") || "[]";
const parsed = JSON.parse(raw);
const last = Array.isArray(parsed) ? parsed.at(-1) : null;
if (!last || typeof last.filename !== "string" || last.filename.length === 0) {
process.exit(1);
}
process.stdout.write(last.filename);
' "$pack_json_file"
)"
UPDATE_TGZ_FILE="$(read_pack_tarball_filename "$pack_json_file")"
if [[ -z "$UPDATE_PACKAGE_SPEC" ]]; then
node scripts/check-openclaw-package-tarball.mjs "${UPDATE_DIR}/${UPDATE_TGZ_FILE}"
fi
@ -334,17 +346,7 @@ process.stdout.write(last.version);
echo "==> Pack baseline tgz: ${PACKAGE_NAME}@${UPDATE_BASELINE_VERSION}"
quiet_npm pack "${PACKAGE_NAME}@${UPDATE_BASELINE_VERSION}" --json --pack-destination "$UPDATE_DIR" >"$baseline_pack_json_file"
BASELINE_TGZ_FILE="$(
node -e '
const raw = require("node:fs").readFileSync(process.argv[1], "utf8") || "[]";
const parsed = JSON.parse(raw);
const last = Array.isArray(parsed) ? parsed.at(-1) : null;
if (!last || typeof last.filename !== "string" || last.filename.length === 0) {
process.exit(1);
}
process.stdout.write(last.filename);
' "$baseline_pack_json_file"
)"
BASELINE_TGZ_FILE="$(read_pack_tarball_filename "$baseline_pack_json_file")"
UPDATE_BASELINE_VERSION="$(
node -e '
const raw = require("node:fs").readFileSync(process.argv[1], "utf8") || "[]";

View file

@ -93,6 +93,39 @@ function runDefaultSmokePlatform(env: Record<string, string>, hostArch: string):
return result.stdout;
}
function extractReadPackTarballFilename(): string {
const script = readFileSync(SCRIPT_PATH, "utf8");
const match = script.match(/(read_pack_tarball_filename\(\) \{[\s\S]*?\n\})\n\nSMOKE_IMAGE/u);
if (!match) {
throw new Error("read_pack_tarball_filename helper was not found");
}
return match[1];
}
function runReadPackTarballFilename(filename: string) {
return spawnSync(
"bash",
[
"--noprofile",
"--norc",
"-c",
`${extractReadPackTarballFilename()}
pack_json_file="$(mktemp)"
trap 'rm -f "$pack_json_file"' EXIT
printf '%s' "$PACK_JSON" >"$pack_json_file"
read_pack_tarball_filename "$pack_json_file"`,
],
{
encoding: "utf8",
env: {
HOME: "/tmp",
PACK_JSON: JSON.stringify([{ filename }]),
PATH: process.env.PATH ?? "",
},
},
);
}
describe("test-install-sh-docker", () => {
it("defaults ARM hosts to native arm64 while keeping x64 CI on amd64", () => {
expect(runDefaultSmokePlatform({ CI: "true" }, "aarch64")).toBe("linux/arm64");
@ -334,6 +367,42 @@ describe("test-install-sh-docker", () => {
expect(script).toContain("install smoke cannot verify pack budget");
});
it("keeps npm pack tarball filenames local before serving update artifacts", () => {
const script = readFileSync(SCRIPT_PATH, "utf8");
expect(script).toContain("read_pack_tarball_filename()");
expect(script).toContain('UPDATE_TGZ_FILE="$(read_pack_tarball_filename "$pack_json_file")"');
expect(script).toContain(
'BASELINE_TGZ_FILE="$(read_pack_tarball_filename "$baseline_pack_json_file")"',
);
expect(script).toContain("filename !== path.basename(filename)");
expect(script).toContain("filename !== path.win32.basename(filename)");
expect(script).toContain("npm pack reported unsafe tarball filename");
});
it("rejects path-like npm pack tarball filenames in update smoke metadata", () => {
expect(runReadPackTarballFilename("openclaw-2026.6.17.tgz")).toMatchObject({
status: 0,
stdout: "openclaw-2026.6.17.tgz",
});
const unsafeFilenames = [
"../openclaw.tgz",
"nested/openclaw.tgz",
"nested\\openclaw.tgz",
"/tmp/openclaw.tgz",
"C:\\temp\\openclaw.tgz",
"openclaw.tar.gz",
];
for (const filename of unsafeFilenames) {
const result = runReadPackTarballFilename(filename);
expect(result.status, filename).not.toBe(0);
expect(result.stderr, filename).toContain("npm pack reported unsafe tarball filename");
}
});
it("writes the package dist inventory before packing ignore-scripts tarballs", () => {
const script = readFileSync(SCRIPT_PATH, "utf8");