Skip to content
Merged
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
4 changes: 2 additions & 2 deletions lib/bundler/cli.rb
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@ def install
method_option "source", type: :array, banner: "Update a specific source (and all gems associated with it)"
method_option "force", type: :boolean, aliases: "--redownload", banner: "Force reinstalling every gem, even if already installed"
method_option "ruby", type: :boolean, banner: "Update ruby specified in Gemfile.lock"
method_option "bundler", type: :string, lazy_default: "> 0.a", banner: "Update the locked version of bundler"
method_option "bundler", type: :string, lazy_default: ">= #{Bundler::VERSION}", banner: "Update the locked version of bundler"
method_option "patch", type: :boolean, banner: "Prefer updating only to next patch version"
method_option "minor", type: :boolean, banner: "Prefer updating only to next minor version"
method_option "major", type: :boolean, banner: "Prefer updating to next major version (default)"
Expand Down Expand Up @@ -647,7 +647,7 @@ def inject(*)
method_option "pre", type: :boolean, banner: "If updating, always choose the highest allowed version, regardless of prerelease status"
method_option "strict", type: :boolean, banner: "If updating, do not allow any gem to be updated past latest --patch | --minor | --major"
method_option "conservative", type: :boolean, banner: "If updating, use bundle install conservative update behavior and do not allow shared dependencies to be updated"
method_option "bundler", type: :string, lazy_default: "> 0.a", banner: "Update the locked version of bundler"
method_option "bundler", type: :string, lazy_default: ">= #{Bundler::VERSION}", banner: "Update the locked version of bundler"
method_option "cooldown", type: :numeric, banner: "Only consider gem versions published at least N days ago. Use 0 to disable."
def lock
require_relative "cli/lock"
Expand Down
2 changes: 1 addition & 1 deletion lib/bundler/cli/update.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def run

update_bundler = options[:bundler]

Bundler.self_manager.update_bundler_and_restart_with_it_if_needed(update_bundler) if update_bundler
Bundler.self_manager.update_bundler_and_restart_with_it_if_needed(update_bundler, pre: options[:pre]) if update_bundler

sources = Array(options[:source])
groups = Array(options[:group]).map(&:to_sym)
Expand Down
2 changes: 1 addition & 1 deletion lib/bundler/man/bundle-lock.1
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Lock the gems specified in Gemfile\.
Ignores the existing lockfile\. Resolve then updates lockfile\. Taking a list of gems or updating all gems if no list is given\.
.TP
\fB\-\-bundler[=BUNDLER]\fR
Update the locked version of bundler to the given version or the latest version if no version is given\.
Update the locked version of bundler\. BUNDLER can be a version such as \fB4\.0\.20\fR, or a requirement such as \fB"> 0\.a"\fR\. With no argument, update to the latest released version, which never selects a prerelease\.
.TP
\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 a appropriate platform\-specific gem exists on \fBrubygems\.org\fR it will not be found\.
Expand Down
5 changes: 3 additions & 2 deletions lib/bundler/man/bundle-lock.1.ronn
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,9 @@ Lock the gems specified in Gemfile.
of gems or updating all gems if no list is given.

* `--bundler[=BUNDLER]`:
Update the locked version of bundler to the given version or the latest
version if no version is given.
Update the locked version of bundler. BUNDLER can be a version such as
`4.0.20`, or a requirement such as `"> 0.a"`. With no argument, update to the
latest released version, which never selects a prerelease.

* `--local`:
Do not attempt to connect to `rubygems.org`. Instead, Bundler will use the
Expand Down
2 changes: 1 addition & 1 deletion lib/bundler/man/bundle-update.1
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ Do not attempt to fetch gems remotely and use the gem cache instead\.
Update the locked version of Ruby to the current version of Ruby\.
.TP
\fB\-\-bundler[=BUNDLER]\fR
Update the locked version of bundler to the invoked bundler version\.
Update the locked version of bundler\. BUNDLER can be a version such as \fB4\.0\.20\fR, or a requirement such as \fB"> 0\.a"\fR\. With no argument, update to the latest released version, which never selects a prerelease\.
.TP
\fB\-\-force\fR, \fB\-\-redownload\fR
Force reinstalling every gem, even if already installed\.
Expand Down
4 changes: 3 additions & 1 deletion lib/bundler/man/bundle-update.1.ronn
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,9 @@ gem.
Update the locked version of Ruby to the current version of Ruby.

* `--bundler[=BUNDLER]`:
Update the locked version of bundler to the invoked bundler version.
Update the locked version of bundler. BUNDLER can be a version such as
`4.0.20`, or a requirement such as `"> 0.a"`. With no argument, update to the
latest released version, which never selects a prerelease.

* `--force`, `--redownload`:
Force reinstalling every gem, even if already installed.
Expand Down
24 changes: 15 additions & 9 deletions lib/bundler/self_manager.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ def install_locked_bundler_and_restart_with_it_if_needed
install_and_restart_with(restart_version)
end

def update_bundler_and_restart_with_it_if_needed(target)
spec = resolve_update_version_from(target)
def update_bundler_and_restart_with_it_if_needed(target, pre: false)
spec = resolve_update_version_from(target, pre: pre)
return unless spec

version = spec.version
Expand Down Expand Up @@ -108,9 +108,9 @@ def autoswitching_applies?
lockfile_version
end

def resolve_update_version_from(target)
def resolve_update_version_from(target, pre: false)
requirement = Gem::Requirement.new(target)
update_candidate = find_latest_matching_spec(requirement)
update_candidate = find_latest_matching_spec(requirement, pre: pre)

if update_candidate.nil?
raise InvalidOption, "The `bundle update --bundler` target version (#{target}) does not exist"
Expand All @@ -137,18 +137,24 @@ def remote_specs
end
end

def find_latest_matching_spec(requirement)
def find_latest_matching_spec(requirement, pre: false)
Bundler.configure
local_result = find_latest_matching_spec_from_collection(local_specs, requirement)
# A bare `bundle update --bundler` must stay on releases, like `gem update
# --system`, so only `--pre` or a prerelease requirement opts into one.
allow_prerelease = pre || requirement.prerelease?

local_result = find_latest_matching_spec_from_collection(local_specs, requirement, allow_prerelease)
return local_result if local_result && requirement.specific?

remote_result = find_latest_matching_spec_from_collection(remote_specs, requirement)
remote_result = find_latest_matching_spec_from_collection(remote_specs, requirement, allow_prerelease)
return remote_result if local_result.nil?

[local_result, remote_result].max
[local_result, remote_result].compact.max
end

def find_latest_matching_spec_from_collection(specs, requirement)
def find_latest_matching_spec_from_collection(specs, requirement, allow_prerelease)
specs = specs.reject {|spec| spec.version.prerelease? } unless allow_prerelease

specs.sort.reverse_each.find {|spec| requirement.satisfied_by?(spec.version) }
end

Expand Down
20 changes: 20 additions & 0 deletions spec/commands/lock_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -886,6 +886,26 @@
expect(lockfile).to end_with("BUNDLED WITH\n 99\n")
end

it "does not update the bundler version in the lockfile to a prerelease version, unless the target version allows prereleases" do
build_repo4 do
build_gem "bundler", "55"
build_gem "bundler", "56.0.0.beta1"
end

system_gems "bundler-55", gem_repo: gem_repo4

install_gemfile <<-G, artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
source "https://gem.repo4"
G
lockfile lockfile.sub(/(^\s*)#{Bundler::VERSION}($)/, '\11.0.0\2')

bundle "lock --update --bundler --verbose", artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
expect(lockfile).to end_with("BUNDLED WITH\n 55\n")

bundle "lock --update --bundler '> 0.a' --verbose", artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
expect(lockfile).to end_with("BUNDLED WITH\n 56.0.0.beta1\n")
end

it "supports adding new platforms when there's no previous lockfile" do
gemfile_with_rails_weakling_and_foo_from_repo4

Expand Down
176 changes: 175 additions & 1 deletion spec/commands/update_scenarios_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -466,7 +466,7 @@
bundle :update, bundler: true, verbose: true

expect(out).to include("Updating bundler to 999.0.0")
expect(out).to include("Running `bundle update --bundler \"> 0.a\" --verbose` with bundler 999.0.0")
expect(out).to include("Running `bundle update --bundler \">= 999.0.0\" --verbose` with bundler 999.0.0")
expect(out).not_to include("Installing Bundler 2.99.9 and restarting using that version.")

expect(lockfile).to eq <<~L
Expand Down Expand Up @@ -578,6 +578,180 @@
expect(out).to include("myrack (1.0)")
end

it "does not update the bundler version in the lockfile to a prerelease version", :ruby_repo do
pristine_system_gems "bundler-9.9.9"

build_repo4 do
build_gem "myrack", "1.0"

build_bundler "9.9.9"
build_bundler "999.0.0.beta1"
end

checksums = checksums_section do |c|
c.checksum(gem_repo4, "myrack", "1.0")
c.checksum(gem_repo4, "bundler", "9.9.9")
end

install_gemfile <<-G
source "https://gem.repo4"
gem "myrack"
G

bundle :update, bundler: true, verbose: true

expect(out).to include("Using bundler 9.9.9")

expect(lockfile).to eq <<~L
GEM
remote: https://gem.repo4/
specs:
myrack (1.0)

PLATFORMS
#{lockfile_platforms}

DEPENDENCIES
myrack
#{checksums}
BUNDLED WITH
9.9.9
L
end

it "updates the bundler version in the lockfile to a prerelease version when the target version allows prereleases", :ruby_repo do
bundle_config "path.system true"

pristine_system_gems "bundler-9.0.0"

build_repo4 do
build_gem "myrack", "1.0"

build_bundler "999.0.0.beta1"
end

checksums = checksums_section do |c|
c.checksum(gem_repo4, "myrack", "1.0")
c.checksum(gem_repo4, "bundler", "999.0.0.beta1")
end

install_gemfile <<-G
source "https://gem.repo4"
gem "myrack"
G

bundle "update --bundler '> 0.a' --verbose"

expect(out).to include("Updating bundler to 999.0.0.beta1")

expect(lockfile).to eq <<~L
GEM
remote: https://gem.repo4/
specs:
myrack (1.0)

PLATFORMS
#{lockfile_platforms}

DEPENDENCIES
myrack
#{checksums}
BUNDLED WITH
999.0.0.beta1
L
end

it "goes back to a released version given explicitly when the lockfile is locked to a prerelease", :ruby_repo do
bundle_config "path.system true"

pristine_system_gems "bundler-9.0.0.beta1"

build_repo4 do
build_gem "myrack", "1.0"

build_bundler "9.0.0"
end

checksums = checksums_section do |c|
c.checksum(gem_repo4, "myrack", "1.0")
c.checksum(gem_repo4, "bundler", "9.0.0")
end

install_gemfile <<-G
source "https://gem.repo4"
gem "myrack"
G

# Auto switching puts the beta back in charge on every command, so an
# explicit target is the only way out of a lockfile that names one.
expect(lockfile).to match(/BUNDLED WITH\n\s+9\.0\.0\.beta1\n/)

bundle "update --bundler 9.0.0 --verbose"

expect(out).to include("Updating bundler to 9.0.0")

expect(lockfile).to eq <<~L
GEM
remote: https://gem.repo4/
specs:
myrack (1.0)

PLATFORMS
#{lockfile_platforms}

DEPENDENCIES
myrack
#{checksums}
BUNDLED WITH
9.0.0
L

bundle "--version"
expect(out).to include("9.0.0")
end

it "updates the bundler version in the lockfile to a prerelease version when --pre is given", :ruby_repo do
bundle_config "path.system true"

pristine_system_gems "bundler-9.0.0"

build_repo4 do
build_gem "myrack", "1.0"

build_bundler "999.0.0.beta1"
end

checksums = checksums_section do |c|
c.checksum(gem_repo4, "myrack", "1.0")
c.checksum(gem_repo4, "bundler", "999.0.0.beta1")
end

install_gemfile <<-G
source "https://gem.repo4"
gem "myrack"
G

bundle :update, bundler: true, pre: true, verbose: true

expect(out).to include("Updating bundler to 999.0.0.beta1")

expect(lockfile).to eq <<~L
GEM
remote: https://gem.repo4/
specs:
myrack (1.0)

PLATFORMS
#{lockfile_platforms}

DEPENDENCIES
myrack
#{checksums}
BUNDLED WITH
999.0.0.beta1
L
end

it "errors if the explicit target version does not exist" do
pristine_system_gems "bundler-9.9.9"

Expand Down