aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--completions/bun.bash4
-rw-r--r--completions/bun.zsh6
-rw-r--r--completions/spec.yaml3
-rw-r--r--docs/cli/bun-install.md4
-rw-r--r--docs/cli/install.md9
-rw-r--r--docs/install/index.md9
-rw-r--r--docs/runtime/configuration.md3
-rw-r--r--src/api/demo/schema.d.ts1
-rw-r--r--src/api/demo/schema.js4
-rw-r--r--src/api/demo/schema.peechy1
-rw-r--r--src/api/demo/schema.zig10
-rw-r--r--src/api/schema.d.ts1
-rw-r--r--src/api/schema.js10
-rw-r--r--src/api/schema.peechy1
-rw-r--r--src/api/schema.zig10
-rw-r--r--src/bunfig.zig6
-rw-r--r--src/install/install.zig13
-rw-r--r--test/cli/install/bun-install.test.ts49
18 files changed, 142 insertions, 2 deletions
diff --git a/completions/bun.bash b/completions/bun.bash
index 7eb83c48b..499adc470 100644
--- a/completions/bun.bash
+++ b/completions/bun.bash
@@ -92,10 +92,10 @@ _bun_completions() {
PACKAGE_OPTIONS[REMOVE_OPTIONS_LONG]="";
PACKAGE_OPTIONS[REMOVE_OPTIONS_SHORT]="";
- PACKAGE_OPTIONS[SHARED_OPTIONS_LONG]="--config --yarn --production --no-save --dry-run --lockfile --force --cache-dir --no-cache --silent --verbose --global --cwd --backend --link-native-bins --help";
+ PACKAGE_OPTIONS[SHARED_OPTIONS_LONG]="--config --yarn --production --frozen-lockfile --no-save --dry-run --lockfile --force --cache-dir --no-cache --silent --verbose --global --cwd --backend --link-native-bins --help";
PACKAGE_OPTIONS[SHARED_OPTIONS_SHORT]="-c -y -p -f -g";
- PM_OPTIONS[LONG_OPTIONS]="--config --yarn --production --no-save --dry-run --lockfile --force --cache-dir --no-cache --silent --verbose --no-progress --no-summary --no-verify --ignore-scripts --global --cwd --backend --link-native-bins --help"
+ PM_OPTIONS[LONG_OPTIONS]="--config --yarn --production --frozen-lockfile --no-save --dry-run --lockfile --force --cache-dir --no-cache --silent --verbose --no-progress --no-summary --no-verify --ignore-scripts --global --cwd --backend --link-native-bins --help"
PM_OPTIONS[SHORT_OPTIONS]="-c -y -p -f -g"
local cur_word="${COMP_WORDS[${COMP_CWORD}]}";
diff --git a/completions/bun.zsh b/completions/bun.zsh
index 946445b64..a8f66b4fa 100644
--- a/completions/bun.zsh
+++ b/completions/bun.zsh
@@ -47,6 +47,7 @@ _bun() {
'-g[Add a package globally]' \
'--global[Add a package globally]' \
'--production[Don'"'"'t install devDependencies]' \
+ '--frozen-lockfile[Disallow changes to lockfile]' \
'--optional[Add dependency to optionalDependencies]' \
'--development[Add dependency to devDependencies]' \
'-d[Add dependency to devDependencies]' \
@@ -88,6 +89,7 @@ _bun() {
'--yarn[Write a yarn.lock file (yarn v1)]' \
'--global[Add a package globally]' \
'--production[Don'"'"'t install devDependencies]' \
+ '--frozen-lockfile[Disallow changes to lockfile]' \
'--optional[Add dependency to optionalDependencies]' \
'--development[Add dependency to devDependencies]' \
'-d[Add dependency to devDependencies]' \
@@ -123,6 +125,7 @@ _bun() {
'--yarn[Write a yarn.lock file (yarn v1)]' \
'--global[Add a package globally]' \
'--production[Don'"'"'t install devDependencies]' \
+ '--frozen-lockfile[Disallow changes to lockfile]' \
'--optional[Add dependency to optionalDependencies]' \
'--development[Add dependency to devDependencies]' \
'-d[Add dependency to devDependencies]' \
@@ -278,6 +281,7 @@ _bun() {
'--yarn[Write a yarn.lock file (yarn v1)]'
'-p[Do not install devDependencies]'
'--production[Do not install devDependencies]'
+ '--frozen-lockfile[Disallow changes to lockfile]' \
'--no-save[Do not save a lockfile]'
'--dry-run[Do not install anything]'
'--lockfile[Store & load a lockfile at a specific filepath]'
@@ -532,6 +536,7 @@ _bun() {
'--yarn[Write a yarn.lock file (yarn v1)]' \
'--production[Don'"'"'t install devDependencies]' \
'-p[Don'"'"'t install devDependencies]' \
+ '--frozen-lockfile[Disallow changes to lockfile]' \
'--no-save[]' \
'--dry-run[Don'"'"'t install anything]' \
'--force[Always request the latest versions from the registry & reinstall all dependenices]' \
@@ -565,6 +570,7 @@ _bun() {
'--yarn[Write a yarn.lock file (yarn v1)]' \
'--production[Don'"'"'t install devDependencies]' \
'-p[Don'"'"'t install devDependencies]' \
+ '--frozen-lockfile[Disallow changes to lockfile]' \
'--no-save[]' \
'--dry-run[Don'"'"'t install anything]' \
'-g[Remove a package globally]' \
diff --git a/completions/spec.yaml b/completions/spec.yaml
index 9c37ae89e..c3391c192 100644
--- a/completions/spec.yaml
+++ b/completions/spec.yaml
@@ -115,6 +115,7 @@ subcommands:
- yarn -- "Write a yarn.lock file (yarn v1)"
- production -- "Don't install devDependencies"
- p -- "Don't install devDependencies"
+ - frozen-lockfile -- "Disallow changes to lockfile"
- no-save --
- dry-run -- "Don't install anything"
- force -- "Always request the latest versions from the registry & reinstall all dependenices"
@@ -152,6 +153,7 @@ subcommands:
- development -- "Add dependency to devDependencies"
- d -- "Add dependency to devDependencies"
- p -- "Don't install devDependencies"
+ - frozen-lockfile -- "Disallow changes to lockfile"
- no-save --
- dry-run -- "Don't install anything"
- force -- "Always request the latest versions from the registry & reinstall all dependenices"
@@ -192,6 +194,7 @@ subcommands:
- yarn -- "Write a yarn.lock file (yarn v1)"
- production -- "Don't install devDependencies"
- p -- "Don't install devDependencies"
+ - frozen-lockfile -- "Disallow changes to lockfile"
- no-save --
- dry-run -- "Don't install anything"
- force -- "Always request the latest versions from the registry & reinstall all dependenices"
diff --git a/docs/cli/bun-install.md b/docs/cli/bun-install.md
index 11cf3ee81..8050070be 100644
--- a/docs/cli/bun-install.md
+++ b/docs/cli/bun-install.md
@@ -47,6 +47,9 @@ registry = "https://registry.yarnpkg.com/"
# Install for production? This is the equivalent to the "--production" CLI argument
production = false
+# Disallow changes to lockfile? This is the equivalent to the "--fozen-lockfile" CLI argument
+frozenLockfile = false
+
# Don't actually install
dryRun = true
@@ -108,6 +111,7 @@ export interface Install {
scopes: Scopes;
registry: Registry;
production: boolean;
+ frozenLockfile: boolean;
dryRun: boolean;
optional: boolean;
dev: boolean;
diff --git a/docs/cli/install.md b/docs/cli/install.md
index 695c975f9..4489a0d4a 100644
--- a/docs/cli/install.md
+++ b/docs/cli/install.md
@@ -49,6 +49,12 @@ To install in production mode (i.e. without `devDependencies`):
$ bun install --production
```
+To install dependencies without allowing changes to lockfile (useful on CI):
+
+```bash
+$ bun install --frozen-lockfile
+```
+
To perform a dry run (i.e. don't actually install anything):
```bash
@@ -80,6 +86,9 @@ peer = false
# equivalent to `--production` flag
production = false
+# equivalent to `--frozen-lockfile` flag
+frozenLockfile = false
+
# equivalent to `--dry-run` flag
dryRun = false
```
diff --git a/docs/install/index.md b/docs/install/index.md
index 48e001275..162a4abac 100644
--- a/docs/install/index.md
+++ b/docs/install/index.md
@@ -49,6 +49,12 @@ To install in production mode (i.e. without `devDependencies`):
$ bun install --production
```
+To install dependencies without allowing changes to lockfile (useful on CI):
+
+```bash
+$ bun install --frozen-lockfile
+```
+
To perform a dry run (i.e. don't actually install anything):
```bash
@@ -80,6 +86,9 @@ peer = false
# equivalent to `--production` flag
production = false
+# equivalent to `--frozen-lockfile` flag
+frozenLockfile = false
+
# equivalent to `--dry-run` flag
dryRun = false
```
diff --git a/docs/runtime/configuration.md b/docs/runtime/configuration.md
index e1572c990..2ae81713a 100644
--- a/docs/runtime/configuration.md
+++ b/docs/runtime/configuration.md
@@ -129,6 +129,9 @@ peer = false
# equivalent to `--production` flag
production = false
+# equivalent to `--frozen-lockfile` flag
+frozenLockfile = false
+
# equivalent to `--dry-run` flag
dryRun = false
```
diff --git a/src/api/demo/schema.d.ts b/src/api/demo/schema.d.ts
index 6f3949c77..e8a6994e7 100644
--- a/src/api/demo/schema.d.ts
+++ b/src/api/demo/schema.d.ts
@@ -681,6 +681,7 @@ export interface BunInstall {
disable_manifest_cache?: boolean;
global_dir?: string;
global_bin_dir?: string;
+ frozen_lockfile?: boolean;
}
export declare function encodeStackFrame(message: StackFrame, bb: ByteBuffer): void;
diff --git a/src/api/demo/schema.js b/src/api/demo/schema.js
index 7bdd13b65..d23d64a14 100644
--- a/src/api/demo/schema.js
+++ b/src/api/demo/schema.js
@@ -2992,6 +2992,10 @@ function decodeBunInstall(bb) {
result["global_bin_dir"] = bb.readString();
break;
+ case 19:
+ result["frozen-lockfile"] = !!bb.readByte();
+ break;
+
default:
throw new Error("Attempted to parse invalid message");
}
diff --git a/src/api/demo/schema.peechy b/src/api/demo/schema.peechy
index 09d3c1fac..e495bb9c0 100644
--- a/src/api/demo/schema.peechy
+++ b/src/api/demo/schema.peechy
@@ -550,4 +550,5 @@ message BunInstall {
bool disable_manifest_cache = 16;
string global_dir = 17;
string global_bin_dir = 18;
+ string frozen_lockfile = 19;
}
diff --git a/src/api/demo/schema.zig b/src/api/demo/schema.zig
index d57a5c725..a6de100de 100644
--- a/src/api/demo/schema.zig
+++ b/src/api/demo/schema.zig
@@ -2728,6 +2728,9 @@ pub const Api = struct {
/// global_bin_dir
global_bin_dir: ?[]const u8 = null,
+ /// frozen_lockfile
+ frozen_lockfile: ?bool = null,
+
pub fn decode(reader: anytype) anyerror!BunInstall {
var this = std.mem.zeroes(BunInstall);
@@ -2791,6 +2794,9 @@ pub const Api = struct {
18 => {
this.global_bin_dir = try reader.readValue([]const u8);
},
+ 19 => {
+ this.frozen_lockfile = try reader.readValue(bool);
+ },
else => {
return error.InvalidMessage;
},
@@ -2872,6 +2878,10 @@ pub const Api = struct {
try writer.writeFieldID(18);
try writer.writeValue(@TypeOf(global_bin_dir), global_bin_dir);
}
+ if (this.frozen_lockfile) |frozen_lockfile| {
+ try writer.writeFieldID(19);
+ try writer.writeInt(@as(u8, @boolToInt(frozen_lockfile)));
+ }
try writer.endMessage();
}
};
diff --git a/src/api/schema.d.ts b/src/api/schema.d.ts
index 4114d951d..ac6183878 100644
--- a/src/api/schema.d.ts
+++ b/src/api/schema.d.ts
@@ -709,6 +709,7 @@ export interface BunInstall {
disable_manifest_cache?: boolean;
global_dir?: string;
global_bin_dir?: string;
+ frozen_lockfile?: boolean;
}
export interface ClientServerModule {
diff --git a/src/api/schema.js b/src/api/schema.js
index c4f2400ed..270eb9a62 100644
--- a/src/api/schema.js
+++ b/src/api/schema.js
@@ -3044,6 +3044,10 @@ function decodeBunInstall(bb) {
result["global_bin_dir"] = bb.readString();
break;
+ case 19:
+ result["frozen_lockfile"] = !!bb.readByte();
+ break;
+
default:
throw new Error("Attempted to parse invalid message");
}
@@ -3164,6 +3168,12 @@ function encodeBunInstall(message, bb) {
bb.writeByte(18);
bb.writeString(value);
}
+
+ var value = message["frozen_lockfile"];
+ if (value != null) {
+ bb.writeByte(19);
+ bb.writeByte(value);
+ }
bb.writeByte(0);
}
diff --git a/src/api/schema.peechy b/src/api/schema.peechy
index 71e85d68e..6d28381c4 100644
--- a/src/api/schema.peechy
+++ b/src/api/schema.peechy
@@ -590,6 +590,7 @@ message BunInstall {
bool disable_manifest_cache = 16;
string global_dir = 17;
string global_bin_dir = 18;
+ bool frozen_lockfile = 19;
}
struct ClientServerModule {
diff --git a/src/api/schema.zig b/src/api/schema.zig
index 1012e6051..708d32ca0 100644
--- a/src/api/schema.zig
+++ b/src/api/schema.zig
@@ -2901,6 +2901,9 @@ pub const Api = struct {
/// global_bin_dir
global_bin_dir: ?[]const u8 = null,
+ /// frozen_lockfile
+ frozen_lockfile: ?bool = null,
+
pub fn decode(reader: anytype) anyerror!BunInstall {
var this = std.mem.zeroes(BunInstall);
@@ -2964,6 +2967,9 @@ pub const Api = struct {
18 => {
this.global_bin_dir = try reader.readValue([]const u8);
},
+ 19 => {
+ this.frozen_lockfile = try reader.readValue(bool);
+ },
else => {
return error.InvalidMessage;
},
@@ -3045,6 +3051,10 @@ pub const Api = struct {
try writer.writeFieldID(18);
try writer.writeValue(@TypeOf(global_bin_dir), global_bin_dir);
}
+ if (this.frozen_lockfile) |frozen_lockfile| {
+ try writer.writeFieldID(19);
+ try writer.writeInt(@as(u8, @boolToInt(frozen_lockfile)));
+ }
try writer.endMessage();
}
};
diff --git a/src/bunfig.zig b/src/bunfig.zig
index 9df2978b0..597fb0985 100644
--- a/src/bunfig.zig
+++ b/src/bunfig.zig
@@ -322,6 +322,12 @@ pub const Bunfig = struct {
}
}
+ if (_bun.get("frozenLockfile")) |frozen_lockfile| {
+ if (frozen_lockfile.asBool()) |value| {
+ install.frozen_lockfile = value;
+ }
+ }
+
if (_bun.get("lockfile")) |lockfile_expr| {
if (lockfile_expr.get("print")) |lockfile| {
try this.expect(lockfile, .e_string);
diff --git a/src/install/install.zig b/src/install/install.zig
index 32c24548c..81e2a7bb8 100644
--- a/src/install/install.zig
+++ b/src/install/install.zig
@@ -4443,6 +4443,12 @@ pub const PackageManager = struct {
}
}
+ if (bun_install.frozen_lockfile) |frozen_lockfile| {
+ if (frozen_lockfile) {
+ this.enable.frozen_lockfile = true;
+ }
+ }
+
if (bun_install.save_optional) |save| {
this.remote_package_features.optional_dependencies = save;
this.local_package_features.optional_dependencies = save;
@@ -4676,6 +4682,10 @@ pub const PackageManager = struct {
this.enable.frozen_lockfile = true;
}
+ if (cli.frozen_lockfile) {
+ this.enable.frozen_lockfile = true;
+ }
+
if (cli.force) {
this.enable.manifest_cache_control = false;
this.enable.force_install = true;
@@ -5649,6 +5659,7 @@ pub const PackageManager = struct {
clap.parseParam("--save Save to package.json") catch unreachable,
clap.parseParam("--dry-run Don't install anything") catch unreachable,
clap.parseParam("--lockfile <PATH> Store & load a lockfile at a specific filepath") catch unreachable,
+ clap.parseParam("--frozen-lockfile Disallow changes to lockfile") catch unreachable,
clap.parseParam("-f, --force Always request the latest versions from the registry & reinstall all dependencies") catch unreachable,
clap.parseParam("--cache-dir <PATH> Store & load cached data from a specific directory path") catch unreachable,
clap.parseParam("--no-cache Ignore manifest cache entirely") catch unreachable,
@@ -5709,6 +5720,7 @@ pub const PackageManager = struct {
yarn: bool = false,
production: bool = false,
+ frozen_lockfile: bool = false,
no_save: bool = false,
dry_run: bool = false,
force: bool = false,
@@ -5777,6 +5789,7 @@ pub const PackageManager = struct {
var cli = CommandLineArguments{};
cli.yarn = args.flag("--yarn");
cli.production = args.flag("--production");
+ cli.frozen_lockfile = args.flag("--frozen-lockfile");
cli.no_progress = args.flag("--no-progress");
cli.dry_run = args.flag("--dry-run");
cli.global = args.flag("--global");
diff --git a/test/cli/install/bun-install.test.ts b/test/cli/install/bun-install.test.ts
index 4b3342e3a..6baee23a9 100644
--- a/test/cli/install/bun-install.test.ts
+++ b/test/cli/install/bun-install.test.ts
@@ -4241,6 +4241,55 @@ it("should handle --cwd", async () => {
});
});
+it("should handle --frozen-lockfile", async () => {
+ await writeFile(
+ join(package_dir, "package.json"),
+ JSON.stringify({ name: "foo", version: "0.0.1", dependencies: { bar: "0.0.2" } }),
+ );
+
+ const { stderr, exited } = spawn({
+ cmd: [bunExe(), "install", "--frozen-lockfile"],
+ cwd: package_dir,
+ stdout: null,
+ stdin: "pipe",
+ stderr: "pipe",
+ env,
+ });
+
+ expect(stderr).toBeDefined();
+ const err = await new Response(stderr).text();
+ expect(err).toContain("error: lockfile had changes, but lockfile is frozen");
+ expect(await exited).toBe(1);
+});
+
+it("should handle frozenLockfile in config file", async () => {
+ await writeFile(
+ join(package_dir, "package.json"),
+ JSON.stringify({ name: "foo", version: "0.0.1", dependencies: { bar: "0.0.2" } }),
+ );
+ await writeFile(
+ join(package_dir, "bunfig.toml"),
+ `
+[install]
+frozenLockfile = true
+`,
+ );
+
+ const { stderr, exited } = spawn({
+ cmd: [bunExe(), "install"],
+ cwd: package_dir,
+ stdout: null,
+ stdin: "pipe",
+ stderr: "pipe",
+ env,
+ });
+
+ expect(stderr).toBeDefined();
+ const err = await new Response(stderr).text();
+ expect(err).toContain("error: lockfile had changes, but lockfile is frozen");
+ expect(await exited).toBe(1);
+});
+
it("should perform bin-linking across multiple dependencies", async () => {
const foo_package = JSON.stringify({
name: "foo",