diff options
-rw-r--r-- | completions/bun.bash | 4 | ||||
-rw-r--r-- | completions/bun.zsh | 6 | ||||
-rw-r--r-- | completions/spec.yaml | 3 | ||||
-rw-r--r-- | docs/cli/bun-install.md | 4 | ||||
-rw-r--r-- | docs/cli/install.md | 9 | ||||
-rw-r--r-- | docs/install/index.md | 9 | ||||
-rw-r--r-- | docs/runtime/configuration.md | 3 | ||||
-rw-r--r-- | src/api/demo/schema.d.ts | 1 | ||||
-rw-r--r-- | src/api/demo/schema.js | 4 | ||||
-rw-r--r-- | src/api/demo/schema.peechy | 1 | ||||
-rw-r--r-- | src/api/demo/schema.zig | 10 | ||||
-rw-r--r-- | src/api/schema.d.ts | 1 | ||||
-rw-r--r-- | src/api/schema.js | 10 | ||||
-rw-r--r-- | src/api/schema.peechy | 1 | ||||
-rw-r--r-- | src/api/schema.zig | 10 | ||||
-rw-r--r-- | src/bunfig.zig | 6 | ||||
-rw-r--r-- | src/install/install.zig | 13 | ||||
-rw-r--r-- | test/cli/install/bun-install.test.ts | 49 |
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", |