From f5a7632ffc903f1118c97872cc7392e6f6eb9c69 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Tue, 28 Apr 2026 08:31:01 +0100 Subject: [PATCH] ci: allow legacy package stamp warnings --- docs/ci.md | 27 ++++++++++--------- docs/reference/RELEASING.md | 6 +++-- scripts/check-openclaw-package-tarball.mjs | 21 +++++++++++++-- .../check-openclaw-package-tarball.test.ts | 24 +++++++++++++++++ 4 files changed, 61 insertions(+), 17 deletions(-) diff --git a/docs/ci.md b/docs/ci.md index 5448a9bc23d..42e8fabf9da 100644 --- a/docs/ci.md +++ b/docs/ci.md @@ -138,19 +138,20 @@ Acceptance. The Windows packaged and installer fresh lanes also verify that an installed package can import a browser-control override from a raw absolute Windows path. -Package Acceptance has a bounded legacy-compatibility window for already -published packages through `2026.4.25`, including `2026.4.25-beta.*`. Those -allowances are documented here so they do not become permanent silent skips: -known private QA entries in `dist/postinstall-inventory.json` may warn when the -tarball omitted those files; `doctor-switch` may skip the -`gateway install --wrapper` persistence subcase when the package does not expose -that flag; `update-channel-switch` may prune missing `pnpm.patchedDependencies` -from the tarball-derived fake git fixture and may log missing persisted -`update.channel`; plugin smokes may read legacy install-record locations or -accept missing marketplace install-record persistence; and `plugin-update` may -allow config metadata migration while still requiring the install record and -no-reinstall behavior to stay unchanged. Packages after `2026.4.25` must satisfy -the modern contracts; the same conditions fail instead of warn or skip. +Package Acceptance has bounded legacy-compatibility windows for already +published packages. Packages through `2026.4.25`, including `2026.4.25-beta.*`, +may use the compatibility path for known private QA entries in +`dist/postinstall-inventory.json` that point at tarball-omitted files, +`doctor-switch` may skip the `gateway install --wrapper` persistence subcase +when the package does not expose that flag, `update-channel-switch` may prune +missing `pnpm.patchedDependencies` from the tarball-derived fake git fixture and +may log missing persisted `update.channel`, plugin smokes may read legacy +install-record locations or accept missing marketplace install-record +persistence, and `plugin-update` may allow config metadata migration while still +requiring the install record and no-reinstall behavior to stay unchanged. The +published `2026.4.26` package may also warn for local build metadata stamp files +that were already shipped. Later packages must satisfy the modern contracts; the +same conditions fail instead of warn or skip. Examples: diff --git a/docs/reference/RELEASING.md b/docs/reference/RELEASING.md index ef089833a50..dc1269145cf 100644 --- a/docs/reference/RELEASING.md +++ b/docs/reference/RELEASING.md @@ -423,8 +423,10 @@ to npm: private QA inventory entries missing from the tarball, missing `gateway install --wrapper`, missing patch files in the tarball-derived git fixture, missing persisted `update.channel`, legacy plugin install-record locations, missing marketplace install-record persistence, and config metadata -migration during `plugins update`. Packages after `2026.4.25` must satisfy the -modern package contracts; those same gaps fail release validation. +migration during `plugins update`. The published `2026.4.26` package may warn +for local build metadata stamp files that were already shipped. Later packages +must satisfy the modern package contracts; those same gaps fail release +validation. Use broader Package Acceptance profiles when the release question is about an actual installable package: diff --git a/scripts/check-openclaw-package-tarball.mjs b/scripts/check-openclaw-package-tarball.mjs index 8aeb3ca6b47..55516ba0741 100644 --- a/scripts/check-openclaw-package-tarball.mjs +++ b/scripts/check-openclaw-package-tarball.mjs @@ -40,6 +40,7 @@ const entrySet = new Set(normalized); const errors = []; const warnings = []; const LEGACY_PACKAGE_ACCEPTANCE_COMPAT_MAX = { year: 2026, month: 4, day: 25 }; +const LEGACY_LOCAL_BUILD_METADATA_COMPAT_MAX = { year: 2026, month: 4, day: 26 }; const FORBIDDEN_LOCAL_BUILD_METADATA_FILES = new Set(LOCAL_BUILD_METADATA_DIST_PATHS); const LEGACY_OMITTED_PRIVATE_QA_INVENTORY_PREFIXES = [ @@ -97,6 +98,11 @@ function isLegacyPackageAcceptanceCompatVersion(version) { return parsed ? compareCalver(parsed, LEGACY_PACKAGE_ACCEPTANCE_COMPAT_MAX) <= 0 : false; } +function isLegacyLocalBuildMetadataCompatVersion(version) { + const parsed = parseCalver(version); + return parsed ? compareCalver(parsed, LEGACY_LOCAL_BUILD_METADATA_COMPAT_MAX) <= 0 : false; +} + function readTarEntry(entryPath) { const candidates = [entryPath, `package/${entryPath}`]; for (const candidate of candidates) { @@ -123,8 +129,21 @@ if (!entrySet.has("package.json")) { if (!normalized.some((entry) => entry.startsWith("dist/"))) { errors.push("missing dist/ entries"); } +let packageVersion = ""; +if (entrySet.has("package.json")) { + try { + const packageJson = JSON.parse(readTarEntry("package.json")); + packageVersion = typeof packageJson.version === "string" ? packageJson.version : ""; + } catch { + packageVersion = ""; + } +} for (const forbiddenEntry of FORBIDDEN_LOCAL_BUILD_METADATA_FILES) { if (entrySet.has(forbiddenEntry)) { + if (isLegacyLocalBuildMetadataCompatVersion(packageVersion)) { + warnings.push(`legacy package includes local build metadata tar entry ${forbiddenEntry}`); + continue; + } errors.push(`forbidden local build metadata tar entry ${forbiddenEntry}`); } } @@ -133,8 +152,6 @@ if (!entrySet.has("dist/postinstall-inventory.json")) { } if (entrySet.has("dist/postinstall-inventory.json")) { try { - const packageJson = JSON.parse(readTarEntry("package.json")); - const packageVersion = typeof packageJson.version === "string" ? packageJson.version : ""; const allowLegacyPrivateQaInventoryOmissions = isLegacyPackageAcceptanceCompatVersion(packageVersion); const inventory = JSON.parse(readTarEntry("dist/postinstall-inventory.json")); diff --git a/test/scripts/check-openclaw-package-tarball.test.ts b/test/scripts/check-openclaw-package-tarball.test.ts index dbba4e4abbf..fa83805980d 100644 --- a/test/scripts/check-openclaw-package-tarball.test.ts +++ b/test/scripts/check-openclaw-package-tarball.test.ts @@ -103,6 +103,30 @@ describe("check-openclaw-package-tarball", () => { "forbidden local build metadata tar entry dist/.runtime-postbuildstamp", ); }, + "2026.4.27", + ); + }); + + it("allows local build metadata in already published legacy packages through 2026.4.26", () => { + withTarball( + ["dist/index.js", ...LOCAL_BUILD_METADATA_DIST_PATHS], + { + "dist/index.js": "export {};\n", + ...Object.fromEntries(LOCAL_BUILD_METADATA_DIST_PATHS.map((entry) => [entry, "{}\n"])), + }, + (tarball) => { + const result = spawnSync("node", [CHECK_SCRIPT, tarball], { encoding: "utf8" }); + + expect(result.status, result.stderr).toBe(0); + expect(result.stderr).toContain( + "legacy package includes local build metadata tar entry dist/.buildstamp", + ); + expect(result.stderr).toContain( + "legacy package includes local build metadata tar entry dist/.runtime-postbuildstamp", + ); + expect(result.stdout).toContain("OpenClaw package tarball integrity passed."); + }, + "2026.4.26", ); }); });