fix(release): accept complete core tarball manifests (#112813)

* fix(release): consume complete core tarball manifest

* fix(release): declare core tarball selector
This commit is contained in:
Peter Steinberger
2026-07-22 21:02:47 -04:00
committed by GitHub
parent 8ef067a216
commit 79d4e4a3ad
5 changed files with 65 additions and 6 deletions
+9 -1
View File
@@ -456,6 +456,14 @@ jobs:
const path = require("node:path"); const path = require("node:path");
const [manifestPath, packageDir] = process.argv.slice(2); const [manifestPath, packageDir] = process.argv.slice(2);
const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8")); 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 = [ const entries = [
{ {
packageName: "openclaw", packageName: "openclaw",
@@ -463,7 +471,7 @@ jobs:
tarballName: manifest.tarballName, tarballName: manifest.tarballName,
tarballSha256: manifest.tarballSha256, tarballSha256: manifest.tarballSha256,
}, },
...(Array.isArray(manifest.dependencyTarballs) ? manifest.dependencyTarballs : []), ...dependencyTarballs,
]; ];
const packageNames = new Set(); const packageNames = new Set();
const tarballNames = new Set(); const tarballNames = new Set();
@@ -185,6 +185,15 @@ export function requireRunIdFromDispatchOutput(output: unknown, workflowFile: un
*/ */
export function buildPublishCommand(options: unknown): string; export function buildPublishCommand(options: unknown): string;
export function validatePreflightManifest(manifest: unknown, params: unknown): void; 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 validateFullManifest(manifest: unknown, params: unknown): void;
export function validateTrustedToolingPin({ export function validateTrustedToolingPin({
toolingSha, toolingSha,
+13 -5
View File
@@ -1200,10 +1200,7 @@ export function validatePreflightManifest(manifest, params) {
if (!manifest.tarballName || !manifest.tarballSha256) { if (!manifest.tarballName || !manifest.tarballSha256) {
throw new Error("npm preflight manifest missing tarball metadata"); throw new Error("npm preflight manifest missing tarball metadata");
} }
if (!Array.isArray(manifest.dependencyTarballs)) { for (const dependency of preflightCorePackageTarballs(manifest)) {
throw new Error("npm preflight manifest missing dependency tarball metadata");
}
for (const dependency of manifest.dependencyTarballs) {
if ( if (
!dependency?.packageName || !dependency?.packageName ||
!dependency.packageVersion || !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) { export function validateFullManifest(manifest, params) {
if (manifest.workflowName !== "Full Release Validation") { if (manifest.workflowName !== "Full Release Validation") {
throw new Error(`full validation workflow mismatch: ${manifest.workflowName}`); 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}`, `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); const dependencyPath = join(npmDir, dependency.tarballName);
if (!existsSync(dependencyPath)) { if (!existsSync(dependencyPath)) {
throw new Error(`prepared dependency tarball missing: ${dependencyPath}`); throw new Error(`prepared dependency tarball missing: ${dependencyPath}`);
@@ -3187,6 +3187,8 @@ describe("package artifact reuse", () => {
'candidate_manifest="${package_dir}/package-candidate.json"', 'candidate_manifest="${package_dir}/package-candidate.json"',
'find "${package_dir}" -type f -name "*.tgz"', 'find "${package_dir}" -type f -name "*.tgz"',
"package artifact manifest contains duplicate package metadata", "package artifact manifest contains duplicate package metadata",
"Array.isArray(manifest.corePackageTarballs)",
"manifest.corePackageTarballs === undefined",
"package artifact tarball set does not match preflight manifest", "package artifact tarball set does not match preflight manifest",
"package candidate manifest does not match the OpenClaw tarball", "package candidate manifest does not match the OpenClaw tarball",
"Package Telegram artifact SHA-256 differs from package_sha256.", "Package Telegram artifact SHA-256 differs from package_sha256.",
@@ -14,6 +14,7 @@ import {
isDirectReleaseCandidateExecution, isDirectReleaseCandidateExecution,
parseArgs, parseArgs,
parseRunIdFromDispatchOutput, parseRunIdFromDispatchOutput,
preflightCorePackageTarballs,
reconcileReleaseCandidateState, reconcileReleaseCandidateState,
releaseBranchForTag, releaseBranchForTag,
resolveArtifactName, resolveArtifactName,
@@ -589,6 +590,37 @@ describe("release candidate checklist", () => {
).toThrow("invalid dependency tarball metadata"); ).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", () => { it("trusts the npm workflow SHA while binding the candidate through its manifest", () => {
const workflowSha = "a".repeat(40); const workflowSha = "a".repeat(40);
const isTrustedWorkflowAncestor = vi.fn(() => true); const isTrustedWorkflowAncestor = vi.fn(() => true);