Skip to content

Commit 446cc57

Browse files
authored
Merge pull request #5761 from rubygems/prefer-local
Implement `bundle install --prefer-local`
2 parents a121616 + f0d6d4c commit 446cc57

File tree

6 files changed

+68
-2
lines changed

6 files changed

+68
-2
lines changed

bundler/lib/bundler/cli.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,8 @@ def remove(*gems)
218218
"Specify the number of jobs to run in parallel"
219219
method_option "local", :type => :boolean, :banner =>
220220
"Do not attempt to fetch gems remotely and use the gem cache instead"
221+
method_option "prefer-local", :type => :boolean, :banner =>
222+
"Only attempt to fetch gems remotely if not present locally, even if newer versions are available remotely"
221223
method_option "no-cache", :type => :boolean, :banner =>
222224
"Don't update the existing gem cache."
223225
method_option "redownload", :type => :boolean, :aliases => "--force", :banner =>

bundler/lib/bundler/definition.rb

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ def initialize(lockfile, dependencies, sources, unlock, ruby_version = nil, opti
7070
@unlock = unlock
7171
@optional_groups = optional_groups
7272
@remote = false
73+
@prefer_local = false
7374
@specs = nil
7475
@ruby_version = ruby_version
7576
@gemfiles = gemfiles
@@ -170,6 +171,13 @@ def resolve_only_locally!
170171
resolve
171172
end
172173

174+
def resolve_prefering_local!
175+
@prefer_local = true
176+
@remote = true
177+
sources.remote!
178+
resolve
179+
end
180+
173181
def resolve_with_cache!
174182
sources.cached!
175183
resolve
@@ -528,6 +536,19 @@ def precompute_source_requirements_for_indirect_dependencies?
528536
@remote && sources.non_global_rubygems_sources.all?(&:dependency_api_available?) && !sources.aggregate_global_source?
529537
end
530538

539+
def pin_locally_available_names(source_requirements)
540+
source_requirements.each_with_object({}) do |(name, original_source), new_source_requirements|
541+
local_source = original_source.dup
542+
local_source.local_only!
543+
544+
new_source_requirements[name] = if local_source.specs.search(name).any?
545+
local_source
546+
else
547+
original_source
548+
end
549+
end
550+
end
551+
531552
def current_ruby_platform_locked?
532553
return false unless generic_local_platform == Gem::Platform::RUBY
533554
return false if Bundler.settings[:force_ruby_platform] && !@platforms.include?(Gem::Platform::RUBY)
@@ -792,7 +813,9 @@ def source_requirements
792813
# specs will be available later when the resolver knows where to
793814
# look for that gemspec (or its dependencies)
794815
source_requirements = if precompute_source_requirements_for_indirect_dependencies?
795-
{ :default => sources.default_source }.merge(source_map.all_requirements)
816+
all_requirements = source_map.all_requirements
817+
all_requirements = pin_locally_available_names(all_requirements) if @prefer_local
818+
{ :default => sources.default_source }.merge(all_requirements)
796819
else
797820
{ :default => Source::RubygemsAggregate.new(sources, source_map) }.merge(source_map.direct_requirements)
798821
end

bundler/lib/bundler/installer.rb

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,14 @@ def resolve_if_needed(options)
268268
return false if @definition.nothing_changed? && !@definition.missing_specs?
269269
end
270270

271-
options["local"] ? @definition.resolve_with_cache! : @definition.resolve_remotely!
271+
if options["local"]
272+
@definition.resolve_with_cache!
273+
elsif options["prefer-local"]
274+
@definition.resolve_prefering_local!
275+
else
276+
@definition.resolve_remotely!
277+
end
278+
272279
true
273280
end
274281

bundler/lib/bundler/man/bundle-install.1

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,10 @@ The maximum number of parallel download and install jobs\. The default is the nu
7070
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\.
7171
.
7272
.TP
73+
\fB\-\-prefer\-local\fR
74+
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\.
75+
.
76+
.TP
7377
\fB\-\-no\-cache\fR
7478
Do not update the cache in \fBvendor/cache\fR with the newly bundled gems\. This does not remove any gems in the cache but keeps the newly bundled gems from being cached during the install\.
7579
.

bundler/lib/bundler/man/bundle-install.1.ronn

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,12 @@ automatically and that requires `bundler` to silently remember them. Since
109109
appropriate platform-specific gem exists on `rubygems.org` it will not be
110110
found.
111111

112+
* `--prefer-local`:
113+
Force using locally installed gems, or gems already present in Rubygems' cache
114+
or in `vendor/cache`, when resolving, even if newer versions are available
115+
remotely. Only attempt to connect to `rubygems.org` for gems that are not
116+
present locally.
117+
112118
* `--no-cache`:
113119
Do not update the cache in `vendor/cache` with the newly bundled gems. This
114120
does not remove any gems in the cache but keeps the newly bundled gems from

bundler/spec/commands/install_spec.rb

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1020,6 +1020,30 @@ def run
10201020
end
10211021
end
10221022

1023+
context "with --prefer-local flag" do
1024+
before do
1025+
build_repo4 do
1026+
build_gem "foo", "1.0.1"
1027+
build_gem "foo", "1.0.0"
1028+
build_gem "bar", "1.0.0"
1029+
end
1030+
1031+
system_gems "foo-1.0.0", :path => default_bundle_path, :gem_repo => gem_repo4
1032+
end
1033+
1034+
it "fetches remote sources only when not available locally" do
1035+
install_gemfile <<-G, :"prefer-local" => true, :verbose => true
1036+
source "#{file_uri_for(gem_repo4)}"
1037+
1038+
gem "foo"
1039+
gem "bar"
1040+
G
1041+
1042+
expect(out).to include("Using foo 1.0.0").and include("Fetching bar 1.0.0").and include("Installing bar 1.0.0")
1043+
expect(last_command).to be_success
1044+
end
1045+
end
1046+
10231047
context "with a symlinked configured as bundle path and a gem with symlinks" do
10241048
before do
10251049
symlinked_bundled_app = tmp("bundled_app-symlink")

0 commit comments

Comments
 (0)