Skip to content

Commit 2e28234

Browse files
Merge pull request #7047 from rubygems/refactor-lockfile-generation
Refactor lockfile generation and deprecate `Definition#lock` with explicit lockfile
2 parents 7cb43e2 + 7f2f2b8 commit 2e28234

File tree

7 files changed

+76
-51
lines changed

7 files changed

+76
-51
lines changed

bundler/lib/bundler.rb

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -200,12 +200,13 @@ def environment
200200
#
201201
# @param unlock [Hash, Boolean, nil] Gems that have been requested
202202
# to be updated or true if all gems should be updated
203+
# @param lockfile [Pathname] Path to Gemfile.lock
203204
# @return [Bundler::Definition]
204-
def definition(unlock = nil)
205+
def definition(unlock = nil, lockfile = default_lockfile)
205206
@definition = nil if unlock
206207
@definition ||= begin
207208
configure
208-
Definition.build(default_gemfile, default_lockfile, unlock)
209+
Definition.build(default_gemfile, lockfile, unlock)
209210
end
210211
end
211212

bundler/lib/bundler/cli/lock.rb

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,11 @@ def run
3333
update = { bundler: bundler }
3434
end
3535

36+
file = options[:lockfile]
37+
file = file ? Pathname.new(file).expand_path : Bundler.default_lockfile
38+
3639
Bundler.settings.temporary(frozen: false) do
37-
definition = Bundler.definition(update)
40+
definition = Bundler.definition(update, file)
3841

3942
Bundler::CLI::Common.configure_gem_version_promoter(definition, options) if options[:update]
4043

@@ -60,10 +63,8 @@ def run
6063
if print
6164
puts definition.to_lock
6265
else
63-
file = options[:lockfile]
64-
file = file ? File.expand_path(file) : Bundler.default_lockfile
6566
puts "Writing lockfile to #{file}"
66-
definition.lock(file)
67+
definition.lock
6768
end
6869
end
6970

bundler/lib/bundler/definition.rb

Lines changed: 54 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -320,38 +320,26 @@ def groups
320320
dependencies.map(&:groups).flatten.uniq
321321
end
322322

323-
def lock(file, preserve_unknown_sections = false)
324-
return if Definition.no_lock
325-
326-
contents = to_lock
327-
328-
# Convert to \r\n if the existing lock has them
329-
# i.e., Windows with `git config core.autocrlf=true`
330-
contents.gsub!(/\n/, "\r\n") if @lockfile_contents.match?("\r\n")
331-
332-
if @locked_bundler_version
333-
locked_major = @locked_bundler_version.segments.first
334-
current_major = bundler_version_to_lock.segments.first
335-
336-
updating_major = locked_major < current_major
337-
end
323+
def lock(file_or_preserve_unknown_sections = false, preserve_unknown_sections_or_unused = false)
324+
if [true, false, nil].include?(file_or_preserve_unknown_sections)
325+
target_lockfile = lockfile || Bundler.default_lockfile
326+
preserve_unknown_sections = file_or_preserve_unknown_sections
327+
else
328+
target_lockfile = file_or_preserve_unknown_sections
329+
preserve_unknown_sections = preserve_unknown_sections_or_unused
338330

339-
preserve_unknown_sections ||= !updating_major && (Bundler.frozen_bundle? || !(unlocking? || @unlocking_bundler))
331+
suggestion = if target_lockfile == lockfile
332+
"To fix this warning, remove it from the `Definition#lock` call."
333+
else
334+
"Instead, instantiate a new definition passing `#{target_lockfile}`, and call `lock` without a file argument on that definition"
335+
end
340336

341-
if file && File.exist?(file) && lockfiles_equal?(@lockfile_contents, contents, preserve_unknown_sections)
342-
return if Bundler.frozen_bundle?
343-
SharedHelpers.filesystem_access(file) { FileUtils.touch(file) }
344-
return
345-
end
337+
msg = "`Definition#lock` was passed a target file argument. #{suggestion}"
346338

347-
if Bundler.frozen_bundle?
348-
Bundler.ui.error "Cannot write a changed lockfile while frozen."
349-
return
339+
Bundler::SharedHelpers.major_deprecation 2, msg
350340
end
351341

352-
SharedHelpers.filesystem_access(file) do |p|
353-
File.open(p, "wb") {|f| f.puts(contents) }
354-
end
342+
write_lock(target_lockfile, preserve_unknown_sections)
355343
end
356344

357345
def locked_ruby_version
@@ -518,7 +506,45 @@ def should_add_extra_platforms?
518506
end
519507

520508
def lockfile_exists?
521-
lockfile && File.exist?(lockfile)
509+
file_exists?(lockfile)
510+
end
511+
512+
def file_exists?(file)
513+
file && File.exist?(file)
514+
end
515+
516+
def write_lock(file, preserve_unknown_sections)
517+
return if Definition.no_lock
518+
519+
contents = to_lock
520+
521+
# Convert to \r\n if the existing lock has them
522+
# i.e., Windows with `git config core.autocrlf=true`
523+
contents.gsub!(/\n/, "\r\n") if @lockfile_contents.match?("\r\n")
524+
525+
if @locked_bundler_version
526+
locked_major = @locked_bundler_version.segments.first
527+
current_major = bundler_version_to_lock.segments.first
528+
529+
updating_major = locked_major < current_major
530+
end
531+
532+
preserve_unknown_sections ||= !updating_major && (Bundler.frozen_bundle? || !(unlocking? || @unlocking_bundler))
533+
534+
if file_exists?(file) && lockfiles_equal?(@lockfile_contents, contents, preserve_unknown_sections)
535+
return if Bundler.frozen_bundle?
536+
SharedHelpers.filesystem_access(file) { FileUtils.touch(file) }
537+
return
538+
end
539+
540+
if Bundler.frozen_bundle?
541+
Bundler.ui.error "Cannot write a changed lockfile while frozen."
542+
return
543+
end
544+
545+
SharedHelpers.filesystem_access(file) do |p|
546+
File.open(p, "wb") {|f| f.puts(contents) }
547+
end
522548
end
523549

524550
def resolver

bundler/lib/bundler/injector.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ def inject(gemfile_path, lockfile_path)
5050
append_to(gemfile_path, build_gem_lines(@options[:conservative_versioning])) if @deps.any?
5151

5252
# since we resolved successfully, write out the lockfile
53-
@definition.lock(Bundler.default_lockfile)
53+
@definition.lock
5454

5555
# invalidate the cached Bundler.definition
5656
Bundler.reset_paths!

bundler/lib/bundler/installer.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -260,8 +260,8 @@ def resolve_if_needed(options)
260260
true
261261
end
262262

263-
def lock(opts = {})
264-
@definition.lock(Bundler.default_lockfile, opts[:preserve_unknown_sections])
263+
def lock
264+
@definition.lock
265265
end
266266
end
267267
end

bundler/lib/bundler/runtime.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ def self.definition_method(meth)
9595

9696
def lock(opts = {})
9797
return if @definition.no_resolve_needed?
98-
@definition.lock(Bundler.default_lockfile, opts[:preserve_unknown_sections])
98+
@definition.lock(opts[:preserve_unknown_sections])
9999
end
100100

101101
alias_method :gems, :specs

bundler/spec/bundler/definition_spec.rb

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,40 +5,37 @@
55
RSpec.describe Bundler::Definition do
66
describe "#lock" do
77
before do
8-
allow(Bundler).to receive(:settings) { Bundler::Settings.new(".") }
9-
allow(Bundler::SharedHelpers).to receive(:find_gemfile) { Pathname.new("Gemfile") }
8+
allow(Bundler::SharedHelpers).to receive(:find_gemfile) { bundled_app_gemfile }
109
allow(Bundler).to receive(:ui) { double("UI", info: "", debug: "") }
1110
end
12-
context "when it's not possible to write to the file" do
13-
subject { Bundler::Definition.new(nil, [], Bundler::SourceList.new, []) }
1411

12+
subject { Bundler::Definition.new(bundled_app_lock, [], Bundler::SourceList.new, {}) }
13+
14+
context "when it's not possible to write to the file" do
1515
it "raises an PermissionError with explanation" do
1616
allow(File).to receive(:open).and_call_original
17-
expect(File).to receive(:open).with("Gemfile.lock", "wb").
17+
expect(File).to receive(:open).with(bundled_app_lock, "wb").
1818
and_raise(Errno::EACCES)
19-
expect { subject.lock("Gemfile.lock") }.
19+
expect { subject.lock }.
2020
to raise_error(Bundler::PermissionError, /Gemfile\.lock/)
2121
end
2222
end
2323
context "when a temporary resource access issue occurs" do
24-
subject { Bundler::Definition.new(nil, [], Bundler::SourceList.new, []) }
25-
2624
it "raises a TemporaryResourceError with explanation" do
2725
allow(File).to receive(:open).and_call_original
28-
expect(File).to receive(:open).with("Gemfile.lock", "wb").
26+
expect(File).to receive(:open).with(bundled_app_lock, "wb").
2927
and_raise(Errno::EAGAIN)
30-
expect { subject.lock("Gemfile.lock") }.
28+
expect { subject.lock }.
3129
to raise_error(Bundler::TemporaryResourceError, /temporarily unavailable/)
3230
end
3331
end
3432
context "when Bundler::Definition.no_lock is set to true" do
35-
subject { Bundler::Definition.new(nil, [], Bundler::SourceList.new, []) }
3633
before { Bundler::Definition.no_lock = true }
3734
after { Bundler::Definition.no_lock = false }
3835

3936
it "does not create a lock file" do
40-
subject.lock("Gemfile.lock")
41-
expect(File.file?("Gemfile.lock")).to eq false
37+
subject.lock
38+
expect(bundled_app_lock).not_to be_file
4239
end
4340
end
4441
end

0 commit comments

Comments
 (0)