From 6fd68c66896007af866ca71fb334ddf43ff23330 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Fri, 16 Jan 2026 10:03:38 +0000 Subject: [PATCH] fix: enforce pnpm-only UI runner (#1002) (thanks @tosh-hamburg) --- CHANGELOG.md | 1 + Dockerfile | 6 ---- README.md | 2 +- scripts/ui.js | 52 ++++++++--------------------------- src/scripts/ui-script.test.ts | 38 +++++++++++++++++++++++++ 5 files changed, 52 insertions(+), 47 deletions(-) create mode 100644 src/scripts/ui-script.test.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index d978f8bdb1..fc2eebb3a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -49,6 +49,7 @@ - Fix: list model picker entries as provider/model pairs for explicit selection. (#970) — thanks @mcinteerj. - Fix: align OpenAI image-gen defaults with DALL-E 3 standard quality and document output formats. (#880) — thanks @mkbehr. - Fix: persist `gateway.mode=local` after selecting Local run mode in `clawdbot configure`, even if no other sections are chosen. +- Fix: run UI install/build with pnpm only (Docker avoids bun failures). (#1002) — thanks @tosh-hamburg. - Daemon: fix profile-aware service label resolution (env-driven) and add coverage for launchd/systemd/schtasks. (#969) — thanks @bjesuiter. - Agents: avoid false positives when logging unsupported Google tool schema keywords. - Agents: skip Gemini history downgrades for google-antigravity to preserve tool calls. (#894) — thanks @mukhtharcm. diff --git a/Dockerfile b/Dockerfile index a33f0077de..03ff4db5d7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,9 +1,5 @@ FROM node:22-bookworm -# Install Bun (required for build scripts) -RUN curl -fsSL https://bun.sh/install | bash -ENV PATH="/root/.bun/bin:${PATH}" - RUN corepack enable WORKDIR /app @@ -25,8 +21,6 @@ RUN pnpm install --frozen-lockfile COPY . . RUN pnpm build -# Force pnpm for UI build (Bun may fail on ARM/Synology architectures) -ENV CLAWDBOT_PREFER_PNPM=1 RUN pnpm ui:install RUN pnpm ui:build diff --git a/README.md b/README.md index c288d68583..29556eae75 100644 --- a/README.md +++ b/README.md @@ -492,5 +492,5 @@ Thanks to all clawtributors: MSch Mustafa Tag Eldeen ndraiman nexty5870 prathamdby reeltimeapps RLTCmpe Rolf Fredheim Rony Kelner Samrat Jha siraht snopoke The Admiral voidserf wes-davis wstock YuriNachos Zach Knickerbocker Alphonse-arianee Azade carlulsoe cpojer ddyo Erik latitudeki5223 longmaba Manuel Maly Mourad Boustani pcty-nextgen-ios-builder Quentin - Randy Torres ronak-guliani thesash William Stock + Randy Torres ronak-guliani thesash William Stock tosh-hamburg

diff --git a/scripts/ui.js b/scripts/ui.js index fc7bc2ce81..1447f0e18c 100644 --- a/scripts/ui.js +++ b/scripts/ui.js @@ -45,19 +45,8 @@ function which(cmd) { } function resolveRunner() { - // CLAWDBOT_PREFER_PNPM=1 forces pnpm (useful in Docker on architectures where Bun fails) - const preferPnpm = process.env.CLAWDBOT_PREFER_PNPM === "1"; - if (!preferPnpm) { - const bun = which("bun"); - if (bun) return { cmd: bun, kind: "bun" }; - } const pnpm = which("pnpm"); - if (pnpm) return { cmd: pnpm, kind: "pnpm" }; - if (preferPnpm) { - // Fallback to bun if pnpm not found even when preferring pnpm - const bun = which("bun"); - if (bun) return { cmd: bun, kind: "bun" }; - } + if (pnpm) return pnpm; return null; } @@ -108,7 +97,7 @@ if (!action) { const runner = resolveRunner(); if (!runner) { process.stderr.write( - "Missing UI runner: install bun or pnpm, then retry.\n", + "Missing UI runner: install pnpm, then retry.\n", ); process.exit(1); } @@ -129,32 +118,15 @@ if (action !== "install" && !script) { process.exit(2); } -if (runner.kind === "bun") { - if (action === "install") run(runner.cmd, ["install", ...rest]); - else { - if (!depsInstalled(action === "test" ? "test" : "build")) { - const installEnv = - action === "build" - ? { ...process.env, NODE_ENV: "production" } - : process.env; - const installArgs = - action === "build" ? ["install", "--production"] : ["install"]; - runSync(runner.cmd, installArgs, installEnv); - } - run(runner.cmd, ["run", script, ...rest]); - } -} else { - if (action === "install") run(runner.cmd, ["install", ...rest]); - else { - if (!depsInstalled(action === "test" ? "test" : "build")) { - const installEnv = - action === "build" - ? { ...process.env, NODE_ENV: "production" } - : process.env; - const installArgs = - action === "build" ? ["install", "--prod"] : ["install"]; - runSync(runner.cmd, installArgs, installEnv); - } - run(runner.cmd, ["run", script, ...rest]); +if (action === "install") run(runner, ["install", ...rest]); +else { + if (!depsInstalled(action === "test" ? "test" : "build")) { + const installEnv = + action === "build" + ? { ...process.env, NODE_ENV: "production" } + : process.env; + const installArgs = action === "build" ? ["install", "--prod"] : ["install"]; + runSync(runner, installArgs, installEnv); } + run(runner, ["run", script, ...rest]); } diff --git a/src/scripts/ui-script.test.ts b/src/scripts/ui-script.test.ts new file mode 100644 index 0000000000..f9162c16ae --- /dev/null +++ b/src/scripts/ui-script.test.ts @@ -0,0 +1,38 @@ +import { spawnSync } from "node:child_process"; +import fs from "node:fs"; +import os from "node:os"; +import path from "node:path"; +import { describe, expect, it } from "vitest"; + +function runUi(args: string[], env: NodeJS.ProcessEnv) { + return spawnSync(process.execPath, ["scripts/ui.js", ...args], { + env: { ...process.env, ...env }, + encoding: "utf8", + }); +} + +function writeStubPnpm(dir: string) { + const isWin = process.platform === "win32"; + const name = isWin ? "pnpm.cmd" : "pnpm"; + const target = path.join(dir, name); + const contents = isWin ? "@echo off\r\nexit /B 0\r\n" : "#!/bin/sh\nexit 0\n"; + fs.writeFileSync(target, contents); + if (!isWin) fs.chmodSync(target, 0o755); + return target; +} + +describe("scripts/ui.js", () => { + it("fails with a pnpm-only error when pnpm is missing", () => { + const result = runUi(["install"], { PATH: "" }); + expect(result.status).toBe(1); + expect(result.stderr).toContain("install pnpm"); + expect(result.stderr.toLowerCase()).not.toContain("bun"); + }); + + it("runs pnpm when available", () => { + const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "clawdbot-ui-")); + writeStubPnpm(tmp); + const result = runUi(["install"], { PATH: tmp }); + expect(result.status).toBe(0); + }); +});