diff --git a/.github/workflows/npm-telegram-beta-e2e.yml b/.github/workflows/npm-telegram-beta-e2e.yml index 4d5dfe3982d0..3703df983d4f 100644 --- a/.github/workflows/npm-telegram-beta-e2e.yml +++ b/.github/workflows/npm-telegram-beta-e2e.yml @@ -456,6 +456,14 @@ jobs: const path = require("node:path"); const [manifestPath, packageDir] = process.argv.slice(2); const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8")); + const dependencyTarballs = Array.isArray(manifest.corePackageTarballs) + ? manifest.corePackageTarballs + : manifest.corePackageTarballs === undefined && Array.isArray(manifest.dependencyTarballs) + ? manifest.dependencyTarballs + : null; + if (!dependencyTarballs) { + throw new Error("package artifact manifest is missing dependency tarball metadata"); + } const entries = [ { packageName: "openclaw", @@ -463,7 +471,7 @@ jobs: tarballName: manifest.tarballName, tarballSha256: manifest.tarballSha256, }, - ...(Array.isArray(manifest.dependencyTarballs) ? manifest.dependencyTarballs : []), + ...dependencyTarballs, ]; const packageNames = new Set(); const tarballNames = new Set(); diff --git a/scripts/release-candidate-checklist.d.mts b/scripts/release-candidate-checklist.d.mts index 93bfc96225ac..7afcc4fe62a3 100644 --- a/scripts/release-candidate-checklist.d.mts +++ b/scripts/release-candidate-checklist.d.mts @@ -185,6 +185,15 @@ export function requireRunIdFromDispatchOutput(output: unknown, workflowFile: un */ export function buildPublishCommand(options: unknown): string; export function validatePreflightManifest(manifest: unknown, params: unknown): void; +export function preflightCorePackageTarballs(manifest: { + corePackageTarballs?: unknown; + dependencyTarballs?: unknown; +}): Array<{ + packageName: string; + packageVersion: string; + tarballName: string; + tarballSha256: string; +}>; export function validateFullManifest(manifest: unknown, params: unknown): void; export function validateTrustedToolingPin({ toolingSha, diff --git a/scripts/release-candidate-checklist.mjs b/scripts/release-candidate-checklist.mjs index 2a4f73b5db47..b88f8091ad0d 100644 --- a/scripts/release-candidate-checklist.mjs +++ b/scripts/release-candidate-checklist.mjs @@ -1200,10 +1200,7 @@ export function validatePreflightManifest(manifest, params) { if (!manifest.tarballName || !manifest.tarballSha256) { throw new Error("npm preflight manifest missing tarball metadata"); } - if (!Array.isArray(manifest.dependencyTarballs)) { - throw new Error("npm preflight manifest missing dependency tarball metadata"); - } - for (const dependency of manifest.dependencyTarballs) { + for (const dependency of preflightCorePackageTarballs(manifest)) { if ( !dependency?.packageName || !dependency.packageVersion || @@ -1216,6 +1213,17 @@ export function validatePreflightManifest(manifest, params) { } } +export function preflightCorePackageTarballs(manifest) { + const hasCorePackageTarballs = Object.hasOwn(manifest, "corePackageTarballs"); + const tarballs = hasCorePackageTarballs + ? manifest.corePackageTarballs + : manifest.dependencyTarballs; + if (!Array.isArray(tarballs)) { + throw new Error("npm preflight manifest missing dependency tarball metadata"); + } + return tarballs; +} + export function validateFullManifest(manifest, params) { if (manifest.workflowName !== "Full Release Validation") { throw new Error(`full validation workflow mismatch: ${manifest.workflowName}`); @@ -1534,7 +1542,7 @@ async function main() { `prepared tarball digest mismatch: expected ${npmManifest.tarballSha256}, got ${actualTarballSha}`, ); } - const dependencyTarballPaths = npmManifest.dependencyTarballs.map((dependency) => { + const dependencyTarballPaths = preflightCorePackageTarballs(npmManifest).map((dependency) => { const dependencyPath = join(npmDir, dependency.tarballName); if (!existsSync(dependencyPath)) { throw new Error(`prepared dependency tarball missing: ${dependencyPath}`); diff --git a/test/scripts/package-acceptance-workflow.test.ts b/test/scripts/package-acceptance-workflow.test.ts index bf887c08d3f4..e5157c834314 100644 --- a/test/scripts/package-acceptance-workflow.test.ts +++ b/test/scripts/package-acceptance-workflow.test.ts @@ -3187,6 +3187,8 @@ describe("package artifact reuse", () => { 'candidate_manifest="${package_dir}/package-candidate.json"', 'find "${package_dir}" -type f -name "*.tgz"', "package artifact manifest contains duplicate package metadata", + "Array.isArray(manifest.corePackageTarballs)", + "manifest.corePackageTarballs === undefined", "package artifact tarball set does not match preflight manifest", "package candidate manifest does not match the OpenClaw tarball", "Package Telegram artifact SHA-256 differs from package_sha256.", diff --git a/test/scripts/release-candidate-checklist.test.ts b/test/scripts/release-candidate-checklist.test.ts index 9fef509f358c..0daece596720 100644 --- a/test/scripts/release-candidate-checklist.test.ts +++ b/test/scripts/release-candidate-checklist.test.ts @@ -14,6 +14,7 @@ import { isDirectReleaseCandidateExecution, parseArgs, parseRunIdFromDispatchOutput, + preflightCorePackageTarballs, reconcileReleaseCandidateState, releaseBranchForTag, resolveArtifactName, @@ -589,6 +590,37 @@ describe("release candidate checklist", () => { ).toThrow("invalid dependency tarball metadata"); }); + it("prefers the complete core package tarball set with legacy manifest fallback", () => { + const legacyTarball = { + packageName: "@openclaw/ai", + packageVersion: "2026.7.1-beta.3", + tarballName: "openclaw-ai-2026.7.1-beta.3.tgz", + tarballSha256: "ai-sha", + }; + const gatewayProtocolTarball = { + packageName: "@openclaw/gateway-protocol", + packageVersion: "2026.7.1-beta.3", + tarballName: "openclaw-gateway-protocol-2026.7.1-beta.3.tgz", + tarballSha256: "protocol-sha", + }; + + expect( + preflightCorePackageTarballs({ + corePackageTarballs: [legacyTarball, gatewayProtocolTarball], + dependencyTarballs: [legacyTarball], + }), + ).toEqual([legacyTarball, gatewayProtocolTarball]); + expect(preflightCorePackageTarballs({ dependencyTarballs: [legacyTarball] })).toEqual([ + legacyTarball, + ]); + expect(() => + preflightCorePackageTarballs({ + corePackageTarballs: null, + dependencyTarballs: [legacyTarball], + }), + ).toThrow("missing dependency tarball metadata"); + }); + it("trusts the npm workflow SHA while binding the candidate through its manifest", () => { const workflowSha = "a".repeat(40); const isTrustedWorkflowAncestor = vi.fn(() => true);