Skip to content

Commit ee7ff4a

Browse files
deivid-rodriguezmatzbot
authored andcommitted
[rubygems/rubygems] Backwards compatibility for 2.5.17-2.5.23 caches
rubygems/rubygems@9dbfce76cf
1 parent 9a4d91f commit ee7ff4a

File tree

3 files changed

+131
-11
lines changed

3 files changed

+131
-11
lines changed

lib/bundler/runtime.rb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,11 @@ def cache(custom_path = nil, local = false)
136136
specs_to_cache.each do |spec|
137137
next if spec.name == "bundler"
138138
next if spec.source.is_a?(Source::Gemspec)
139-
spec.source.cache(spec, custom_path) if spec.source.respond_to?(:cache)
139+
if spec.source.respond_to?(:migrate_cache)
140+
spec.source.migrate_cache(custom_path, local: local)
141+
elsif spec.source.respond_to?(:cache)
142+
spec.source.cache(spec, custom_path)
143+
end
140144
end
141145

142146
Dir[cache_path.join("*/.git")].each do |git_dir|

lib/bundler/source/git.rb

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -214,16 +214,16 @@ def install(spec, options = {})
214214
requires_checkout? ? spec.post_install_message : nil
215215
end
216216

217-
def cache(spec, custom_path = nil)
218-
return unless Bundler.feature_flag.cache_all?
219-
220-
app_cache_path = app_cache_path(custom_path)
221-
return if cache_path == app_cache_path
217+
def migrate_cache(custom_path = nil, local: false)
218+
if local
219+
cache_to(custom_path, try_migrate: false)
220+
else
221+
cache_to(custom_path, try_migrate: true)
222+
end
223+
end
222224

223-
cached!
224-
FileUtils.rm_rf(app_cache_path)
225-
git_proxy.checkout if requires_checkout?
226-
git_proxy.copy_to(app_cache_path, @submodules)
225+
def cache(spec, custom_path = nil)
226+
cache_to(custom_path, try_migrate: false)
227227
end
228228

229229
def load_spec_files
@@ -267,14 +267,36 @@ def local?
267267

268268
private
269269

270+
def cache_to(custom_path, try_migrate: false)
271+
return unless Bundler.feature_flag.cache_all?
272+
273+
app_cache_path = app_cache_path(custom_path)
274+
275+
migrate = try_migrate ? bare_repo?(app_cache_path) : false
276+
277+
set_cache_path!(nil) if migrate
278+
279+
return if cache_path == app_cache_path
280+
281+
cached!
282+
FileUtils.rm_rf(app_cache_path)
283+
git_proxy.checkout if migrate || requires_checkout?
284+
git_proxy.copy_to(app_cache_path, @submodules)
285+
end
286+
270287
def checkout
271288
Bundler.ui.debug " * Checking out revision: #{ref}"
272-
if use_app_cache?
289+
if use_app_cache? && !bare_repo?(app_cache_path)
273290
SharedHelpers.filesystem_access(install_path.dirname) do |p|
274291
FileUtils.mkdir_p(p)
275292
end
276293
FileUtils.cp_r("#{app_cache_path}/.", install_path)
277294
else
295+
if use_app_cache? && bare_repo?(app_cache_path)
296+
Bundler.ui.warn "Installing from cache in old \"bare repository\" format for compatibility. " \
297+
"Please run `bundle cache` and commit the updated cache to migrate to the new format and get rid of this warning."
298+
end
299+
278300
git_proxy.copy_to(install_path, submodules)
279301
end
280302
serialize_gemspecs_in(install_path)
@@ -416,6 +438,10 @@ def extension_cache_slug(_)
416438
def override_for(path)
417439
Bundler.settings.local_overrides.key(path)
418440
end
441+
442+
def bare_repo?(path)
443+
File.exist?(path.join("objects")) && File.exist?(path.join("HEAD"))
444+
end
419445
end
420446
end
421447
end

spec/bundler/cache/git_spec.rb

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,96 @@
239239
expect(the_bundle).to include_gem "foo 1.0"
240240
end
241241

242+
it "installs properly a bundler 2.5.17-2.5.23 cache as a bare repository without cloning remote repositories" do
243+
git = build_git "foo"
244+
245+
short_ref = git.ref_for("main", 11)
246+
cache_dir = bundled_app("vendor/cache/foo-1.0-#{short_ref}")
247+
248+
gemfile <<-G
249+
source "https://gem.repo1"
250+
gem "foo", :git => '#{lib_path("foo-1.0")}'
251+
G
252+
bundle "config set global_gem_cache false"
253+
bundle "config set cache_all true"
254+
bundle "config path vendor/bundle"
255+
bundle :install
256+
257+
# Simulate old cache by copying the real cache folder to vendor/cache
258+
FileUtils.mkdir_p bundled_app("vendor/cache")
259+
FileUtils.cp_r "#{Dir.glob(vendored_gems("cache/bundler/git/foo-1.0-*")).first}/.", cache_dir
260+
FileUtils.rm_rf bundled_app("vendor/bundle")
261+
262+
bundle "install --local --verbose"
263+
expect(err).to include("Installing from cache in old \"bare repository\" format for compatibility")
264+
265+
expect(out).to_not include("Fetching")
266+
267+
# leaves old cache alone
268+
expect(cache_dir.join("lib/foo.rb")).not_to exist
269+
expect(cache_dir.join("HEAD")).to exist
270+
271+
expect(the_bundle).to include_gem "foo 1.0"
272+
end
273+
274+
it "migrates a bundler 2.5.17-2.5.23 cache as a bare repository when not running with --local" do
275+
git = build_git "foo"
276+
277+
short_ref = git.ref_for("main", 11)
278+
cache_dir = bundled_app("vendor/cache/foo-1.0-#{short_ref}")
279+
280+
gemfile <<-G
281+
source "https://gem.repo1"
282+
gem "foo", :git => '#{lib_path("foo-1.0")}'
283+
G
284+
bundle "config set global_gem_cache false"
285+
bundle "config set cache_all true"
286+
bundle "config path vendor/bundle"
287+
bundle :install
288+
289+
# Simulate old cache by copying the real cache folder to vendor/cache
290+
FileUtils.mkdir_p bundled_app("vendor/cache")
291+
FileUtils.cp_r "#{Dir.glob(vendored_gems("cache/bundler/git/foo-1.0-*")).first}/.", cache_dir
292+
FileUtils.rm_rf bundled_app("vendor/bundle")
293+
294+
bundle "install --verbose"
295+
expect(out).to include("Fetching")
296+
297+
# migrates old cache alone
298+
expect(cache_dir.join("lib/foo.rb")).to exist
299+
expect(cache_dir.join("HEAD")).not_to exist
300+
301+
expect(the_bundle).to include_gem "foo 1.0"
302+
end
303+
304+
it "migrates a bundler 2.5.17-2.5.23 cache as a bare repository when running `bundle cache`, even if gems already installed" do
305+
git = build_git "foo"
306+
307+
short_ref = git.ref_for("main", 11)
308+
cache_dir = bundled_app("vendor/cache/foo-1.0-#{short_ref}")
309+
310+
gemfile <<-G
311+
source "https://gem.repo1"
312+
gem "foo", :git => '#{lib_path("foo-1.0")}'
313+
G
314+
bundle "config set global_gem_cache false"
315+
bundle "config set cache_all true"
316+
bundle "config path vendor/bundle"
317+
bundle :install
318+
319+
# Simulate old cache by copying the real cache folder to vendor/cache
320+
FileUtils.mkdir_p bundled_app("vendor/cache")
321+
FileUtils.cp_r "#{Dir.glob(vendored_gems("cache/bundler/git/foo-1.0-*")).first}/.", cache_dir
322+
323+
bundle "cache"
324+
325+
# migrates old cache alone
326+
expect(cache_dir.join("lib/foo.rb")).to exist
327+
expect(cache_dir.join("HEAD")).not_to exist
328+
329+
expect(the_bundle).to include_gem "foo 1.0"
330+
end
331+
242332
it "copies repository to vendor cache, including submodules" do
243333
# CVE-2022-39253: https://lore.kernel.org/lkml/[email protected]/
244334
system(*%W[git config --global protocol.file.allow always])

0 commit comments

Comments
 (0)