mirror of
https://github.com/oven-sh/bun
synced 2026-02-02 15:08:46 +00:00
feat(cli/run): add --parallel and --sequential for running multiple scripts with workspace support (#26551)
## Summary Adds `bun run --parallel` and `bun run --sequential` — new flags for running multiple package.json scripts concurrently or sequentially with Foreman-style prefixed output. Includes full `--filter`/`--workspaces` integration for running scripts across workspace packages. ### Usage ```bash # Run "build" and "test" concurrently from the current package.json bun run --parallel build test # Run "build" and "test" sequentially with prefixed output bun run --sequential build test # Glob-matched script names bun run --parallel "build:*" # Run "build" in all workspace packages concurrently bun run --parallel --filter '*' build # Run "build" in all workspace packages sequentially bun run --sequential --workspaces build # Glob-matched scripts across all packages bun run --parallel --filter '*' "build:*" # Multiple scripts across all packages bun run --parallel --filter '*' build lint test # Continue running even if one package fails bun run --parallel --no-exit-on-error --filter '*' test # Skip packages missing the script bun run --parallel --workspaces --if-present build ``` ## How it works ### Output format Each script's stdout/stderr is prefixed with a colored, padded label: ``` build | compiling... test | running suite... lint | checking files... ``` ### Label format - **Without `--filter`/`--workspaces`**: labels are just the script name → `build | output` - **With `--filter`/`--workspaces`**: labels are `package:script` → `pkg-a:build | output` - **Fallback**: if a package.json has no `name` field, the relative path from the workspace root is used (e.g., `packages/my-pkg:build`) ### Execution model - **`--parallel`**: all scripts start immediately, output is interleaved with prefixes - **`--sequential`**: scripts run one at a time in order, each waiting for the previous to finish - **Pre/post scripts** (`prebuild`/`postbuild`) are grouped with their main script and run in dependency order within each group - By default, a failure kills all remaining scripts. `--no-exit-on-error` lets all scripts finish. ### Workspace integration The workspace branch in `multi_run.zig` uses a two-pass approach for deterministic ordering: 1. **Collect**: iterate workspace packages using `FilterArg.PackageFilterIterator` (same infrastructure as `filter_run.zig`), filtering with `FilterArg.FilterSet`, collecting matched packages with their scripts, PATH, and cwd. 2. **Sort**: sort matched packages by name (tiebreak by directory path) for deterministic ordering — filesystem iteration order from the glob walker is nondeterministic. 3. **Build configs**: for each sorted package, expand script names (including globs like `build:*`) against that package's scripts map, creating `ScriptConfig` entries with `pkg:script` labels and per-package cwd/PATH. ### Behavioral consistency with `filter_run.zig` | Behavior | `filter_run.zig` | `multi_run.zig` (this PR) | |----------|-------------------|---------------------------| | `--workspaces` skips root package | Yes | Yes | | `--workspaces` errors on missing script | Yes | Yes | | `--if-present` silently skips missing | Yes | Yes | | `--filter` without `--workspaces` includes root | Yes (if matches) | Yes (if matches) | | Pre/post script chains | Per-package | Per-package | | Per-package cwd | Yes | Yes | | Per-package PATH (`node_modules/.bin`) | Yes | Yes | ### Key implementation details - Each workspace package script runs in its own package directory with its own `node_modules/.bin` PATH - `dirpath` from the glob walker is duped to avoid use-after-free when the iterator's arena is freed between patterns - `addScriptConfigs` takes an optional `label_prefix` parameter — `null` for single-package mode, package name for workspace mode - `MultiRunProcessHandle` is registered in the `ProcessExitHandler` tagged pointer union in `process.zig` ## Files changed | File | Change | |------|--------| | `src/cli/multi_run.zig` | New file: process management, output routing, workspace integration, dependency ordering | | `src/cli.zig` | Dispatch to `MultiRun.run()` for `--parallel`/`--sequential`, new context fields | | `src/cli/Arguments.zig` | Parse `--parallel`, `--sequential`, `--no-exit-on-error` flags | | `src/bun.js/api/bun/process.zig` | Register `MultiRunProcessHandle` in `ProcessExitHandler` tagged pointer union | | `test/cli/run/multi-run.test.ts` | 118 tests (102 core + 16 workspace integration) | | `docs/pm/filter.mdx` | Document `--parallel`/`--sequential` + `--filter`/`--workspaces` combination | | `docs/snippets/cli/run.mdx` | Add `--parallel`, `--sequential`, `--no-exit-on-error` parameter docs | ## Test plan All 118 tests pass with debug build (`bun bd test test/cli/run/multi-run.test.ts`). The 16 new workspace tests all fail with system bun (`USE_SYSTEM_BUN=1`), confirming they test new functionality. ### Workspace integration tests (16 tests) 1. `--parallel --filter='*'` runs script in all packages 2. `--parallel --filter='pkg-a'` runs only in matching package 3. `--parallel --workspaces` matches all workspace packages 4. `--parallel --filter='*'` with glob expands per-package scripts 5. `--sequential --filter='*'` runs in sequence (deterministic order) 6. Workspace + failure aborts other scripts 7. Workspace + `--no-exit-on-error` lets all finish 8. `--workspaces` skips root package 9. Each workspace script runs in its own package directory (cwd verification) 10. Multiple script names across workspaces (`build` + `test`) 11. Pre/post scripts work per workspace package 12. `--filter` skips packages without the script (no error) 13. `--workspaces` errors when a package is missing the script 14. `--workspaces --if-present` skips missing scripts silently 15. Labels are padded correctly across workspace packages 16. Package without `name` field uses relative path as label --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Dylan Conway <dylan.conway567@gmail.com>
This commit is contained in:
@@ -97,6 +97,31 @@ Filters respect your [workspace configuration](/pm/workspaces): If you have a `p
|
||||
bun run --filter foo myscript
|
||||
```
|
||||
|
||||
### Parallel and sequential mode
|
||||
|
||||
Combine `--filter` or `--workspaces` with `--parallel` or `--sequential` to run scripts across workspace packages with Foreman-style prefixed output:
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
# Run "build" in all matching packages concurrently
|
||||
bun run --parallel --filter '*' build
|
||||
|
||||
# Run "build" in all workspace packages sequentially
|
||||
bun run --sequential --workspaces build
|
||||
|
||||
# Run glob-matched scripts across all packages
|
||||
bun run --parallel --filter '*' "build:*"
|
||||
|
||||
# Continue running even if one package's script fails
|
||||
bun run --parallel --no-exit-on-error --filter '*' test
|
||||
|
||||
# Run multiple scripts across all packages
|
||||
bun run --parallel --filter '*' build lint
|
||||
```
|
||||
|
||||
Each line of output is prefixed with the package and script name (e.g. `pkg-a:build | ...`). Without `--filter`/`--workspaces`, the prefix is just the script name (e.g. `build | ...`). When a package's `package.json` has no `name` field, the relative path from the workspace root is used instead.
|
||||
|
||||
Use `--if-present` with `--workspaces` to skip packages that don't have the requested script instead of erroring.
|
||||
|
||||
### Dependency Order
|
||||
|
||||
Bun will respect package dependency order when running scripts. Say you have a package `foo` that depends on another package `bar` in your workspace, and both packages have a `build` script. When you run `bun --filter '*' build`, you will notice that `foo` will only start running once `bar` is done.
|
||||
|
||||
@@ -40,6 +40,18 @@ bun run <file or script>
|
||||
Run a script in all workspace packages (from the <code>workspaces</code> field in <code>package.json</code>)
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="--parallel" type="boolean">
|
||||
Run multiple scripts or workspace scripts concurrently with prefixed output
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="--sequential" type="boolean">
|
||||
Run multiple scripts or workspace scripts one after another with prefixed output
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="--no-exit-on-error" type="boolean">
|
||||
When using <code>--parallel</code> or <code>--sequential</code>, continue running other scripts when one fails
|
||||
</ParamField>
|
||||
|
||||
### Runtime & Process Control
|
||||
|
||||
<ParamField path="--bun" type="boolean">
|
||||
|
||||
Reference in New Issue
Block a user