mirror of
https://github.com/nexu-io/open-design.git
synced 2026-10-27 09:51:12 +01:00
fix(tools-pack): replace corepack with npx in linux container build (#558)
This commit is contained in:
+16
-8
@@ -96,14 +96,22 @@ export function buildDockerArgs(
|
||||
// None of these values can contain shell metacharacters, so direct
|
||||
// interpolation into the inner command string is safe.
|
||||
//
|
||||
// We invoke pnpm via `corepack pnpm` instead of `corepack enable && pnpm ...`
|
||||
// because the container runs as the host's non-root uid (--user above) and
|
||||
// `corepack enable` writes shims next to the system Node binary, which is
|
||||
// owned by root in electronuserland/builder:base and not writable by the
|
||||
// unprivileged container user. `corepack pnpm` resolves and executes the
|
||||
// pinned packageManager from package.json directly, no shims required.
|
||||
// We can't rely on `corepack pnpm` here: although Node 16.10+ ships corepack,
|
||||
// the `electronuserland/builder:base` image strips the corepack binary, so
|
||||
// the inner `bash -lc` fails with `corepack: command not found`. We also
|
||||
// can't `corepack enable` ourselves — the container runs as the host's
|
||||
// non-root uid (--user above) and corepack would try to write shims next
|
||||
// to the system Node binary, which is owned by root in this image.
|
||||
//
|
||||
// Use `npx --yes pnpm@<version>` instead: `npx` ships with npm (always
|
||||
// present in the image), `--yes` skips the install confirmation, and the
|
||||
// package gets cached under `$HOME/.npm/_npx`, which is writable by the
|
||||
// unprivileged user. The pinned version matches the `packageManager`
|
||||
// field in the root package.json so reproducibility is preserved.
|
||||
const PNPM_VERSION = "10.33.2";
|
||||
const pnpmCmd = `npx --yes pnpm@${PNPM_VERSION}`;
|
||||
const innerArgs = [
|
||||
"corepack pnpm tools-pack linux build",
|
||||
`${pnpmCmd} tools-pack linux build`,
|
||||
`--to ${config.to}`,
|
||||
`--namespace ${config.namespace}`,
|
||||
"--dir /tools-pack",
|
||||
@@ -111,7 +119,7 @@ export function buildDockerArgs(
|
||||
if (config.portable) {
|
||||
innerArgs.push("--portable");
|
||||
}
|
||||
const innerCommand = "corepack pnpm install --frozen-lockfile && " + innerArgs.join(" ");
|
||||
const innerCommand = `${pnpmCmd} install --frozen-lockfile && ` + innerArgs.join(" ");
|
||||
|
||||
return [
|
||||
"run",
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
import { readFileSync } from "node:fs";
|
||||
import { dirname, join } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import type { ToolPackConfig } from "../src/config.js";
|
||||
@@ -83,16 +87,33 @@ describe("buildDockerArgs", () => {
|
||||
it("re-invokes pnpm tools-pack linux build inside the container without --containerized", () => {
|
||||
const args = buildDockerArgs(makeConfig(), { uid: 1000, gid: 1000 });
|
||||
const last = args[args.length - 1];
|
||||
expect(last).toMatch(/corepack pnpm install --frozen-lockfile/);
|
||||
expect(last).toMatch(/corepack pnpm tools-pack linux build --to all --namespace default/);
|
||||
expect(last).toMatch(/npx --yes pnpm@\d+\.\d+\.\d+ install --frozen-lockfile/);
|
||||
expect(last).toMatch(/npx --yes pnpm@\d+\.\d+\.\d+ tools-pack linux build --to all --namespace default/);
|
||||
expect(last).not.toMatch(/--containerized/);
|
||||
});
|
||||
|
||||
it("invokes pnpm via `corepack pnpm` rather than `corepack enable` (non-root container can't write Node shim dir)", () => {
|
||||
it("invokes pnpm via `npx --yes pnpm@<version>` (electronuserland/builder:base strips corepack, and the non-root container can't write Node shim dir)", () => {
|
||||
const args = buildDockerArgs(makeConfig(), { uid: 1000, gid: 1000 });
|
||||
const last = args[args.length - 1];
|
||||
expect(last).not.toMatch(/corepack enable/);
|
||||
expect(last).toMatch(/corepack pnpm/);
|
||||
expect(last).not.toMatch(/corepack/);
|
||||
expect(last).toMatch(/npx --yes pnpm@/);
|
||||
});
|
||||
|
||||
it("hardcoded pnpm version stays in lockstep with root package.json `packageManager`", () => {
|
||||
// Guard against silent drift: if someone bumps packageManager in the
|
||||
// root package.json but forgets to update PNPM_VERSION in linux.ts,
|
||||
// the Linux container build would silently keep using the old pnpm.
|
||||
const repoRoot = join(dirname(fileURLToPath(import.meta.url)), "..", "..", "..");
|
||||
const rootPkg = JSON.parse(readFileSync(join(repoRoot, "package.json"), "utf-8")) as {
|
||||
packageManager?: string;
|
||||
};
|
||||
const match = String(rootPkg.packageManager ?? "").match(/^pnpm@(\d+\.\d+\.\d+)$/);
|
||||
expect(match, `expected root packageManager "pnpm@x.y.z", got ${rootPkg.packageManager}`).not.toBeNull();
|
||||
const expectedVersion = match![1];
|
||||
|
||||
const args = buildDockerArgs(makeConfig(), { uid: 1000, gid: 1000 });
|
||||
const last = args[args.length - 1];
|
||||
expect(last).toContain(`npx --yes pnpm@${expectedVersion}`);
|
||||
});
|
||||
|
||||
it("forwards --dir /tools-pack so inner build output lands under the mounted host dir", () => {
|
||||
|
||||
Reference in New Issue
Block a user