bundler-multilock 1.0.11 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 16c87f72f551998a90bed072f8c103a9870aad116cd7f2fd97226f64d64524e9
4
- data.tar.gz: f3a25f8165c3c7e3169a6f03ce812572dbe95ddab0901d6af5110c10b631eba2
3
+ metadata.gz: 12e431ea7783f9bc36d1efae8aaeda88434936f587c954c34ad4163b3338dc44
4
+ data.tar.gz: 8eda2e0bc8f785b0ceefb312d4d34c6fd754dab58c835ec7b818c551af903bdf
5
5
  SHA512:
6
- metadata.gz: e6b8780298dceaa4181dd6e524906af947fe26842acca882c4f06a9e6b84dcee2b479b00a88375faec133cf7d429a136f2cafe5200354f7342317088afdbae75
7
- data.tar.gz: 5824ecc485b0934497beba52e5b6c554a1e990cdee7ab9db79fe24e1e8ff547a07c32390eef2470c1c8e93a6545e7ace99b115ac830266cdbe2151c492eff8fd
6
+ metadata.gz: 3fea018e198880d1c2a7dcaa635faa1ccff9b65d46bbb72c15cb153dd20acf34dfb833189bd1c4f3dd04f3053b23e2220ca769b101806e2b99a3ddc92097675c
7
+ data.tar.gz: 80095ec510ebefa11c457ef13c45bc0af40b6961b1ffff54c0724f5010cd72b6fa512f3de11155a6883db71dd6dc87322c6fbef314d5b7c61e99572e4fe77b43
@@ -5,6 +5,8 @@ require "set"
5
5
  module Bundler
6
6
  module Multilock
7
7
  class Check
8
+ attr_reader :lockfiles, :lockfile_contents, :lockfile_specs
9
+
8
10
  class << self
9
11
  def run
10
12
  new.run
@@ -12,23 +14,32 @@ module Bundler
12
14
  end
13
15
 
14
16
  def initialize
15
- default_lockfile_contents = Bundler.default_lockfile.read
16
- @default_lockfile = LockfileParser.new(default_lockfile_contents)
17
- @default_specs = @default_lockfile.specs.to_h do |spec|
17
+ @lockfiles = {}
18
+ @lockfile_contents = {}
19
+ @lockfile_specs = {}
20
+ end
21
+
22
+ def load_lockfile(lockfile)
23
+ return if lockfile_contents.key?(lockfile)
24
+
25
+ contents = lockfile_contents[lockfile] = lockfile.read.freeze
26
+ parser = lockfiles[lockfile] = LockfileParser.new(contents)
27
+ lockfile_specs[lockfile] = parser.specs.to_h do |spec|
18
28
  [[spec.name, spec.platform], spec]
19
29
  end
20
30
  end
21
31
 
22
32
  def run(skip_base_checks: false)
23
- return true unless Bundler.default_lockfile.exist?
33
+ return true unless Bundler.default_lockfile(force_original: true).exist?
24
34
 
25
35
  success = true
26
36
  unless skip_base_checks
27
- missing_specs = base_check({ gemfile: Bundler.default_gemfile, lockfile: Bundler.default_lockfile },
37
+ missing_specs = base_check({ gemfile: Bundler.default_gemfile,
38
+ lockfile: Bundler.default_lockfile(force_original: true) },
28
39
  return_missing: true).to_set
29
40
  end
30
41
  Multilock.lockfile_definitions.each do |lockfile_definition|
31
- next if lockfile_definition[:lockfile] == Bundler.default_lockfile
42
+ next if lockfile_definition[:lockfile] == Bundler.default_lockfile(force_original: true)
32
43
 
33
44
  unless lockfile_definition[:lockfile].exist?
34
45
  Bundler.ui.error("Lockfile #{lockfile_definition[:lockfile]} does not exist.")
@@ -77,7 +88,7 @@ module Bundler
77
88
  Multilock.prepare_block = nil
78
89
  end
79
90
 
80
- # this checks for mismatches between the default lockfile and the given lockfile,
91
+ # this checks for mismatches between the parent lockfile and the given lockfile,
81
92
  # and for pinned dependencies in lockfiles requiring them
82
93
  def check(lockfile_definition, allow_mismatched_dependencies: true)
83
94
  success = true
@@ -85,13 +96,16 @@ module Bundler
85
96
  needs_pin_check = []
86
97
  lockfile = LockfileParser.new(lockfile_definition[:lockfile].read)
87
98
  lockfile_path = lockfile_definition[:lockfile].relative_path_from(Dir.pwd)
88
- unless lockfile.platforms == @default_lockfile.platforms
89
- Bundler.ui.error("The platforms in #{lockfile_path} do not match the default lockfile.")
99
+ parent = lockfile_definition[:parent]
100
+ load_lockfile(parent)
101
+ parent_lockfile = lockfiles[parent]
102
+ unless lockfile.platforms == parent_lockfile.platforms
103
+ Bundler.ui.error("The platforms in #{lockfile_path} do not match the parent lockfile.")
90
104
  success = false
91
105
  end
92
- unless lockfile.bundler_version == @default_lockfile.bundler_version
106
+ unless lockfile.bundler_version == parent_lockfile.bundler_version
93
107
  Bundler.ui.error("bundler (#{lockfile.bundler_version}) in #{lockfile_path} " \
94
- "does not match the default lockfile's version (@#{@default_lockfile.bundler_version}).")
108
+ "does not match the parent lockfile's version (@#{parent_lockfile.bundler_version}).")
95
109
  success = false
96
110
  end
97
111
 
@@ -100,13 +114,13 @@ module Bundler
100
114
  allow_mismatched_dependencies = lockfile_definition[:allow_mismatched_dependencies]
101
115
  end
102
116
 
103
- # build list of top-level dependencies that differ from the default lockfile,
117
+ # build list of top-level dependencies that differ from the parent lockfile,
104
118
  # and all _their_ transitive dependencies
105
119
  if allow_mismatched_dependencies
106
120
  transitive_dependencies = Set.new
107
- # only dependencies that differ from the default lockfile
121
+ # only dependencies that differ from the parent lockfile
108
122
  pending_transitive_dependencies = lockfile.dependencies.reject do |name, dep|
109
- @default_lockfile.dependencies[name] == dep
123
+ parent_lockfile.dependencies[name] == dep
110
124
  end.map(&:first)
111
125
 
112
126
  until pending_transitive_dependencies.empty?
@@ -133,40 +147,40 @@ module Bundler
133
147
 
134
148
  # check for conflicting requirements (and build list of pins, in the same loop)
135
149
  specs.values.flatten.each do |spec|
136
- default_spec = @default_specs[[spec.name, spec.platform]]
150
+ parent_spec = lockfile_specs[parent][[spec.name, spec.platform]]
137
151
 
138
152
  if lockfile_definition[:enforce_pinned_additional_dependencies]
139
153
  # look through what this spec depends on, and keep track of all pinned requirements
140
154
  find_pinned_dependencies(proven_pinned, spec.dependencies)
141
155
 
142
- needs_pin_check << spec unless default_spec
156
+ needs_pin_check << spec unless parent_spec
143
157
  end
144
158
 
145
- next unless default_spec
159
+ next unless parent_spec
146
160
 
147
161
  # have to ensure Path sources are relative to their lockfile before comparing
148
- same_source = if [default_spec.source, spec.source].grep(Source::Path).length == 2
162
+ same_source = if [parent_spec.source, spec.source].grep(Source::Path).length == 2
149
163
  lockfile_definition[:lockfile]
150
164
  .dirname
151
165
  .join(spec.source.path)
152
166
  .ascend
153
- .any?(Bundler.default_lockfile.dirname.join(default_spec.source.path))
167
+ .any?(parent.dirname.join(parent_spec.source.path))
154
168
  else
155
- default_spec.source == spec.source
169
+ parent_spec.source == spec.source
156
170
  end
157
171
 
158
- next if default_spec.version == spec.version && same_source
172
+ next if parent_spec.version == spec.version && same_source
159
173
  next if allow_mismatched_dependencies && transitive_dependencies.include?(spec.name)
160
174
 
161
175
  Bundler.ui.error("#{spec}#{spec.git_version} in #{lockfile_path} " \
162
- "does not match the default lockfile's version " \
163
- "(@#{default_spec.version}#{default_spec.git_version}); " \
176
+ "does not match the parent lockfile's version " \
177
+ "(@#{parent_spec.version}#{parent_spec.git_version}); " \
164
178
  "this may be due to a conflicting requirement, which would require manual resolution.")
165
179
  success = false
166
180
  end
167
181
 
168
182
  # now that we have built a list of every gem that is pinned, go through
169
- # the gems that were in this lockfile, but not the default lockfile, and
183
+ # the gems that were in this lockfile, but not the parent lockfile, and
170
184
  # ensure it's pinned _somehow_
171
185
  needs_pin_check.each do |spec|
172
186
  pinned = case spec.source
@@ -183,7 +197,7 @@ module Bundler
183
197
  next if pinned
184
198
 
185
199
  Bundler.ui.error("#{spec} in #{lockfile_path} has not been pinned to a specific version, " \
186
- "which is required since it is not part of the default lockfile.")
200
+ "which is required since it is not part of the parent lockfile.")
187
201
  success = false
188
202
  end
189
203
 
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bundler
4
+ module Multilock
5
+ module Ext
6
+ module SharedHeleprs
7
+ module ClassMethods
8
+ ::Bundler::SharedHelpers.singleton_class.prepend(self)
9
+ ::Bundler::SharedHelpers.instance_variable_set(:@filesystem_accesses, nil)
10
+
11
+ def capture_filesystem_access
12
+ @filesystem_accesses = []
13
+ yield
14
+ @filesystem_accesses
15
+ ensure
16
+ @filesystem_accesses = nil
17
+ end
18
+
19
+ def filesystem_access(path, action = :write)
20
+ @filesystem_accesses << [path, action] if @filesystem_accesses
21
+
22
+ super
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bundler
4
+ module Multilock
5
+ module Ext
6
+ module Source
7
+ ::Bundler::Source.prepend(self)
8
+
9
+ def print_using_message(...)
10
+ return if Bundler.settings[:suppress_install_using_messages]
11
+
12
+ super
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Bundler
4
4
  module Multilock
5
- VERSION = "1.0.11"
5
+ VERSION = "1.1.0"
6
6
  end
7
7
  end
@@ -5,6 +5,8 @@ require_relative "multilock/ext/definition"
5
5
  require_relative "multilock/ext/dsl"
6
6
  require_relative "multilock/ext/plugin"
7
7
  require_relative "multilock/ext/plugin/dsl"
8
+ require_relative "multilock/ext/shared_helpers"
9
+ require_relative "multilock/ext/source"
8
10
  require_relative "multilock/ext/source_list"
9
11
  require_relative "multilock/version"
10
12
 
@@ -20,8 +22,11 @@ module Bundler
20
22
  # @param builder [Dsl] The Bundler DSL
21
23
  # @param gemfile [String, nil]
22
24
  # The Gemfile for this lockfile (defaults to Gemfile)
23
- # @param default [Boolean]
25
+ # @param active [Boolean]
24
26
  # If this lockfile should be the default (instead of Gemfile.lock)
27
+ # BUNDLE_LOCKFILE will still override a lockfile tagged as active
28
+ # @param parent [String] The parent lockfile to sync dependencies from.
29
+ # Also used for comparing enforce_pinned_additional_dependencies against.
25
30
  # @param allow_mismatched_dependencies [true, false]
26
31
  # Allows version differences in dependencies between this lockfile and
27
32
  # the default lockfile. Note that even with this option, only top-level
@@ -32,34 +37,30 @@ module Bundler
32
37
  # default lockfile, enforce that they are pinned.
33
38
  # @yield
34
39
  # Block executed only when this lockfile is active.
35
- # @return [true, false] if the lockfile is the current lockfile
40
+ # @return [true, false] if the lockfile is the active lockfile
36
41
  def add_lockfile(lockfile = nil,
37
42
  builder:,
38
43
  gemfile: nil,
44
+ active: nil,
39
45
  default: nil,
46
+ parent: nil,
40
47
  allow_mismatched_dependencies: true,
41
48
  enforce_pinned_additional_dependencies: false,
42
49
  &block)
43
- # terminology gets confusing here. The "default" param means
44
- # "use this lockfile when not overridden by BUNDLE_LOCKFILE"
45
- # but Bundler.defaul_lockfile (usually) means "Gemfile.lock"
46
- # so refer to the former as "current" internally
47
- current = default
48
- current = true if current.nil? && lockfile_definitions.empty? && lockfile.nil? && gemfile.nil?
50
+ # backcompat
51
+ active = default if active.nil?
52
+ Bundler.ui.warn("lockfile(default:) is deprecated. Use lockfile(active:) instead.") if default
53
+
54
+ active = true if active.nil? && lockfile_definitions.empty? && lockfile.nil? && gemfile.nil?
49
55
 
50
- # allow short-form lockfile names
51
- if lockfile.is_a?(String) && !(lockfile.include?("/") || lockfile.end_with?(".lock"))
52
- lockfile = "Gemfile.#{lockfile}.lock"
53
- end
54
56
  # if a gemfile was provided, but not a lockfile, infer the default lockfile for that gemfile
55
57
  lockfile ||= "#{gemfile}.lock" if gemfile
56
- # use absolute paths
57
- lockfile = Bundler.root.join(lockfile).expand_path if lockfile
58
- # use the default lockfile (Gemfile.lock) if none was given
59
- lockfile ||= Bundler.default_lockfile(force_original: true)
60
- raise ArgumentError, "Lockfile #{lockfile} is already defined" if lockfile_definitions.any? do |definition|
61
- definition[:lockfile] == lockfile
62
- end
58
+ # allow short-form lockfile names
59
+ lockfile = expand_lockfile(lockfile)
60
+
61
+ if lockfile_definitions.find { |definition| definition[:lockfile] == lockfile }
62
+ raise ArgumentError, "Lockfile #{lockfile} is already defined"
63
+ end
63
64
 
64
65
  env_lockfile = ENV["BUNDLE_LOCKFILE"]
65
66
  if env_lockfile
@@ -67,18 +68,25 @@ module Bundler
67
68
  env_lockfile = "Gemfile.#{env_lockfile}.lock"
68
69
  end
69
70
  env_lockfile = Bundler.root.join(env_lockfile).expand_path
70
- current = env_lockfile == lockfile
71
+ active = env_lockfile == lockfile
72
+ end
73
+
74
+ if active && (old_active = lockfile_definitions.find { |definition| definition[:active] })
75
+ raise ArgumentError, "Only one lockfile (#{old_active[:lockfile]}) can be flagged as the default"
71
76
  end
72
77
 
73
- if current && (old_current = lockfile_definitions.find { |definition| definition[:current] })
74
- raise ArgumentError, "Only one lockfile (#{old_current[:lockfile]}) can be flagged as the default"
78
+ parent = expand_lockfile(parent)
79
+ if parent != Bundler.default_lockfile(force_original: true) &&
80
+ !lockfile_definitions.find { |definition| definition[:lockfile] == parent }
81
+ raise ArgumentError, "Parent lockfile #{parent} is not defined"
75
82
  end
76
83
 
77
84
  lockfile_definitions << (lockfile_def = {
78
85
  gemfile: (gemfile && Bundler.root.join(gemfile).expand_path) || Bundler.default_gemfile,
79
86
  lockfile: lockfile,
80
- current: current,
87
+ active: active,
81
88
  prepare: block,
89
+ parent: parent,
82
90
  allow_mismatched_dependencies: allow_mismatched_dependencies,
83
91
  enforce_pinned_additional_dependencies: enforce_pinned_additional_dependencies
84
92
  })
@@ -94,10 +102,10 @@ module Bundler
94
102
  # If they're using BUNDLE_LOCKFILE, then they really do want to
95
103
  # use a particular lockfile, and it overrides whatever they
96
104
  # dynamically set in their gemfile
97
- current = lockfile == Bundler.default_lockfile(force_original: true)
105
+ active = lockfile == Bundler.default_lockfile(force_original: true)
98
106
  end
99
107
 
100
- if current
108
+ if active
101
109
  block&.call
102
110
  Bundler.default_lockfile = lockfile
103
111
 
@@ -146,16 +154,13 @@ module Bundler
146
154
  require "tempfile"
147
155
  require_relative "multilock/lockfile_generator"
148
156
 
157
+ Bundler.ui.debug("Syncing to alternate lockfiles")
149
158
  Bundler.ui.info ""
150
159
 
151
- default_lockfile_contents = Bundler.default_lockfile.read.freeze
152
- default_specs = LockfileParser.new(default_lockfile_contents).specs.to_h do |spec|
153
- [[spec.name, spec.platform], spec]
154
- end
155
- default_root = Bundler.root
156
-
157
160
  attempts = 1
158
161
 
162
+ default_root = Bundler.root
163
+
159
164
  checker = Check.new
160
165
  synced_any = false
161
166
  Bundler.settings.temporary(cache_all_platforms: true, suppress_install_using_messages: true) do
@@ -196,21 +201,26 @@ module Bundler
196
201
  Bundler.ui.info("Syncing to #{relative_lockfile}...") if attempts == 1
197
202
  synced_any = true
198
203
 
199
- # adjust locked paths from the default lockfile to be relative to _this_ gemfile
200
- adjusted_default_lockfile_contents =
201
- default_lockfile_contents.gsub(/PATH\n remote: ([^\n]+)\n/) do |remote|
204
+ parent = lockfile_definition[:parent]
205
+ parent_root = parent.dirname
206
+ checker.load_lockfile(parent)
207
+ parent_specs = checker.lockfile_specs[parent]
208
+
209
+ # adjust locked paths from the parent lockfile to be relative to _this_ gemfile
210
+ adjusted_parent_lockfile_contents =
211
+ checker.lockfile_contents[parent].gsub(/PATH\n remote: ([^\n]+)\n/) do |remote|
202
212
  remote_path = Pathname.new($1)
203
213
  next remote if remote_path.absolute?
204
214
 
205
- relative_remote_path = remote_path.expand_path(default_root).relative_path_from(Bundler.root).to_s
215
+ relative_remote_path = remote_path.expand_path(parent_root).relative_path_from(Bundler.root).to_s
206
216
  remote.sub($1, relative_remote_path)
207
217
  end
208
218
 
209
219
  # add a source for the current gem
210
- gem_spec = default_specs[[File.basename(Bundler.root), "ruby"]]
220
+ gem_spec = parent_specs[[File.basename(Bundler.root), "ruby"]]
211
221
 
212
222
  if gem_spec
213
- adjusted_default_lockfile_contents += <<~TEXT
223
+ adjusted_parent_lockfile_contents += <<~TEXT
214
224
  PATH
215
225
  remote: .
216
226
  specs:
@@ -220,39 +230,39 @@ module Bundler
220
230
 
221
231
  if lockfile_definition[:lockfile].exist?
222
232
  # if the lockfile already exists, "merge" it together
223
- default_lockfile = LockfileParser.new(adjusted_default_lockfile_contents)
233
+ parent_lockfile = LockfileParser.new(adjusted_parent_lockfile_contents)
224
234
  lockfile = LockfileParser.new(lockfile_definition[:lockfile].read)
225
235
 
226
236
  dependency_changes = false
227
237
  # replace any duplicate specs with what's in the default lockfile
228
238
  lockfile.specs.map! do |spec|
229
- default_spec = default_specs[[spec.name, spec.platform]]
230
- next spec unless default_spec
239
+ parent_spec = parent_specs[[spec.name, spec.platform]]
240
+ next spec unless parent_spec
231
241
 
232
- dependency_changes ||= spec != default_spec
233
- default_spec
242
+ dependency_changes ||= spec != parent_spec
243
+ parent_spec
234
244
  end
235
245
 
236
- lockfile.specs.replace(default_lockfile.specs + lockfile.specs).uniq!
237
- lockfile.sources.replace(default_lockfile.sources + lockfile.sources).uniq!
238
- lockfile.platforms.replace(default_lockfile.platforms).uniq!
246
+ lockfile.specs.replace(parent_lockfile.specs + lockfile.specs).uniq!
247
+ lockfile.sources.replace(parent_lockfile.sources + lockfile.sources).uniq!
248
+ lockfile.platforms.replace(parent_lockfile.platforms).uniq!
239
249
  # prune more specific platforms
240
250
  lockfile.platforms.delete_if do |p1|
241
251
  lockfile.platforms.any? do |p2|
242
252
  p2 != "ruby" && p1 != p2 && MatchPlatform.platforms_match?(p2, p1)
243
253
  end
244
254
  end
245
- lockfile.instance_variable_set(:@ruby_version, default_lockfile.ruby_version)
246
- unless lockfile.bundler_version == default_lockfile.bundler_version
255
+ lockfile.instance_variable_set(:@ruby_version, parent_lockfile.ruby_version)
256
+ unless lockfile.bundler_version == parent_lockfile.bundler_version
247
257
  unlocking_bundler = true
248
- lockfile.instance_variable_set(:@bundler_version, default_lockfile.bundler_version)
258
+ lockfile.instance_variable_set(:@bundler_version, parent_lockfile.bundler_version)
249
259
  end
250
260
 
251
261
  new_contents = LockfileGenerator.generate(lockfile)
252
262
  else
253
- # no lockfile? just start out with the default lockfile's contents to inherit its
263
+ # no lockfile? just start out with the parent lockfile's contents to inherit its
254
264
  # locked gems
255
- new_contents = adjusted_default_lockfile_contents
265
+ new_contents = adjusted_parent_lockfile_contents
256
266
  end
257
267
 
258
268
  had_changes = false
@@ -273,8 +283,9 @@ module Bundler
273
283
  # once to reset them back to the default lockfile's version.
274
284
  # if it's already good, the `check` check at the beginning of
275
285
  # the loop will skip the second sync anyway.
276
- if had_changes && attempts < 3
286
+ if had_changes && attempts < 2
277
287
  attempts += 1
288
+ Bundler.ui.debug("Re-running sync to #{relative_lockfile} to reset common dependencies")
278
289
  redo
279
290
  else
280
291
  attempts = 1
@@ -297,9 +308,9 @@ module Bundler
297
308
  @loaded = true
298
309
  return if lockfile_definitions.empty?
299
310
 
300
- return unless lockfile_definitions.none? { |definition| definition[:current] }
311
+ return unless lockfile_definitions.none? { |definition| definition[:active] }
301
312
 
302
- # Gemfile.lock isn't explicitly specified, otherwise it would be current
313
+ # Gemfile.lock isn't explicitly specified, otherwise it would be active
303
314
  default_lockfile_definition = lockfile_definitions.find do |definition|
304
315
  definition[:lockfile] == Bundler.default_lockfile(force_original: true)
305
316
  end
@@ -309,7 +320,7 @@ module Bundler
309
320
 
310
321
  raise GemfileNotFound, "Could not locate lockfile #{ENV["BUNDLE_LOCKFILE"].inspect}" if ENV["BUNDLE_LOCKFILE"]
311
322
 
312
- return unless default_lockfile_definition && default_lockfile_definition[:current] == false
323
+ return unless default_lockfile_definition && default_lockfile_definition[:active] == false
313
324
 
314
325
  raise GemfileEvalError, "No lockfiles marked as default"
315
326
  end
@@ -321,8 +332,10 @@ module Bundler
321
332
 
322
333
  # @!visibility private
323
334
  def inject_preamble
335
+ Bundler.ui.debug("Injecting multilock preamble")
336
+
324
337
  minor_version = Gem::Version.new(::Bundler::Multilock::VERSION).segments[0..1].join(".")
325
- bundle_preamble1_match = %(plugin "bundler-multilock")
338
+ bundle_preamble1_match = /plugin ["']bundler-multilock["']/
326
339
  bundle_preamble1 = <<~RUBY
327
340
  plugin "bundler-multilock", "~> #{minor_version}"
328
341
  RUBY
@@ -346,7 +359,16 @@ module Bundler
346
359
  end
347
360
 
348
361
  builder = Bundler::Plugin::DSL.new
349
- builder.eval_gemfile(Bundler.default_gemfile)
362
+ # this method is called as part of the plugin loading, but @loaded_plugin_names
363
+ # hasn't been set yet, so avoid re-entrancy issues
364
+ plugins = Bundler::Plugin.instance_variable_get(:@loaded_plugin_names)
365
+ original_plugins = plugins.dup
366
+ plugins << "bundler-multilock"
367
+ begin
368
+ builder.eval_gemfile(Bundler.default_gemfile)
369
+ ensure
370
+ plugins.replace(original_plugins)
371
+ end
350
372
  gemfiles = builder.instance_variable_get(:@gemfiles).map(&:read)
351
373
 
352
374
  modified = inject_specific_preamble(gemfile, gemfiles, injection_point, bundle_preamble2, add_newline: true)
@@ -368,8 +390,20 @@ module Bundler
368
390
 
369
391
  private
370
392
 
371
- def inject_specific_preamble(gemfile, gemfiles, injection_point, preamble, add_newline:, match: preamble)
372
- return false if gemfiles.any? { |g| g.include?(match) }
393
+ def expand_lockfile(lockfile)
394
+ if lockfile.is_a?(String) && !(lockfile.include?("/") || lockfile.end_with?(".lock"))
395
+ lockfile = "Gemfile.#{lockfile}.lock"
396
+ end
397
+ # use absolute paths
398
+ lockfile = Bundler.root.join(lockfile).expand_path if lockfile
399
+ # use the default lockfile (Gemfile.lock) if none was given
400
+ lockfile || Bundler.default_lockfile(force_original: true)
401
+ end
402
+
403
+ def inject_specific_preamble(gemfile, gemfiles, injection_point, preamble, add_newline:, match: nil)
404
+ # allow either type of quotes
405
+ match ||= Regexp.new(Regexp.escape(preamble).gsub('"', %(["'])))
406
+ return false if gemfiles.any? { |g| match.match?(g) }
373
407
 
374
408
  add_newline = false unless gemfile[injection_point - 1] == "\n"
375
409
 
@@ -391,31 +425,43 @@ module Bundler
391
425
 
392
426
  definition = builder.to_definition(lockfile, { bundler: unlocking_bundler })
393
427
  definition.instance_variable_set(:@dependency_changes, dependency_changes) if dependency_changes
394
- orig_definition = definition.dup # we might need it twice
395
428
 
396
429
  current_lockfile = lockfile_definition[:lockfile]
397
- if current_lockfile.exist?
398
- definition.instance_variable_set(:@lockfile_contents, current_lockfile.read)
399
- if install
400
- current_definition = builder.to_definition(current_lockfile, { bundler: unlocking_bundler })
401
- begin
402
- current_definition.resolve_with_cache!
403
- if current_definition.missing_specs.any?
404
- Bundler.with_default_lockfile(current_lockfile) do
405
- Installer.install(gemfile.dirname, current_definition, {})
406
- end
430
+ definition.instance_variable_set(:@lockfile_contents, current_lockfile.read) if current_lockfile.exist?
431
+
432
+ orig_definition = definition.dup # we might need it twice
433
+
434
+ if current_lockfile.exist? && install
435
+ Bundler.settings.temporary(frozen: true) do
436
+ current_definition = builder.to_definition(current_lockfile, {})
437
+ current_definition.resolve_with_cache!
438
+ if current_definition.missing_specs.any?
439
+ Bundler.with_default_lockfile(current_lockfile) do
440
+ Installer.install(gemfile.dirname, current_definition, {})
407
441
  end
408
- rescue RubyVersionMismatch, GemNotFound, SolveFailure
409
- # ignore
410
442
  end
443
+ rescue RubyVersionMismatch, GemNotFound, SolveFailure
444
+ # ignore
411
445
  end
412
446
  end
413
447
 
414
448
  resolved_remotely = false
415
- begin
449
+ accesses = begin
416
450
  previous_ui_level = Bundler.ui.level
417
451
  Bundler.ui.level = "warn"
418
452
  begin
453
+ # this is a horrible hack, to fix what I consider to be a Bundler bug.
454
+ # basically, if you have multiple platform specific gems in your
455
+ # lockfile, and that gem gets unlocked, Bundler will only search
456
+ # locally to find them. But non-platform-local gems are _never_
457
+ # installed locally. So just find the non-platform-local gems
458
+ # in the lockfile (that we know are there from a prior remote
459
+ # resolution), and add them to the locally installed spec list.
460
+ definition.send(:source_map).locked_specs.each do |spec|
461
+ next if spec.match_platform(Bundler.local_platform)
462
+
463
+ spec.source.specs.add(spec)
464
+ end
419
465
  definition.resolve_with_cache!
420
466
  rescue GemNotFound, SolveFailure
421
467
  definition = orig_definition
@@ -423,7 +469,9 @@ module Bundler
423
469
  definition.resolve_remotely!
424
470
  resolved_remotely = true
425
471
  end
426
- definition.lock(lockfile_definition[:lockfile], true)
472
+ SharedHelpers.capture_filesystem_access do
473
+ definition.lock(lockfile_definition[:lockfile], true)
474
+ end
427
475
  ensure
428
476
  Bundler.ui.level = previous_ui_level
429
477
  end
@@ -436,7 +484,7 @@ module Bundler
436
484
  end
437
485
  end
438
486
 
439
- !definition.nothing_changed?
487
+ accesses && !accesses.empty?
440
488
  end
441
489
  end
442
490
 
@@ -448,3 +496,26 @@ module Bundler
448
496
  end
449
497
 
450
498
  Bundler::Multilock.inject_preamble unless Bundler::Multilock.loaded?
499
+
500
+ # this is terrible, but we can't prepend into these modules because we only load
501
+ # _inside_ of the CLI commands already running
502
+ if defined?(Bundler::CLI::Check)
503
+ require_relative "multilock/check"
504
+ at_exit do
505
+ next unless $!.nil?
506
+ next if $!.is_a?(SystemExit) && !$!.success?
507
+
508
+ next if Bundler::Multilock::Check.run
509
+
510
+ Bundler.ui.warn("You can attempt to fix by running `bundle install`")
511
+ exit 1
512
+ end
513
+ end
514
+ if defined?(Bundler::CLI::Lock)
515
+ at_exit do
516
+ next unless $!.nil?
517
+ next if $!.is_a?(SystemExit) && !$!.success?
518
+
519
+ Bundler::Multilock.after_install_all(install: false)
520
+ end
521
+ end
data/plugins.rb CHANGED
@@ -20,29 +20,6 @@
20
20
 
21
21
  require_relative "lib/bundler/multilock"
22
22
 
23
- # this is terrible, but we can't prepend into these modules because we only load
24
- # _inside_ of the CLI commands already running
25
- if defined?(Bundler::CLI::Check)
26
- require_relative "lib/bundler/multilock/check"
27
- at_exit do
28
- next unless $!.nil?
29
- next if $!.is_a?(SystemExit) && !$!.success?
30
-
31
- next if Bundler::Multilock::Check.run
32
-
33
- Bundler.ui.warn("You can attempt to fix by running `bundle install`")
34
- exit 1
35
- end
36
- end
37
- if defined?(Bundler::CLI::Lock)
38
- at_exit do
39
- next unless $!.nil?
40
- next if $!.is_a?(SystemExit) && !$!.success?
41
-
42
- Bundler::Multilock.after_install_all(install: false)
43
- end
44
- end
45
-
46
23
  Bundler::Plugin.add_hook(Bundler::Plugin::Events::GEM_AFTER_INSTALL_ALL) do |_|
47
24
  Bundler::Multilock.after_install_all
48
25
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bundler-multilock
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.11
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Instructure
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-09-28 00:00:00.000000000 Z
11
+ date: 2023-10-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -121,6 +121,8 @@ files:
121
121
  - lib/bundler/multilock/ext/dsl.rb
122
122
  - lib/bundler/multilock/ext/plugin.rb
123
123
  - lib/bundler/multilock/ext/plugin/dsl.rb
124
+ - lib/bundler/multilock/ext/shared_helpers.rb
125
+ - lib/bundler/multilock/ext/source.rb
124
126
  - lib/bundler/multilock/ext/source_list.rb
125
127
  - lib/bundler/multilock/lockfile_generator.rb
126
128
  - lib/bundler/multilock/version.rb