From e920ee32894dcd2ab0f97ff6f45c29d65024da0c Mon Sep 17 00:00:00 2001 From: Jeremy Evans Date: Sat, 22 Nov 2025 20:31:14 -0800 Subject: [PATCH] [ruby/rubygems] Support bundle install --lockfile option This allows for specifying the lockfile to read and write. It mirrors the --gemfile option, and has higher priority than the lockfile method in the Gemfile. It also mirrors the bundle lock --lockfile option. When the --lockfile option is used, it is applied twice. First, before the Gemfile is read, to specify the lockfile to operate on, and again after the Gemfile is read, so that if the Gemfile has a lockfile method that overrides the defintion's lockfile, the --lockfile option still has higher precedence. https://github.com/ruby/rubygems/commit/17acdd4a89 --- lib/bundler/cli.rb | 17 +++++++++++++++-- lib/bundler/cli/install.rb | 1 + lib/bundler/man/bundle-install.1 | 5 ++++- lib/bundler/man/bundle-install.1.ronn | 5 +++++ lib/bundler/man/gemfile.5 | 6 ++++-- lib/bundler/man/gemfile.5.ronn | 7 ++++--- spec/bundler/commands/install_spec.rb | 23 +++++++++++++++++++++++ 7 files changed, 56 insertions(+), 8 deletions(-) diff --git a/lib/bundler/cli.rb b/lib/bundler/cli.rb index 48178965697667..55f656e3f3afd0 100644 --- a/lib/bundler/cli.rb +++ b/lib/bundler/cli.rb @@ -59,17 +59,29 @@ def self.aliases_for(command_name) def initialize(*args) super + current_cmd = args.last[:current_command].name + custom_gemfile = options[:gemfile] || Bundler.settings[:gemfile] if custom_gemfile && !custom_gemfile.empty? Bundler::SharedHelpers.set_env "BUNDLE_GEMFILE", File.expand_path(custom_gemfile) - Bundler.reset_settings_and_root! + reset_settings = true end + # lock --lockfile works differently than install --lockfile + unless current_cmd == "lock" + custom_lockfile = options[:lockfile] || Bundler.settings[:lockfile] + if custom_lockfile && !custom_lockfile.empty? + Bundler::SharedHelpers.set_env "BUNDLE_LOCKFILE", File.expand_path(custom_lockfile) + reset_settings = true + end + end + + Bundler.reset_settings_and_root! if reset_settings + Bundler.auto_switch Bundler.settings.set_command_option_if_given :retry, options[:retry] - current_cmd = args.last[:current_command].name Bundler.auto_install if AUTO_INSTALL_CMDS.include?(current_cmd) rescue UnknownArgumentError => e raise InvalidOption, e.message @@ -232,6 +244,7 @@ def remove(*gems) method_option "gemfile", type: :string, banner: "Use the specified gemfile instead of Gemfile" method_option "jobs", aliases: "-j", type: :numeric, banner: "Specify the number of jobs to run in parallel" method_option "local", type: :boolean, banner: "Do not attempt to fetch gems remotely and use the gem cache instead" + method_option "lockfile", type: :string, banner: "Use the specified lockfile instead of the default." method_option "prefer-local", type: :boolean, banner: "Only attempt to fetch gems remotely if not present locally, even if newer versions are available remotely" method_option "no-cache", type: :boolean, banner: "Don't update the existing gem cache." method_option "no-lock", type: :boolean, banner: "Don't create a lockfile." diff --git a/lib/bundler/cli/install.rb b/lib/bundler/cli/install.rb index 85b303eee60900..ba1cef8434e63c 100644 --- a/lib/bundler/cli/install.rb +++ b/lib/bundler/cli/install.rb @@ -44,6 +44,7 @@ def run # (rather than some optimizations we perform at app runtime). definition = Bundler.definition(strict: true) definition.validate_runtime! + definition.lockfile = options["lockfile"] if options["lockfile"] definition.lockfile = false if options["no-lock"] installer = Installer.install(Bundler.root, definition, options) diff --git a/lib/bundler/man/bundle-install.1 b/lib/bundler/man/bundle-install.1 index 1acbe430585e22..68530f3ebb588d 100644 --- a/lib/bundler/man/bundle-install.1 +++ b/lib/bundler/man/bundle-install.1 @@ -4,7 +4,7 @@ .SH "NAME" \fBbundle\-install\fR \- Install the dependencies specified in your Gemfile .SH "SYNOPSIS" -\fBbundle install\fR [\-\-force] [\-\-full\-index] [\-\-gemfile=GEMFILE] [\-\-jobs=NUMBER] [\-\-local] [\-\-no\-cache] [\-\-no\-lock] [\-\-prefer\-local] [\-\-quiet] [\-\-retry=NUMBER] [\-\-standalone[=GROUP[ GROUP\|\.\|\.\|\.]]] [\-\-trust\-policy=TRUST\-POLICY] [\-\-target\-rbconfig=TARGET\-RBCONFIG] +\fBbundle install\fR [\-\-force] [\-\-full\-index] [\-\-gemfile=GEMFILE] [\-\-jobs=NUMBER] [\-\-local] [\-\-lockfile=LOCKFILE] [\-\-no\-cache] [\-\-no\-lock] [\-\-prefer\-local] [\-\-quiet] [\-\-retry=NUMBER] [\-\-standalone[=GROUP[ GROUP\|\.\|\.\|\.]]] [\-\-trust\-policy=TRUST\-POLICY] [\-\-target\-rbconfig=TARGET\-RBCONFIG] .SH "DESCRIPTION" Install the gems specified in your Gemfile(5)\. If this is the first time you run bundle install (and a \fBGemfile\.lock\fR does not exist), Bundler will fetch all remote sources, resolve dependencies and install all needed gems\. .P @@ -28,6 +28,9 @@ The maximum number of parallel download and install jobs\. The default is the nu \fB\-\-local\fR Do not attempt to connect to \fBrubygems\.org\fR\. Instead, Bundler will use the gems already present in Rubygems' cache or in \fBvendor/cache\fR\. Note that if an appropriate platform\-specific gem exists on \fBrubygems\.org\fR it will not be found\. .TP +\fB\-\-lockfile=LOCKFILE\fR +The location of the lockfile which Bundler should use\. This defaults to the Gemfile location with \fB\.lock\fR appended\. +.TP \fB\-\-prefer\-local\fR Force using locally installed gems, or gems already present in Rubygems' cache or in \fBvendor/cache\fR, when resolving, even if newer versions are available remotely\. Only attempt to connect to \fBrubygems\.org\fR for gems that are not present locally\. .TP diff --git a/lib/bundler/man/bundle-install.1.ronn b/lib/bundler/man/bundle-install.1.ronn index adb47490d74b2d..c7d88bfb73c3e4 100644 --- a/lib/bundler/man/bundle-install.1.ronn +++ b/lib/bundler/man/bundle-install.1.ronn @@ -8,6 +8,7 @@ bundle-install(1) -- Install the dependencies specified in your Gemfile [--gemfile=GEMFILE] [--jobs=NUMBER] [--local] + [--lockfile=LOCKFILE] [--no-cache] [--no-lock] [--prefer-local] @@ -61,6 +62,10 @@ update process below under [CONSERVATIVE UPDATING][]. appropriate platform-specific gem exists on `rubygems.org` it will not be found. +* `--lockfile=LOCKFILE`: + The location of the lockfile which Bundler should use. This defaults + to the Gemfile location with `.lock` appended. + * `--prefer-local`: Force using locally installed gems, or gems already present in Rubygems' cache or in `vendor/cache`, when resolving, even if newer versions are available diff --git a/lib/bundler/man/gemfile.5 b/lib/bundler/man/gemfile.5 index f345580ed77edb..fce7d8e178439b 100644 --- a/lib/bundler/man/gemfile.5 +++ b/lib/bundler/man/gemfile.5 @@ -492,10 +492,12 @@ When determining path to the lockfile or whether to create a lockfile, the follo .IP "1." 4 The \fBbundle install\fR \fB\-\-no\-lock\fR option (which disables lockfile creation)\. .IP "2." 4 -The \fBlockfile\fR method in the Gemfile\. +The \fBbundle install\fR \fB\-\-lockfile\fR option\. .IP "3." 4 -The \fBBUNDLE_LOCKFILE\fR environment variable\. +The \fBlockfile\fR method in the Gemfile\. .IP "4." 4 +The \fBBUNDLE_LOCKFILE\fR environment variable\. +.IP "5." 4 The default behavior of adding \fB\.lock\fR to the end of the Gemfile name\. .IP "" 0 diff --git a/lib/bundler/man/gemfile.5.ronn b/lib/bundler/man/gemfile.5.ronn index 3dea29cb3c6aaf..e4bc91359e3cc5 100644 --- a/lib/bundler/man/gemfile.5.ronn +++ b/lib/bundler/man/gemfile.5.ronn @@ -580,6 +580,7 @@ When determining path to the lockfile or whether to create a lockfile, the following precedence is used: 1. The `bundle install` `--no-lock` option (which disables lockfile creation). -2. The `lockfile` method in the Gemfile. -3. The `BUNDLE_LOCKFILE` environment variable. -4. The default behavior of adding `.lock` to the end of the Gemfile name. +1. The `bundle install` `--lockfile` option. +1. The `lockfile` method in the Gemfile. +1. The `BUNDLE_LOCKFILE` environment variable. +1. The default behavior of adding `.lock` to the end of the Gemfile name. diff --git a/spec/bundler/commands/install_spec.rb b/spec/bundler/commands/install_spec.rb index 69d9a860996197..bacd8d64f2d6c3 100644 --- a/spec/bundler/commands/install_spec.rb +++ b/spec/bundler/commands/install_spec.rb @@ -41,6 +41,17 @@ expect(bundled_app("OmgFile.lock")).to exist end + it "creates lockfile based on --lockfile option is given" do + gemfile bundled_app("OmgFile"), <<-G + source "https://gem.repo1" + gem "myrack", "1.0" + G + + bundle "install --gemfile OmgFile --lockfile ReallyOmgFile.lock" + + expect(bundled_app("ReallyOmgFile.lock")).to exist + end + it "does not make a lockfile if lockfile false is used in Gemfile" do install_gemfile <<-G lockfile false @@ -100,6 +111,18 @@ expect(bundled_app("OmgFile.lock")).not_to exist end + it "doesn't create a lockfile if --no-lock and --lockfile options are given" do + gemfile bundled_app("OmgFile"), <<-G + source "https://gem.repo1" + gem "myrack", "1.0" + G + + bundle "install --gemfile OmgFile --no-lock --lockfile ReallyOmgFile.lock" + + expect(bundled_app("OmgFile.lock")).not_to exist + expect(bundled_app("ReallyOmgFile.lock")).not_to exist + end + it "doesn't delete the lockfile if one already exists" do install_gemfile <<-G source "https://gem.repo1"