bundler-multilock 1.0.10 → 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: f71e197447bdcd02b427ac7bc3a21a7e41e20a3d67edda69ed3821dea293e3ac
4
- data.tar.gz: bf11ee91ace5dff77af3bc2b56ec283d2d227513e8d9397ce00e199ab73e3d83
3
+ metadata.gz: 12e431ea7783f9bc36d1efae8aaeda88434936f587c954c34ad4163b3338dc44
4
+ data.tar.gz: 8eda2e0bc8f785b0ceefb312d4d34c6fd754dab58c835ec7b818c551af903bdf
5
5
  SHA512:
6
- metadata.gz: 4cad381cf2fbb7d441f98d39322d54a0496570ceb3e564669a39429072224b7cbbca1af2f68205bc1149e05dc7535ca75e3f51631795732c019354d92ce17952
7
- data.tar.gz: 3ddb854fc272683a96711403a8f65fc4d588260c46e7753f71ca790192be19f8f9e7b9b7612457d99ea4596ab6830b8cf8eda5c0385621f0769949f41cb95c87
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.10"
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,36 +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
- lockfile.instance_variable_set(:@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
257
+ unlocking_bundler = true
258
+ lockfile.instance_variable_set(:@bundler_version, parent_lockfile.bundler_version)
259
+ end
247
260
 
248
261
  new_contents = LockfileGenerator.generate(lockfile)
249
262
  else
250
- # 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
251
264
  # locked gems
252
- new_contents = adjusted_default_lockfile_contents
265
+ new_contents = adjusted_parent_lockfile_contents
253
266
  end
254
267
 
255
268
  had_changes = false
@@ -261,7 +274,8 @@ module Bundler
261
274
  had_changes = write_lockfile(lockfile_definition,
262
275
  temp_lockfile.path,
263
276
  install: install,
264
- dependency_changes: dependency_changes)
277
+ dependency_changes: dependency_changes,
278
+ unlocking_bundler: unlocking_bundler)
265
279
  end
266
280
 
267
281
  # if we had changes, bundler may have updated some common
@@ -269,8 +283,9 @@ module Bundler
269
283
  # once to reset them back to the default lockfile's version.
270
284
  # if it's already good, the `check` check at the beginning of
271
285
  # the loop will skip the second sync anyway.
272
- if had_changes && attempts < 3
286
+ if had_changes && attempts < 2
273
287
  attempts += 1
288
+ Bundler.ui.debug("Re-running sync to #{relative_lockfile} to reset common dependencies")
274
289
  redo
275
290
  else
276
291
  attempts = 1
@@ -293,9 +308,9 @@ module Bundler
293
308
  @loaded = true
294
309
  return if lockfile_definitions.empty?
295
310
 
296
- return unless lockfile_definitions.none? { |definition| definition[:current] }
311
+ return unless lockfile_definitions.none? { |definition| definition[:active] }
297
312
 
298
- # Gemfile.lock isn't explicitly specified, otherwise it would be current
313
+ # Gemfile.lock isn't explicitly specified, otherwise it would be active
299
314
  default_lockfile_definition = lockfile_definitions.find do |definition|
300
315
  definition[:lockfile] == Bundler.default_lockfile(force_original: true)
301
316
  end
@@ -305,7 +320,7 @@ module Bundler
305
320
 
306
321
  raise GemfileNotFound, "Could not locate lockfile #{ENV["BUNDLE_LOCKFILE"].inspect}" if ENV["BUNDLE_LOCKFILE"]
307
322
 
308
- return unless default_lockfile_definition && default_lockfile_definition[:current] == false
323
+ return unless default_lockfile_definition && default_lockfile_definition[:active] == false
309
324
 
310
325
  raise GemfileEvalError, "No lockfiles marked as default"
311
326
  end
@@ -317,8 +332,10 @@ module Bundler
317
332
 
318
333
  # @!visibility private
319
334
  def inject_preamble
335
+ Bundler.ui.debug("Injecting multilock preamble")
336
+
320
337
  minor_version = Gem::Version.new(::Bundler::Multilock::VERSION).segments[0..1].join(".")
321
- bundle_preamble1_match = %(plugin "bundler-multilock")
338
+ bundle_preamble1_match = /plugin ["']bundler-multilock["']/
322
339
  bundle_preamble1 = <<~RUBY
323
340
  plugin "bundler-multilock", "~> #{minor_version}"
324
341
  RUBY
@@ -342,7 +359,16 @@ module Bundler
342
359
  end
343
360
 
344
361
  builder = Bundler::Plugin::DSL.new
345
- 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
346
372
  gemfiles = builder.instance_variable_get(:@gemfiles).map(&:read)
347
373
 
348
374
  modified = inject_specific_preamble(gemfile, gemfiles, injection_point, bundle_preamble2, add_newline: true)
@@ -364,8 +390,20 @@ module Bundler
364
390
 
365
391
  private
366
392
 
367
- def inject_specific_preamble(gemfile, gemfiles, injection_point, preamble, add_newline:, match: preamble)
368
- 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) }
369
407
 
370
408
  add_newline = false unless gemfile[injection_point - 1] == "\n"
371
409
 
@@ -375,7 +413,7 @@ module Bundler
375
413
  true
376
414
  end
377
415
 
378
- def write_lockfile(lockfile_definition, lockfile, install:, dependency_changes: false)
416
+ def write_lockfile(lockfile_definition, lockfile, install:, dependency_changes: false, unlocking_bundler: false)
379
417
  prepare_block = lockfile_definition[:prepare]
380
418
 
381
419
  gemfile = lockfile_definition[:gemfile]
@@ -385,33 +423,45 @@ module Bundler
385
423
  builder.eval_gemfile(gemfile, &prepare_block) if prepare_block
386
424
  builder.eval_gemfile(gemfile)
387
425
 
388
- definition = builder.to_definition(lockfile, {})
426
+ definition = builder.to_definition(lockfile, { bundler: unlocking_bundler })
389
427
  definition.instance_variable_set(:@dependency_changes, dependency_changes) if dependency_changes
390
- orig_definition = definition.dup # we might need it twice
391
428
 
392
429
  current_lockfile = lockfile_definition[:lockfile]
393
- if current_lockfile.exist?
394
- definition.instance_variable_set(:@lockfile_contents, current_lockfile.read)
395
- if install
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
396
436
  current_definition = builder.to_definition(current_lockfile, {})
397
- begin
398
- current_definition.resolve_with_cache!
399
- if current_definition.missing_specs.any?
400
- Bundler.with_default_lockfile(current_lockfile) do
401
- Installer.install(gemfile.dirname, current_definition, {})
402
- end
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, {})
403
441
  end
404
- rescue RubyVersionMismatch, GemNotFound, SolveFailure
405
- # ignore
406
442
  end
443
+ rescue RubyVersionMismatch, GemNotFound, SolveFailure
444
+ # ignore
407
445
  end
408
446
  end
409
447
 
410
448
  resolved_remotely = false
411
- begin
449
+ accesses = begin
412
450
  previous_ui_level = Bundler.ui.level
413
451
  Bundler.ui.level = "warn"
414
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
415
465
  definition.resolve_with_cache!
416
466
  rescue GemNotFound, SolveFailure
417
467
  definition = orig_definition
@@ -419,7 +469,9 @@ module Bundler
419
469
  definition.resolve_remotely!
420
470
  resolved_remotely = true
421
471
  end
422
- definition.lock(lockfile_definition[:lockfile], true)
472
+ SharedHelpers.capture_filesystem_access do
473
+ definition.lock(lockfile_definition[:lockfile], true)
474
+ end
423
475
  ensure
424
476
  Bundler.ui.level = previous_ui_level
425
477
  end
@@ -432,7 +484,7 @@ module Bundler
432
484
  end
433
485
  end
434
486
 
435
- !definition.nothing_changed?
487
+ accesses && !accesses.empty?
436
488
  end
437
489
  end
438
490
 
@@ -444,3 +496,26 @@ module Bundler
444
496
  end
445
497
 
446
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.10
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-26 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
@@ -25,19 +25,19 @@ dependencies:
25
25
  - !ruby/object:Gem::Version
26
26
  version: 2.4.19
27
27
  - !ruby/object:Gem::Dependency
28
- name: byebug
28
+ name: debug
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '11.1'
33
+ version: '1.8'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '11.1'
40
+ version: '1.8'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rake
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -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