mirror of
https://github.com/oven-sh/bun
synced 2026-02-09 18:38:55 +00:00
## Summary This PR implements the `--workspaces` flag for the `bun run` command, allowing scripts to be run in all workspace packages as defined in the `"workspaces"` field in package.json. Fixes the infinite loop issue reported in https://github.com/threepointone/bun-workspace-bug-repro ## Changes - Added `--workspaces` flag to run scripts in all workspace packages - Added `--if-present` flag to gracefully skip packages without the script - Root package is excluded when using `--workspaces` to prevent infinite recursion - Added comprehensive tests for the new functionality ## Usage ```bash # Run "test" script in all workspace packages bun run --workspaces test # Skip packages that don't have the script bun run --workspaces --if-present build # Combine with filters bun run --filter="@scope/*" test ``` ## Behavior The `--workspaces` flag must come **before** the script name (matching npm's behavior): - ✅ `bun run --workspaces test` - ❌ `bun run test --workspaces` (treated as passthrough to script) ## Test Plan - [x] Added test cases in `test/cli/run/workspaces.test.ts` - [x] Verified fix for infinite loop issue in https://github.com/threepointone/bun-workspace-bug-repro - [x] Tested with `--if-present` flag - [x] All tests pass locally 🤖 Generated with [Claude Code](https://claude.ai/code) --------- Co-authored-by: Claude Bot <claude-bot@bun.sh> Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Dylan Conway <dylan.conway567@gmail.com> Co-authored-by: Jarred Sumner <jarred@jarredsumner.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
104 lines
2.7 KiB
TypeScript
104 lines
2.7 KiB
TypeScript
import { expect, test } from "bun:test";
|
|
import { bunEnv, bunExe, tempDirWithFiles } from "harness";
|
|
|
|
test("bun run --workspaces runs script in all workspace packages", async () => {
|
|
const dir = tempDirWithFiles("workspaces-test", {
|
|
"package.json": JSON.stringify({
|
|
name: "root",
|
|
workspaces: ["packages/*"],
|
|
scripts: {
|
|
test: "echo root test",
|
|
},
|
|
}),
|
|
"packages/a/package.json": JSON.stringify({
|
|
name: "a",
|
|
scripts: {
|
|
test: "echo package a test",
|
|
},
|
|
}),
|
|
"packages/b/package.json": JSON.stringify({
|
|
name: "b",
|
|
scripts: {
|
|
test: "echo package b test",
|
|
},
|
|
}),
|
|
});
|
|
|
|
const proc = Bun.spawn({
|
|
cmd: [bunExe(), "run", "--workspaces", "test"],
|
|
env: bunEnv,
|
|
cwd: dir,
|
|
stdout: "pipe",
|
|
stderr: "pipe",
|
|
});
|
|
|
|
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
|
|
|
|
expect(exitCode).toBe(0);
|
|
expect(stdout).toContain("package a test");
|
|
expect(stdout).toContain("package b test");
|
|
// Root should not be included when using --workspaces
|
|
expect(stdout).not.toContain("root test");
|
|
});
|
|
|
|
test("bun run --workspaces --if-present succeeds when script is missing", async () => {
|
|
const dir = tempDirWithFiles("workspaces-if-present", {
|
|
"package.json": JSON.stringify({
|
|
name: "root",
|
|
workspaces: ["packages/*"],
|
|
}),
|
|
"packages/a/package.json": JSON.stringify({
|
|
name: "a",
|
|
scripts: {
|
|
test: "echo package a test",
|
|
},
|
|
}),
|
|
"packages/b/package.json": JSON.stringify({
|
|
name: "b",
|
|
// No test script
|
|
}),
|
|
});
|
|
|
|
const proc = Bun.spawn({
|
|
cmd: [bunExe(), "run", "--workspaces", "--if-present", "test"],
|
|
env: bunEnv,
|
|
cwd: dir,
|
|
stdout: "pipe",
|
|
stderr: "pipe",
|
|
});
|
|
|
|
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
|
|
|
|
expect(exitCode).toBe(0);
|
|
expect(stdout).toContain("package a test");
|
|
// Should not fail for package b
|
|
});
|
|
|
|
test("bun run --workspaces fails when no packages have the script", async () => {
|
|
const dir = tempDirWithFiles("workspaces-no-script", {
|
|
"package.json": JSON.stringify({
|
|
name: "root",
|
|
workspaces: ["packages/*"],
|
|
}),
|
|
"packages/a/package.json": JSON.stringify({
|
|
name: "a",
|
|
}),
|
|
"packages/b/package.json": JSON.stringify({
|
|
name: "b",
|
|
}),
|
|
});
|
|
|
|
const proc = Bun.spawn({
|
|
cmd: [bunExe(), "run", "--workspaces", "nonexistent"],
|
|
env: bunEnv,
|
|
cwd: dir,
|
|
stdout: "pipe",
|
|
stderr: "pipe",
|
|
});
|
|
|
|
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
|
|
|
|
expect(exitCode).toBe(1);
|
|
expect(stderr).toContain("No workspace packages have script");
|
|
});
|