From 9c11cb81f31e32150f013f635e5db333a31935b8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 21 Apr 2026 12:01:09 +0000 Subject: [PATCH 1/5] Add bun workspace manager support in workspace-tools Agent-Logs-Url: https://github.com/RafalFilipek/lage/sessions/39cec0ca-a9e9-41f4-8f1b-984c06efbb2d Co-authored-by: RafalFilipek <80960+RafalFilipek@users.noreply.github.com> --- packages/workspace-tools/README.md | 3 ++- .../workspace-tools/etc/workspace-tools.api.md | 2 +- .../__fixtures__/monorepo-basic-bun/README.md | 5 +++++ .../__fixtures__/monorepo-basic-bun/bun.lock | 0 .../monorepo-basic-bun/individual/package.json | 5 +++++ .../monorepo-basic-bun/package.json | 17 +++++++++++++++++ .../packages/package-a/package.json | 9 +++++++++ .../packages/package-b/package.json | 5 +++++ .../src/__tests__/setupFixture.ts | 1 + .../getWorkspaceManagerAndRoot.test.ts | 1 + .../__tests__/workspaces/getWorkspaces.test.ts | 1 + .../src/types/WorkspaceManager.ts | 2 +- .../src/workspaces/implementations/bun.ts | 9 +++++++++ .../getWorkspaceManagerAndRoot.ts | 12 ++++++++++-- .../implementations/getWorkspaceUtilities.ts | 5 +++++ 15 files changed, 72 insertions(+), 5 deletions(-) create mode 100644 packages/workspace-tools/src/__fixtures__/monorepo-basic-bun/README.md create mode 100644 packages/workspace-tools/src/__fixtures__/monorepo-basic-bun/bun.lock create mode 100644 packages/workspace-tools/src/__fixtures__/monorepo-basic-bun/individual/package.json create mode 100644 packages/workspace-tools/src/__fixtures__/monorepo-basic-bun/package.json create mode 100644 packages/workspace-tools/src/__fixtures__/monorepo-basic-bun/packages/package-a/package.json create mode 100644 packages/workspace-tools/src/__fixtures__/monorepo-basic-bun/packages/package-b/package.json create mode 100644 packages/workspace-tools/src/workspaces/implementations/bun.ts diff --git a/packages/workspace-tools/README.md b/packages/workspace-tools/README.md index 55d215aef..f9eb6da4b 100644 --- a/packages/workspace-tools/README.md +++ b/packages/workspace-tools/README.md @@ -3,6 +3,7 @@ A collection of utilities that are useful in a git-controlled monorepo managed by one of these tools: - lerna +- bun workspaces - npm workspaces - pnpm workspaces - rush @@ -20,7 +21,7 @@ Override the `maxBuffer` value for git processes, for example if the repo is ver ### PREFERRED_WORKSPACE_MANAGER -Sometimes if multiple workspace/monorepo manager files are checked in, it's necessary to hint which manager is used: `npm`, `yarn`, `pnpm`, `rush`, or `lerna`. Some APIs also accept a `manager` parameter, which is now the preferred method when available. +Sometimes if multiple workspace/monorepo manager files are checked in, it's necessary to hint which manager is used: `bun`, `npm`, `yarn`, `pnpm`, `rush`, or `lerna`. Some APIs also accept a `manager` parameter, which is now the preferred method when available. ### VERBOSE diff --git a/packages/workspace-tools/etc/workspace-tools.api.md b/packages/workspace-tools/etc/workspace-tools.api.md index 9a598ce21..b70b3259b 100644 --- a/packages/workspace-tools/etc/workspace-tools.api.md +++ b/packages/workspace-tools/etc/workspace-tools.api.md @@ -668,7 +668,7 @@ export function stageAndCommit(patterns: string[], message: string, cwd: string, export type WorkspaceInfos = WorkspacePackageInfo[]; // @public (undocumented) -export type WorkspaceManager = "yarn" | "pnpm" | "rush" | "npm" | "lerna"; +export type WorkspaceManager = "yarn" | "pnpm" | "rush" | "npm" | "lerna" | "bun"; // @public (undocumented) interface WorkspaceManagerAndRoot { diff --git a/packages/workspace-tools/src/__fixtures__/monorepo-basic-bun/README.md b/packages/workspace-tools/src/__fixtures__/monorepo-basic-bun/README.md new file mode 100644 index 000000000..3e2259fa6 --- /dev/null +++ b/packages/workspace-tools/src/__fixtures__/monorepo-basic-bun/README.md @@ -0,0 +1,5 @@ +This fixture is intended to match the other `monorepo-basic-*` fixtures (bun): + +- Workspaces: `["individual", "packages/*"]` +- Same basic dependencies at root +- `package-a` depends on `react` and `react-dom` (to introduce a `peerDependency`) diff --git a/packages/workspace-tools/src/__fixtures__/monorepo-basic-bun/bun.lock b/packages/workspace-tools/src/__fixtures__/monorepo-basic-bun/bun.lock new file mode 100644 index 000000000..e69de29bb diff --git a/packages/workspace-tools/src/__fixtures__/monorepo-basic-bun/individual/package.json b/packages/workspace-tools/src/__fixtures__/monorepo-basic-bun/individual/package.json new file mode 100644 index 000000000..ec1362d53 --- /dev/null +++ b/packages/workspace-tools/src/__fixtures__/monorepo-basic-bun/individual/package.json @@ -0,0 +1,5 @@ +{ + "name": "individual", + "license": "MIT", + "version": "0.1.0" +} diff --git a/packages/workspace-tools/src/__fixtures__/monorepo-basic-bun/package.json b/packages/workspace-tools/src/__fixtures__/monorepo-basic-bun/package.json new file mode 100644 index 000000000..8f9352fc9 --- /dev/null +++ b/packages/workspace-tools/src/__fixtures__/monorepo-basic-bun/package.json @@ -0,0 +1,17 @@ +{ + "name": "monorepo-basic-bun", + "description": "Basic monorepo with bun (workspaces and deps should match other monorepo-basic-*)", + "license": "MIT", + "version": "0.1.0", + "private": true, + "devDependencies": { + "@types/node": "^20.0.0", + "typescript": "^4.0.0" + }, + "workspaces": { + "packages": [ + "packages/*", + "individual" + ] + } +} diff --git a/packages/workspace-tools/src/__fixtures__/monorepo-basic-bun/packages/package-a/package.json b/packages/workspace-tools/src/__fixtures__/monorepo-basic-bun/packages/package-a/package.json new file mode 100644 index 000000000..1c3fb64f0 --- /dev/null +++ b/packages/workspace-tools/src/__fixtures__/monorepo-basic-bun/packages/package-a/package.json @@ -0,0 +1,9 @@ +{ + "name": "package-a", + "license": "MIT", + "version": "0.1.0", + "dependencies": { + "react": "^19.0.0", + "react-dom": "^19.0.0" + } +} diff --git a/packages/workspace-tools/src/__fixtures__/monorepo-basic-bun/packages/package-b/package.json b/packages/workspace-tools/src/__fixtures__/monorepo-basic-bun/packages/package-b/package.json new file mode 100644 index 000000000..8b6623a6e --- /dev/null +++ b/packages/workspace-tools/src/__fixtures__/monorepo-basic-bun/packages/package-b/package.json @@ -0,0 +1,5 @@ +{ + "name": "package-b", + "license": "MIT", + "version": "0.1.0" +} diff --git a/packages/workspace-tools/src/__tests__/setupFixture.ts b/packages/workspace-tools/src/__tests__/setupFixture.ts index d38cca7c8..23d220403 100644 --- a/packages/workspace-tools/src/__tests__/setupFixture.ts +++ b/packages/workspace-tools/src/__tests__/setupFixture.ts @@ -16,6 +16,7 @@ type RealFixtureName = | "extra-yarn-1" | "extra-yarn-berry" | "monorepo-basic-npm" + | "monorepo-basic-bun" | "monorepo-basic-pnpm" | "monorepo-basic-yarn-1" | "monorepo-basic-yarn-berry" diff --git a/packages/workspace-tools/src/__tests__/workspaces/getWorkspaceManagerAndRoot.test.ts b/packages/workspace-tools/src/__tests__/workspaces/getWorkspaceManagerAndRoot.test.ts index d76af6ffd..e7c52233a 100644 --- a/packages/workspace-tools/src/__tests__/workspaces/getWorkspaceManagerAndRoot.test.ts +++ b/packages/workspace-tools/src/__tests__/workspaces/getWorkspaceManagerAndRoot.test.ts @@ -21,6 +21,7 @@ describe("getWorkspaceManagerAndRoot", () => { { desc: "yarn", manager: "yarn", fixtureName: "monorepo-basic-yarn-1" }, { desc: "yarn berry", manager: "yarn", fixtureName: "monorepo-basic-yarn-berry" }, { desc: "pnpm", manager: "pnpm", fixtureName: "monorepo-basic-pnpm" }, + { desc: "bun", manager: "bun", fixtureName: "monorepo-basic-bun" }, { desc: "rush", manager: "rush", fixtureName: "monorepo-rush-pnpm" }, { desc: "npm", manager: "npm", fixtureName: "monorepo-basic-npm" }, { desc: "lerna + npm", manager: "lerna", fixtureName: "monorepo-basic-lerna-npm" }, diff --git a/packages/workspace-tools/src/__tests__/workspaces/getWorkspaces.test.ts b/packages/workspace-tools/src/__tests__/workspaces/getWorkspaces.test.ts index a93a37aae..e6ada4ba5 100644 --- a/packages/workspace-tools/src/__tests__/workspaces/getWorkspaces.test.ts +++ b/packages/workspace-tools/src/__tests__/workspaces/getWorkspaces.test.ts @@ -17,6 +17,7 @@ describe("getWorkspaceInfos", () => { }>([ { manager: "yarn", desc: "yarn", fixtureName: "monorepo-basic-yarn-1" }, { manager: "pnpm", desc: "pnpm", fixtureName: "monorepo-basic-pnpm" }, + { manager: "bun", desc: "bun", fixtureName: "monorepo-basic-bun" }, { manager: "rush", desc: "rush + pnpm", fixtureName: "monorepo-rush-pnpm" }, { manager: "rush", desc: "rush + yarn", fixtureName: "monorepo-rush-yarn" }, { manager: "npm", desc: "npm", fixtureName: "monorepo-basic-npm" }, diff --git a/packages/workspace-tools/src/types/WorkspaceManager.ts b/packages/workspace-tools/src/types/WorkspaceManager.ts index ee41f0f17..fa299243a 100644 --- a/packages/workspace-tools/src/types/WorkspaceManager.ts +++ b/packages/workspace-tools/src/types/WorkspaceManager.ts @@ -1 +1 @@ -export type WorkspaceManager = "yarn" | "pnpm" | "rush" | "npm" | "lerna"; +export type WorkspaceManager = "yarn" | "pnpm" | "rush" | "npm" | "lerna" | "bun"; diff --git a/packages/workspace-tools/src/workspaces/implementations/bun.ts b/packages/workspace-tools/src/workspaces/implementations/bun.ts new file mode 100644 index 000000000..216c6aa42 --- /dev/null +++ b/packages/workspace-tools/src/workspaces/implementations/bun.ts @@ -0,0 +1,9 @@ +import { getPackageJsonWorkspacePatterns } from "./getPackageJsonWorkspacePatterns.js"; +import type { WorkspaceUtilities } from "./WorkspaceUtilities.js"; + +/** + * Bun uses the standard package.json "workspaces" field. + */ +export const bunUtilities: WorkspaceUtilities = { + getWorkspacePatterns: getPackageJsonWorkspacePatterns, +}; diff --git a/packages/workspace-tools/src/workspaces/implementations/getWorkspaceManagerAndRoot.ts b/packages/workspace-tools/src/workspaces/implementations/getWorkspaceManagerAndRoot.ts index bae8ae458..a0b82607c 100644 --- a/packages/workspace-tools/src/workspaces/implementations/getWorkspaceManagerAndRoot.ts +++ b/packages/workspace-tools/src/workspaces/implementations/getWorkspaceManagerAndRoot.ts @@ -24,9 +24,15 @@ export const managerFiles = { rush: "rush.json", yarn: "yarn.lock", pnpm: "pnpm-workspace.yaml", + bun: ["bun.lock", "bun.lockb"], npm: "package-lock.json", } as const; +function getManagerFileNames(manager: WorkspaceManager): readonly string[] { + const file = managerFiles[manager]; + return Array.isArray(file) ? file : [file]; +} + /** * Get the preferred workspace/monorepo manager based on `process.env.PREFERRED_WORKSPACE_MANAGER` * (if valid). @@ -57,7 +63,9 @@ export function getWorkspaceManagerAndRoot( } managerOverride ??= getPreferredWorkspaceManager(); - const filesToSearch = managerOverride ? managerFiles[managerOverride] : Object.values(managerFiles); + const filesToSearch = managerOverride + ? getManagerFileNames(managerOverride) + : Object.values(managerFiles).flatMap((files) => (Array.isArray(files) ? files : [files])); const managerFile = searchUp(filesToSearch, cwd); if (managerFile) { @@ -65,7 +73,7 @@ export function getWorkspaceManagerAndRoot( cache.set(cwd, { manager: managerOverride || - (Object.keys(managerFiles) as WorkspaceManager[]).find((name) => managerFiles[name] === managerFileName)!, + (Object.keys(managerFiles) as WorkspaceManager[]).find((name) => getManagerFileNames(name).includes(managerFileName))!, root: path.dirname(managerFile), }); } else { diff --git a/packages/workspace-tools/src/workspaces/implementations/getWorkspaceUtilities.ts b/packages/workspace-tools/src/workspaces/implementations/getWorkspaceUtilities.ts index bfa6138b9..61815d081 100644 --- a/packages/workspace-tools/src/workspaces/implementations/getWorkspaceUtilities.ts +++ b/packages/workspace-tools/src/workspaces/implementations/getWorkspaceUtilities.ts @@ -8,6 +8,11 @@ const utils: Partial> = {}; */ export function getWorkspaceUtilities(manager: WorkspaceManager): WorkspaceUtilities { switch (manager) { + case "bun": + // eslint-disable-next-line @typescript-eslint/consistent-type-imports, @typescript-eslint/no-require-imports + utils.bun ??= (require("./bun") as typeof import("./bun")).bunUtilities; + break; + case "npm": // eslint-disable-next-line @typescript-eslint/consistent-type-imports, @typescript-eslint/no-require-imports utils.npm ??= (require("./npm") as typeof import("./npm")).npmUtilities; From 3db9f0f557913681c72e702659f8752a2d228281 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 21 Apr 2026 12:05:54 +0000 Subject: [PATCH 2/5] Fix bun manager file detection typing Agent-Logs-Url: https://github.com/RafalFilipek/lage/sessions/39cec0ca-a9e9-41f4-8f1b-984c06efbb2d Co-authored-by: RafalFilipek <80960+RafalFilipek@users.noreply.github.com> --- .../implementations/getWorkspaceManagerAndRoot.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/workspace-tools/src/workspaces/implementations/getWorkspaceManagerAndRoot.ts b/packages/workspace-tools/src/workspaces/implementations/getWorkspaceManagerAndRoot.ts index a0b82607c..5c0731b8e 100644 --- a/packages/workspace-tools/src/workspaces/implementations/getWorkspaceManagerAndRoot.ts +++ b/packages/workspace-tools/src/workspaces/implementations/getWorkspaceManagerAndRoot.ts @@ -28,9 +28,9 @@ export const managerFiles = { npm: "package-lock.json", } as const; -function getManagerFileNames(manager: WorkspaceManager): readonly string[] { - const file = managerFiles[manager]; - return Array.isArray(file) ? file : [file]; +function getManagerFileNames(manager: WorkspaceManager): string[] { + const fileOrFiles = managerFiles[manager]; + return typeof fileOrFiles === "string" ? [fileOrFiles] : [...fileOrFiles]; } /** From 4874f9bb4f584b0cbf69efe16fab5c85ce118ead Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 21 Apr 2026 12:06:32 +0000 Subject: [PATCH 3/5] Add beachball change file for workspace-tools bun support Agent-Logs-Url: https://github.com/RafalFilipek/lage/sessions/39cec0ca-a9e9-41f4-8f1b-984c06efbb2d Co-authored-by: RafalFilipek <80960+RafalFilipek@users.noreply.github.com> --- change/change-bun-workspace-tools.json | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 change/change-bun-workspace-tools.json diff --git a/change/change-bun-workspace-tools.json b/change/change-bun-workspace-tools.json new file mode 100644 index 000000000..7a3941168 --- /dev/null +++ b/change/change-bun-workspace-tools.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "type": "patch", + "comment": "Add bun as a supported workspace manager in workspace-tools, including manager detection and workspace fixture/test coverage.", + "packageName": "workspace-tools", + "email": "email not defined", + "dependentChangeType": "patch" + } + ] +} From 65c4bf015c77a232fd4f632b86c81b4b855f5c06 Mon Sep 17 00:00:00 2001 From: Rafau Date: Tue, 21 Apr 2026 14:23:31 +0200 Subject: [PATCH 4/5] Update packages/workspace-tools/src/__fixtures__/monorepo-basic-bun/README.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../src/__fixtures__/monorepo-basic-bun/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/workspace-tools/src/__fixtures__/monorepo-basic-bun/README.md b/packages/workspace-tools/src/__fixtures__/monorepo-basic-bun/README.md index 3e2259fa6..7a1804142 100644 --- a/packages/workspace-tools/src/__fixtures__/monorepo-basic-bun/README.md +++ b/packages/workspace-tools/src/__fixtures__/monorepo-basic-bun/README.md @@ -1,5 +1,5 @@ This fixture is intended to match the other `monorepo-basic-*` fixtures (bun): -- Workspaces: `["individual", "packages/*"]` +- Workspaces: `["packages/*", "individual"]` - Same basic dependencies at root - `package-a` depends on `react` and `react-dom` (to introduce a `peerDependency`) From ea7665a82b0b5371cd646b1377f28b25839783a9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 21 Apr 2026 12:25:24 +0000 Subject: [PATCH 5/5] Add coverage for bun.lockb manager detection Agent-Logs-Url: https://github.com/RafalFilipek/lage/sessions/b81e1d79-1a38-41b4-bc3b-d9b148a333cf Co-authored-by: RafalFilipek <80960+RafalFilipek@users.noreply.github.com> --- .../workspaces/getWorkspaceManagerAndRoot.test.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/packages/workspace-tools/src/__tests__/workspaces/getWorkspaceManagerAndRoot.test.ts b/packages/workspace-tools/src/__tests__/workspaces/getWorkspaceManagerAndRoot.test.ts index e7c52233a..7ac7192af 100644 --- a/packages/workspace-tools/src/__tests__/workspaces/getWorkspaceManagerAndRoot.test.ts +++ b/packages/workspace-tools/src/__tests__/workspaces/getWorkspaceManagerAndRoot.test.ts @@ -45,6 +45,17 @@ describe("getWorkspaceManagerAndRoot", () => { }); }); + it("detects bun workspace root with bun.lockb", () => { + const repoRoot = setupFixture("monorepo-basic-bun"); + fs.rmSync(path.join(repoRoot, "bun.lock")); + fs.writeFileSync(path.join(repoRoot, "bun.lockb"), ""); + + expect(getWorkspaceManagerAndRoot(repoRoot)).toEqual({ + root: repoRoot, + manager: "bun", + }); + }); + it("handles nested monorepo", () => { // This fixture has a monorepo under the "monorepo" folder, not at the git root. const repoRoot = setupFixture("monorepo-nested", { git: true });