fix(install): provision corepack pnpm shim for git checkout builds

Workspace build scripts (packages/db, packages/plugins/sdk) invoke bare
`pnpm`. On a clean machine pnpm exists only through corepack, so the
hermetic git-ref build died with 'pnpm: not found' inside
`pnpm -r --filter @paperclipai/server... run build`. Enable a corepack
pnpm shim into the staging dir and prepend it to PATH for every checkout
build command. Found by the PAP-14994 clean-machine Docker e2e.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
DottaandClaude Fable 5 committed 2026-07-31 18:21:11 +00:00
1 parent 5f3f71a5e7
commit 57ec5c0a86
2 files changed
+15 -8

No files matched your search

+1 -1
View File
@@ -178,7 +178,7 @@ describe("managed install commands", () => {
file === "corepack" ||
(file === "npm" && args[0] === "pack") ||
(file === process.execPath && args[0]?.endsWith("prepare-bundled-package.mjs")));
expect(buildCalls).toHaveLength(8);
expect(buildCalls).toHaveLength(9);
for (const call of buildCalls) {
const env = call[2]?.env;
expect(env, `${call[0]} ${call[1].join(" ")} must run with an explicit env`).toBeDefined();
+14 -7
View File
@@ -244,12 +244,19 @@ export async function installGitPayload(repo: string, sha: string, runCommand: C
const stagedPayload = path.join(stagingRoot, "payload");
fs.rmSync(stagingRoot, { recursive: true, force: true });
fs.mkdirSync(checkoutPath, { recursive: true, mode: 0o700 });
// Workspace build scripts invoke bare `pnpm`; on a machine where pnpm exists only
// through corepack, nothing puts it on PATH, so provision a shim into the staging dir.
const pnpmShimDir = path.join(stagingRoot, "pnpm-bin");
fs.mkdirSync(pnpmShimDir, { recursive: true, mode: 0o700 });
const buildEnv = (extra: NodeJS.ProcessEnv = {}) =>
gitBuildEnv({ PATH: [pnpmShimDir, process.env.PATH].filter(Boolean).join(path.delimiter), ...extra });
try {
await runCommand("curl", ["--fail", "--silent", "--show-error", "--location", "--output", archivePath, `https://codeload.github.com/${repo}/tar.gz/${sha}`], { maxBuffer: 4 * 1024 * 1024 });
await runCommand("tar", ["-xzf", archivePath, "--strip-components=1", "-C", checkoutPath], { maxBuffer: 4 * 1024 * 1024 });
await runCommand("corepack", ["pnpm", "install", "--frozen-lockfile"], { cwd: checkoutPath, env: gitBuildEnv(), maxBuffer: 32 * 1024 * 1024 });
await runCommand("bash", ["scripts/build-npm.sh", "--skip-checks", "--skip-typecheck"], { cwd: checkoutPath, env: gitBuildEnv(), maxBuffer: 32 * 1024 * 1024 });
await runCommand("corepack", ["pnpm", "-r", "--filter", "@paperclipai/server...", "--if-present", "run", "build"], { cwd: checkoutPath, env: gitBuildEnv(), maxBuffer: 32 * 1024 * 1024 });
await runCommand("corepack", ["enable", "pnpm", "--install-directory", pnpmShimDir], { cwd: checkoutPath, env: buildEnv(), maxBuffer: 4 * 1024 * 1024 });
await runCommand("corepack", ["pnpm", "install", "--frozen-lockfile"], { cwd: checkoutPath, env: buildEnv(), maxBuffer: 32 * 1024 * 1024 });
await runCommand("bash", ["scripts/build-npm.sh", "--skip-checks", "--skip-typecheck"], { cwd: checkoutPath, env: buildEnv(), maxBuffer: 32 * 1024 * 1024 });
await runCommand("corepack", ["pnpm", "-r", "--filter", "@paperclipai/server...", "--if-present", "run", "build"], { cwd: checkoutPath, env: buildEnv(), maxBuffer: 32 * 1024 * 1024 });
const metadata = JSON.parse(fs.readFileSync(path.join(checkoutPath, "cli", "package.json"), "utf8")) as { version: string };
const workspacePackages = resolveGitInstallWorkspacePackages(checkoutPath);
for (const [index, workspacePackage] of workspacePackages.entries()) {
@@ -258,13 +265,13 @@ export async function installGitPayload(repo: string, sha: string, runCommand: C
const bundledDependencies = packageJson.bundleDependencies ?? packageJson.bundledDependencies ?? [];
if (bundledDependencies.length > 0) {
const stagedPackage = path.join(stagingRoot, `workspace-package-${index}`);
await runCommand(process.execPath, [path.join(checkoutPath, "scripts", "prepare-bundled-package.mjs"), packageDir, stagedPackage], { cwd: checkoutPath, env: gitBuildEnv(), maxBuffer: 32 * 1024 * 1024 });
await runCommand("npm", ["pack", stagedPackage, "--pack-destination", stagingRoot], { cwd: checkoutPath, env: gitBuildEnv(), maxBuffer: 16 * 1024 * 1024 });
await runCommand(process.execPath, [path.join(checkoutPath, "scripts", "prepare-bundled-package.mjs"), packageDir, stagedPackage], { cwd: checkoutPath, env: buildEnv(), maxBuffer: 32 * 1024 * 1024 });
await runCommand("npm", ["pack", stagedPackage, "--pack-destination", stagingRoot], { cwd: checkoutPath, env: buildEnv(), maxBuffer: 16 * 1024 * 1024 });
} else {
await runCommand("corepack", ["pnpm", "--dir", workspacePackage.dir, "pack", "--pack-destination", stagingRoot], { cwd: checkoutPath, env: gitBuildEnv({ PAPERCLIP_RELEASE_REUSE_UI_DIST: "1" }), maxBuffer: 32 * 1024 * 1024 });
await runCommand("corepack", ["pnpm", "--dir", workspacePackage.dir, "pack", "--pack-destination", stagingRoot], { cwd: checkoutPath, env: buildEnv({ PAPERCLIP_RELEASE_REUSE_UI_DIST: "1" }), maxBuffer: 32 * 1024 * 1024 });
}
}
await runCommand("npm", ["pack", "--pack-destination", stagingRoot], { cwd: path.join(checkoutPath, "cli"), env: gitBuildEnv(), maxBuffer: 16 * 1024 * 1024 });
await runCommand("npm", ["pack", "--pack-destination", stagingRoot], { cwd: path.join(checkoutPath, "cli"), env: buildEnv(), maxBuffer: 16 * 1024 * 1024 });
const tarballs = fs.readdirSync(stagingRoot).filter((entry) => entry.endsWith(".tgz"));
const cliTarball = tarballs.find((entry) => entry === `paperclipai-${metadata.version}.tgz`);
const workspaceTarballs = tarballs.filter((entry) => entry !== cliTarball);