fix(package): clean dist before root packaging (#126577)

This commit is contained in:
Peter Steinberger
2026-08-20 01:13:06 -07:00
committed by GitHub
parent 980b07f4a2
commit ab7fc490d6
7 changed files with 75 additions and 29 deletions
+1 -1
View File
@@ -397,7 +397,7 @@ jobs:
- name: Verify release contents
env:
OPENCLAW_RELEASE_CHECK_LOCAL_PACKAGE_TARBALL_DIR: ${{ steps.core_package_tarballs.outputs.dir }}
run: pnpm release:check
run: pnpm release:generated:check && node --import tsx scripts/release-check.ts
- name: Exercise all extended-stable plugin npm packages
id: plugin_npm_preflight
+2 -1
View File
@@ -1521,6 +1521,7 @@
"build": "node --import tsx scripts/build-all.mts",
"build:ci-artifacts": "node --import tsx scripts/build-all.mts ciArtifacts",
"build:docker": "node --import tsx scripts/tsdown-build.mts && node --import tsx scripts/check-cli-bootstrap-imports.mts && node scripts/runtime-postbuild.mjs && node --import tsx scripts/build-stamp.mts && node --import tsx scripts/runtime-postbuild-stamp.mts && pnpm plugins:assets:build && pnpm plugins:assets:copy && node --import tsx scripts/copy-hook-metadata.ts && node --import tsx scripts/write-build-info.ts && node --import tsx scripts/write-cli-startup-metadata.ts",
"build:package": "pnpm clean:dist && pnpm build",
"build:plugin-sdk:dts": "node scripts/run-tsgo.mjs -p tsconfig.plugin-sdk.dts.json --declaration true",
"build:plugin-sdk:strict-smoke": "node --import tsx scripts/tsdown-build.mts && node scripts/runtime-postbuild.mjs && node --import tsx scripts/run-with-env.mts OPENCLAW_PLUGIN_SDK_CANONICAL_DTS=1 -- node --import tsx scripts/write-plugin-sdk-entry-dts.ts && node --import tsx scripts/check-plugin-sdk-exports.mts",
"build:strict-smoke": "pnpm plugins:assets:build && node --import tsx scripts/tsdown-build.mts && node --import tsx scripts/check-cli-bootstrap-imports.mts && node scripts/runtime-postbuild.mjs && node --import tsx scripts/build-stamp.mts && node --import tsx scripts/runtime-postbuild-stamp.mts && node --import tsx scripts/run-with-env.mts OPENCLAW_PLUGIN_SDK_CANONICAL_DTS=1 -- node --import tsx scripts/write-plugin-sdk-entry-dts.ts && node --import tsx scripts/check-plugin-sdk-exports.mts",
@@ -1766,7 +1767,7 @@
"release:beta": "node --import tsx scripts/release-candidate-checklist.mts",
"release:beta-smoke": "node --import tsx scripts/release-beta-smoke.ts",
"release:candidate": "node --import tsx scripts/release-candidate-checklist.mts",
"release:check": "pnpm release:generated:check && node --import tsx scripts/release-check.ts",
"release:check": "pnpm build:package && pnpm release:generated:check && node --import tsx scripts/release-check.ts",
"release:fast-pretag-check": "bash scripts/release-fast-pretag-check.sh",
"release:generated:check": "node scripts/release-preflight.mjs --check",
"release:openclaw:npm:check": "node --import tsx scripts/openclaw-npm-release-check.ts",
+1 -2
View File
@@ -307,8 +307,7 @@ function prepackPreparationRestoreError(error: unknown, restoreError: unknown):
async function main(): Promise<void> {
ensureSupportedSourcePack();
const buildEnv = resolvePrepackBuildEnvironment();
runPnpm(["build"], buildEnv);
runPnpm(["ui:build"], buildEnv);
runPnpm(["build:package"], buildEnv);
await preparePrepackArtifacts(buildEnv);
}
+13 -22
View File
@@ -11,6 +11,7 @@ import { DOCKER_SELECTED_PLUGIN_BUILD_IDS_ENV } from "./lib/bundled-plugin-build
import { toErrorObject } from "./lib/error-format.mts";
import { terminateManagedChild } from "./lib/managed-child-process.mts";
import { resolveNpmJsonEntries } from "./lib/npm-json-output.mts";
import { assertRealOutputRoot } from "./lib/output-root-guard.mjs";
import { isRecord } from "./lib/record-shared.mjs";
import { resolveNpmRunner } from "./npm-runner.mts";
import { preparePackageChangelog, restorePackageChangelog } from "./package-changelog.mjs";
@@ -473,16 +474,6 @@ function run(command: string, args: string[], cwd: string, options: RunOptions =
});
}
const PACKAGE_ARTIFACT_BUILD_STEPS = [
{
label: "Building OpenClaw package artifacts",
command: "pnpm",
// Let the frozen source own its build entrypoint while the packaging env
// keeps canonical declaration emission enabled.
args: ["run", "build"],
},
];
export async function buildPackageArtifacts(
sourceDir: string,
packageOptions: PackageOptions = {},
@@ -496,18 +487,18 @@ export async function buildPackageArtifacts(
for (const envName of PACKAGE_BUILD_PLUGIN_SELECTION_ENV_NAMES) {
delete buildEnv[envName];
}
for (const step of PACKAGE_ARTIFACT_BUILD_STEPS) {
console.error(`==> ${step.label}`);
await runImpl(step.command, step.args, sourceDir, {
env: {
...buildEnv,
},
timeoutMs: resolveTimeoutMs(
"OPENCLAW_DOCKER_PACKAGE_BUILD_TIMEOUT_MS",
DEFAULT_PACKAGE_BUILD_TIMEOUT_MS,
),
});
}
const timeoutMs = resolveTimeoutMs(
"OPENCLAW_DOCKER_PACKAGE_BUILD_TIMEOUT_MS",
DEFAULT_PACKAGE_BUILD_TIMEOUT_MS,
);
const distDir = path.join(sourceDir, "dist");
assertRealOutputRoot(distDir);
console.error("==> Cleaning OpenClaw package artifacts");
await fs.rm(distDir, { force: true, recursive: true });
// Frozen sources own their build entrypoint and may predate clean:dist.
console.error("==> Building OpenClaw package artifacts");
await runImpl("pnpm", ["run", "build"], sourceDir, { env: buildEnv, timeoutMs });
}
async function runCapture(command: string, args: string[], cwd: string, options: RunOptions = {}) {
@@ -5,6 +5,7 @@ import os from "node:os";
import path from "node:path";
import { pathToFileURL } from "node:url";
import { MAX_TIMER_TIMEOUT_MS } from "@openclaw/normalization-core/number-coercion";
import * as tar from "tar";
import { afterEach, describe, expect, it, vi } from "vitest";
import { DOCKER_SELECTED_PLUGIN_BUILD_IDS_ENV } from "../../../../scripts/lib/bundled-plugin-build-entries.mjs";
import {
@@ -250,6 +251,7 @@ describe("package-openclaw-for-docker", () => {
"scripts/lib/managed-child-process.mts",
"scripts/lib/npm-json-output.mts",
"scripts/lib/optional-bundled-clusters.mjs",
"scripts/lib/output-root-guard.mjs",
"scripts/lib/record-shared.mjs",
"scripts/lib/windows-cmd-helpers-runtime.mts",
"scripts/lib/windows-taskkill.mjs",
@@ -361,6 +363,7 @@ describe("package-openclaw-for-docker", () => {
});
it("uses the source package build entrypoint with declaration generation", async () => {
const sourceDir = tempDirs.make("openclaw-package-build-source-");
const calls: Array<{
command: string;
args: string[];
@@ -387,7 +390,7 @@ describe("package-openclaw-for-docker", () => {
process.env.OPENCLAW_BUILD_PRIVATE_QA = "1";
try {
await buildPackageArtifacts("/repo", {
await buildPackageArtifacts(sourceDir, {
runImpl: async (
command: string,
args: string[],
@@ -437,7 +440,7 @@ describe("package-openclaw-for-docker", () => {
{
command: "pnpm",
args: ["run", "build"],
cwd: "/repo",
cwd: sourceDir,
dockerBuildExtensions: undefined,
internalDockerBuildPluginIds: undefined,
noPnpm: "1",
@@ -449,6 +452,45 @@ describe("package-openclaw-for-docker", () => {
]);
});
it("omits stale hashed dist output when frozen sources expose only their own build", async () => {
const sourceDir = tempDirs.make("openclaw-package-clean-dist-source-");
const outputDir = tempDirs.make("openclaw-package-clean-dist-output-");
const stalePath = path.join(sourceDir, "dist", "runtime-OLDHASH.js");
fs.mkdirSync(path.dirname(stalePath));
fs.writeFileSync(stalePath, "export const stale = true;\n");
fs.writeFileSync(
path.join(sourceDir, "package.json"),
`${JSON.stringify(
{
files: ["dist"],
name: "openclaw",
scripts: {
build:
"node -e \"const fs=require('fs');fs.mkdirSync('dist',{recursive:true});fs.writeFileSync('dist/index.js','export {};\\n')\"",
},
version: "2026.4.25",
},
null,
2,
)}\n`,
);
await buildPackageArtifacts(sourceDir);
const tarball = await packOpenClawPackageForDocker(sourceDir, outputDir, {
...skipDocsMapLifecycle,
prepareChangelog: async () => {},
restoreChangelog: async () => {},
});
const entries: string[] = [];
await tar.t({
file: tarball,
onentry: (entry) => entries.push(entry.path),
});
expect(entries).toContain("package/dist/index.js");
expect(entries).not.toContain("package/dist/runtime-OLDHASH.js");
});
it("rejects loose package artifact timeout env values", async () => {
const previousTimeout = process.env.OPENCLAW_DOCKER_PACKAGE_BUILD_TIMEOUT_MS;
try {
+9
View File
@@ -182,6 +182,15 @@ describe("package scripts", () => {
);
});
it("cleans package builds before validating release contents", () => {
const scripts = readPackageJson().scripts;
expect(scripts["build:package"]).toBe("pnpm clean:dist && pnpm build");
expect(scripts["release:check"]).toBe(
"pnpm build:package && pnpm release:generated:check && node --import tsx scripts/release-check.ts",
);
});
it("uses the shipped package launcher for npm start", () => {
expect(readPackageJson().scripts.start).toBe("node openclaw.mjs");
});
@@ -325,7 +325,11 @@ describe("minimal npm extended-stable workflow", () => {
expect(buildControlUi.if).toBe("steps.dist_build_cache.outputs.cache-hit != 'true'");
expect(buildControlUi.env?.OPENCLAW_CONTROL_UI_RELEASE_BUILD).toBe("1");
expect(step(preflight, "Check").if).toBeUndefined();
expect(step(preflight, "Verify release contents").if).toBeUndefined();
const verifyReleaseContents = step(preflight, "Verify release contents");
expect(verifyReleaseContents.if).toBeUndefined();
expect(verifyReleaseContents.run).toBe(
"pnpm release:generated:check && node --import tsx scripts/release-check.ts",
);
expect(step(preflight, "Verify prepared npm tarball install").if).toBeUndefined();
const save = step(preflight, "Save preflight build outputs");