Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions docs/pm/cli/install.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -158,13 +158,15 @@ cowsay "Bun!"

## Production mode

To install in production mode (without `devDependencies` or `optionalDependencies`):
To install in production mode (without `devDependencies`):

```bash terminal icon="terminal"
bun install --production
```

For reproducible installs, use `--frozen-lockfile`. Bun installs the exact versions specified in the lockfile and does not update it. If your `package.json` disagrees with `bun.lock`, Bun exits with an error.
Passing `--production` also implies `--frozen-lockfile`, so the install fails if `package.json` is out of sync with the lockfile. Production mode also aborts immediately on the first install error (for example, a bad lockfile or a failed bin link) instead of reporting it at the end. If you only want to skip `devDependencies` without these stricter checks, use `--omit=dev` instead.

For reproducible installs without skipping `devDependencies`, use `--frozen-lockfile` on its own. Bun installs the exact versions specified in the lockfile and does not update it. If your `package.json` disagrees with `bun.lock`, Bun exits with an error.

```bash terminal icon="terminal"
bun install --frozen-lockfile
Expand Down
2 changes: 1 addition & 1 deletion docs/runtime/bunfig.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -430,7 +430,7 @@ peer = true

Whether `bun install` runs in "production mode". Default `false`.

In production mode, `"devDependencies"` are not installed. The `--production` CLI flag overrides this setting.
In production mode, `"devDependencies"` are not installed and the lockfile is frozen (as if `--frozen-lockfile` was passed), so installs fail if `package.json` disagrees with `bun.lock`. The `--production` CLI flag overrides this setting.

```toml title="bunfig.toml" icon="settings"
[install]
Expand Down
2 changes: 1 addition & 1 deletion docs/snippets/cli/add.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ bun add <package> <@version>
### Dependency Management

<ParamField path="--production" type="boolean">
Don't install devDependencies. Alias: <code>-p</code>
Don't install devDependencies. Implies --frozen-lockfile. Alias: <code>-p</code>
</ParamField>

<ParamField path="--omit" type="string">
Expand Down
2 changes: 1 addition & 1 deletion docs/snippets/cli/install.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ bun install <name>@<version>
### Dependency Scope & Management

<ParamField path="--production" type="boolean">
Don't install devDependencies
Don't install devDependencies. Implies --frozen-lockfile
</ParamField>
Comment thread
robobun marked this conversation as resolved.

<ParamField path="--no-save" type="boolean">
Expand Down
2 changes: 1 addition & 1 deletion docs/snippets/cli/link.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ bun link <packages>
### Dependency Management

<ParamField path="--production" type="boolean">
Don't install devDependencies. Alias: <code>-p</code>
Don't install devDependencies. Implies --frozen-lockfile. Alias: <code>-p</code>
</ParamField>

<ParamField path="--omit" type="string">
Expand Down
2 changes: 1 addition & 1 deletion docs/snippets/cli/outdated.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ bun outdated <filter>
### Dependency Scope & Target

<ParamField path="-p, --production" type="boolean">
Don't install devDependencies
Don't install devDependencies. Implies --frozen-lockfile
</ParamField>

<ParamField path="--omit" type="string">
Expand Down
2 changes: 1 addition & 1 deletion docs/snippets/cli/patch.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ bun patch <package>@<version>
### Dependency Management

<ParamField path="--production" type="boolean">
Don't install devDependencies. Alias: <code>-p</code>
Don't install devDependencies. Implies --frozen-lockfile. Alias: <code>-p</code>
</ParamField>

<ParamField path="--ignore-scripts" type="boolean">
Expand Down
2 changes: 1 addition & 1 deletion docs/snippets/cli/publish.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ bun publish --cafile ./ca-cert.pem
#### Dependency Management

<ParamField path="-p, --production" type="boolean">
Don't install devDependencies
Don't install devDependencies. Implies --frozen-lockfile
</ParamField>

<ParamField path="--omit" type="string">
Expand Down
2 changes: 1 addition & 1 deletion docs/snippets/cli/remove.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ bun remove <package>
### Dependency Filtering

<ParamField path="--production" type="boolean">
Don't install devDependencies. Alias: <code>-p</code>
Don't install devDependencies. Implies --frozen-lockfile. Alias: <code>-p</code>
</ParamField>

<ParamField path="--omit" type="string">
Expand Down
2 changes: 1 addition & 1 deletion docs/snippets/cli/update.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ bun update <package> <version>
### Dependency Scope

<ParamField path="--production" type="boolean">
Don't install devDependencies. Alias: <code>-p</code>
Don't install devDependencies. Implies --frozen-lockfile. Alias: <code>-p</code>
</ParamField>

<ParamField path="--global" type="boolean">
Expand Down
4 changes: 3 additions & 1 deletion src/install/PackageManager/CommandLineArguments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,9 @@ const BACKEND_PARAM: ParamType = clap::param!(
const SHARED_PARAMS: &[ParamType] = &[
clap::param!("-c, --config <STR>? Specify path to config file (bunfig.toml)"),
clap::param!("-y, --yarn Write a yarn.lock file (yarn v1)"),
clap::param!("-p, --production Don't install devDependencies"),
clap::param!(
"-p, --production Don't install devDependencies. Implies --frozen-lockfile"
),
clap::param!("-P, --prod"),
clap::param!(
"--no-save Don't update package.json or save a lockfile"
Expand Down
31 changes: 16 additions & 15 deletions src/install/PackageManager/install_with_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -731,15 +731,7 @@ pub fn install_with_manager(
}
}

if log_level != Options::LogLevel::Silent {
bun_core::pretty_errorln!(
"<r><red>error<r><d>:<r> lockfile had changes, but lockfile is frozen"
);
bun_core::note!(
"try re-running without <d>--frozen-lockfile<r> and commit the updated lockfile"
);
}
Global::crash();
crash_frozen_lockfile(log_level);
}
}

Expand Down Expand Up @@ -1376,6 +1368,20 @@ fn add_dependency_error(
// `bun install` / `bun install --frozen-lockfile` (node_modules already up to
// date) faults in far fewer distinct `.text` pages.

#[cold]
#[inline(never)]
fn crash_frozen_lockfile(log_level: Options::LogLevel) -> ! {
if log_level != Options::LogLevel::Silent {
bun_core::pretty_errorln!(
"<r><red>error<r><d>:<r> lockfile had changes, but lockfile is frozen"
);
bun_core::note!(
"try re-running without <d>--frozen-lockfile<r> or <d>--production<r> and commit the updated lockfile"
);
}
Global::crash();
}

#[cold]
#[inline(never)]
fn report_lockfile_load_error(
Expand Down Expand Up @@ -1517,12 +1523,7 @@ fn create_new_lockfile_and_enqueue(
if manager.options.enable.frozen_lockfile()
&& !matches!(load_result, lockfile::LoadResult::NotFound)
{
if log_level != Options::LogLevel::Silent {
bun_core::pretty_errorln!(
"<r><red>error<r>: lockfile had changes, but lockfile is frozen"
);
}
Global::crash();
crash_frozen_lockfile(log_level);
}

// SAFETY: `manager.log` is a non-null backref to the CLI log set at init().
Expand Down
62 changes: 62 additions & 0 deletions test/cli/install/bun-install.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6764,6 +6764,68 @@ describe.concurrent("bun-install", () => {
});
});

// https://github.com/oven-sh/bun/issues/10949
it("documents that --production implies --frozen-lockfile", async () => {
await withContext(defaultOpts, async ctx => {
let urls: string[] = [];
setContextHandler(
ctx,
dummyRegistryForContext(ctx, urls, { "0.0.3": { as: "0.0.3" }, "0.0.5": { as: "0.0.5" } }),
);

// --help should say that --production implies --frozen-lockfile
const help = spawn({
cmd: [bunExe(), "install", "--help"],
cwd: ctx.package_dir,
stdout: "pipe",
stderr: "pipe",
env,
});
const [helpOut, helpErr] = await Promise.all([help.stdout.text(), help.stderr.text()]);
const helpText = helpOut + helpErr;
const productionLine = helpText.split("\n").find(line => line.includes("--production"));
expect(productionLine).toMatch(/--production.*Implies --frozen-lockfile/);
expect(await help.exited).toBe(0);

await writeFile(
join(ctx.package_dir, "package.json"),
JSON.stringify({ name: "foo", version: "0.0.1", dependencies: { baz: "0.0.3" } }),
);

expect(
await spawn({
cmd: [bunExe(), "install"],
cwd: ctx.package_dir,
stdout: "ignore",
stdin: "ignore",
stderr: "ignore",
env,
}).exited,
).toBe(0);

// change version of baz in package.json so it disagrees with the lockfile
await writeFile(
join(ctx.package_dir, "package.json"),
JSON.stringify({ name: "foo", version: "0.0.1", dependencies: { baz: "0.0.5" } }),
);

const { stderr, exited } = spawn({
cmd: [bunExe(), "install", "--production"],
cwd: ctx.package_dir,
stdout: "ignore",
stdin: "ignore",
stderr: "pipe",
env,
});

// the note should mention --production, not just --frozen-lockfile
const err = await stderr.text();
expect(err).toContain("error: lockfile had changes, but lockfile is frozen");
Comment thread
robobun marked this conversation as resolved.
expect(err).toContain("try re-running without --frozen-lockfile or --production and commit the updated lockfile");
expect(await exited).toBe(1);
});
});

it("should perform bin-linking across multiple dependencies", async () => {
await withContext(defaultOpts, async ctx => {
const foo_package = JSON.stringify({
Expand Down
Loading