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

View File

@@ -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();

View File

@@ -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,

View File

@@ -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}`);

View File

@@ -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.",

View File

@@ -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);