bundler-resolutions 0.3.0 → 0.5.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: 7a6def9961c77861086b6b58177a934945938362e2125ba5dc49b8a6c06efdff
4
- data.tar.gz: ccef554f38761f9916843d675f70ef7189ef8a7070bdb3d9b2e466e2eed998de
3
+ metadata.gz: ca66a89bbd900f1f8c14ff704e6e4155fd4dc768f05d3c16f1d3eb18a0a57d19
4
+ data.tar.gz: 77aa2e3521124d04f3c6cb4422fef1e417b0c6c705a840f728696bddefa35aa8
5
5
  SHA512:
6
- metadata.gz: '01507905d1b9402c1efc92f9509b5aeef2b858d18c3b3e8555af937e1b479dc4b2711f87eed3ef87b8d64bfb7285bb299322a5925d43013e6dac6afa2128b5ba'
7
- data.tar.gz: c85660a42a9149ffcded4f649ef8b2d9413da63eb435d9c8231be3fcdf26dc6aebea0a6608bd1206298ac3caf47b01681a573cecc4e92b0dc3b6638a893b219d
6
+ metadata.gz: e453be51627d50ffa3186b27c2ee4926805e4f1d11c26a061cdfd94960f83286513b036f228e0ad2029730308cfbcd08a663b62c6faead45fa26291c78e13c21
7
+ data.tar.gz: 1c0681c2f8b334f3c85073d693900f3898d87343ed912f50f7fd1823a0986ac546979bf8dba447e7d39bbe5bd06765476aefeb6a703152a3befd09316b7f5577
data/README.md CHANGED
@@ -33,7 +33,7 @@ gems:
33
33
 
34
34
  `Gemfile`:
35
35
  ```ruby
36
- gem 'bundler-resolutions'
36
+ gem "bundler-resolutions", path: "../", install_if: -> { require Gem::Specification.find_by_name('bundler-resolutions').gem_dir + "/lib/bundler/resolutions"; true }
37
37
  gem "rails"
38
38
  ```
39
39
 
@@ -9,13 +9,24 @@ module Bundler
9
9
 
10
10
  class << self
11
11
  def load_config(config = nil)
12
+ location = "config hash"
12
13
  raw_hash = if config.is_a?(Hash)
13
14
  config
14
15
  else
15
- YAML.safe_load_file(find_config(config))
16
+ location = find_config(config)
17
+ YAML.safe_load_file(location)
16
18
  end
17
- gems = raw_hash.fetch("gems")
18
- gems.transform_values { |version| Gem::Requirement.new(version.split(",")) }
19
+ gems = raw_hash.fetch("gems") {
20
+ raise <<~ERR
21
+ No 'gems' key found in #{location}. Please ensure the file is formatted correctly.
22
+
23
+ Data was:
24
+ #{raw_hash.inspect}
25
+ ERR
26
+ }
27
+ gems.transform_values { |reqs|
28
+ Array(reqs).flat_map { |v| v.split(",") }.map { |req| Gem::Requirement.new(req) }
29
+ }
19
30
  end
20
31
 
21
32
  private def find_config(config = nil)
@@ -25,8 +36,8 @@ module Bundler
25
36
  env_file = ENV["BUNDLER_RESOLUTIONS_CONFIG"]
26
37
  return env_file if env_file
27
38
 
28
- # Otherwise find it in the file tree
29
- dir = Dir.pwd
39
+ # Otherwise find it above where `BUNDLE_GEMFILE` is located, or pwd if not set
40
+ dir = ENV["BUNDLE_GEMFILE"] ? File.dirname(ENV["BUNDLE_GEMFILE"]) : Dir.pwd
30
41
  until File.exist?(File.join(dir, CONFIG_FILE_NAME))
31
42
  dir = File.dirname(dir)
32
43
  raise "Could not find #{CONFIG_FILE_NAME}" if dir == "/"
@@ -1,9 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "../resolutions"
4
-
5
3
  module Bundler
6
4
  class Resolutions
7
- VERSION = "0.3.0"
5
+ VERSION = "0.5.0"
8
6
  end
9
7
  end
8
+
9
+ require_relative "../resolutions"
@@ -2,6 +2,7 @@
2
2
 
3
3
  require "yaml"
4
4
  require_relative "resolutions/config"
5
+ require_relative "resolutions/version"
5
6
 
6
7
  module Bundler
7
8
  class Resolutions
@@ -15,19 +16,24 @@ module Bundler
15
16
 
16
17
  class << self
17
18
  def instance
18
- @instance ||= new
19
+ @instance ||= new # rubocop:disable ThreadSafety/ClassInstanceVariable
20
+ end
21
+
22
+ # You can debug with BUNDLER_RESOLUTIONS_DEBUG=gem_name or BUNDLER_RESOLUTIONS_DEBUG=true
23
+ # to see all messages.
24
+ def log(message, gem = nil)
25
+ return if ENV["BUNDLER_RESOLUTIONS_DEBUG"].nil?
26
+ unless ENV["BUNDLER_RESOLUTIONS_DEBUG"] == "true" ||
27
+ ENV["BUNDLER_RESOLUTIONS_DEBUG"].split(",").include?(gem)
28
+ return
29
+ end
30
+
31
+ puts "bundler-resolutions: #{message}"
19
32
  end
20
33
  end
21
34
 
22
35
  # A module we prepend to Bundler::Resolutions::Resolver
23
- # :reek:ModuleInitialize
24
36
  module Resolver
25
- # Override the initializer in the resolver
26
- def initialize(*args)
27
- Bundler::Resolutions.instance.add_concrete_resolutions_for(args.first)
28
- super
29
- end
30
-
31
37
  # This overrides the default behaviour of the resolver to filter out versions that don't
32
38
  # satisfy the requirements specified in .bundler-resolutions.yml.
33
39
  def filtered_versions_for(package)
@@ -36,79 +42,66 @@ module Bundler
36
42
  end
37
43
 
38
44
  def constrain_versions_for(results, package)
45
+ log("Constraining versions for #{package} with results: #{results.map(&:to_s)}")
39
46
  results.select do |pkg|
40
- req = resolutions_for(package.name)
41
- if req
42
- log("making sure #{package} is satisfied by #{req}")
43
- req.satisfied_by?(pkg.version)
44
- else
47
+ reqs = resolutions_for(package.name)
48
+ if reqs.nil?
45
49
  true
46
- end
47
- end
48
- end
49
-
50
- def add_concrete_resolutions_for(base)
51
- base.requirements.each do |bundler_dependency|
52
- requirement_name = bundler_dependency.name
53
- resolutions = resolutions_for(requirement_name)
54
-
55
- if resolutions
56
- log(<<~MSG, requirement_name)
57
- has resolutions for concrete dependency '#{requirement_name}': #{resolutions}
58
- MSG
59
50
  else
60
- log("has no resolutions for concrete dependency '#{requirement_name}'", requirement_name)
61
- next
51
+ log("making sure #{package} / #{pkg} is satisfied by #{reqs.map(&:to_s)}")
52
+ reqs.all? { |req| req.satisfied_by?(pkg.version) }
62
53
  end
63
-
64
- bundler_resolutions_reqs = resolutions.requirements
65
- apply_resolutions_for_concrete_gem(bundler_dependency, bundler_resolutions_reqs)
66
54
  end
67
55
  end
68
56
 
69
- private def resolutions_for(package_name)
57
+ def resolutions_for(package_name)
70
58
  config[package_name]
71
59
  end
72
60
 
73
- # You can debug with BUNDLER_RESOLUTIONS_DEBUG=gem_name or BUNDLER_RESOLUTIONS_DEBUG=true
74
- # to see all messages.
75
- private def log(message, gem = nil)
76
- return unless ENV["BUNDLER_RESOLUTIONS_DEBUG"]
77
- return if gem && !ENV["BUNDLER_RESOLUTIONS_DEBUG"].split(",").include?(gem)
61
+ def log(message, gem = nil) = self.class.log(message, gem)
78
62
 
79
- puts "bundler-resolutions: #{message}"
80
- end
81
-
82
- private def apply_resolutions_for_concrete_gem(bundler_dependency, bundler_resolutions_reqs)
83
- requirement_name = bundler_dependency.name
84
- bundler_resolutions_reqs.each do |r|
85
- # If the concrete requirement is already in the Gemfile, skip it
86
- requirements = bundler_dependency.requirement.requirements
87
- if requirements.include?(r)
88
- # We don't want to double up / dupe the same requirements
89
- log(<<~MSG, requirement_name)
90
- Skipping adding requirements to gem concretely specified in Gemfile as it
91
- was already present: #{requirement_name}: #{bundler_dependency}
92
- MSG
93
- next
94
- end
95
-
96
- # Otherwise add the additional requirement
97
- before_req = bundler_dependency.to_s
98
- # If there were no requirements before, there is a default one for ">= 0". We need to
99
- # remove that so when we add the new one the implicit ">= 0" is not present, as it normally
100
- # isn't written out to lockfiles.
101
- requirements.clear if bundler_dependency.requirement == DEFAULT_GEM_REQUIREMENT
102
- # Add the new requirement
103
- requirements << r
104
- after_req = bundler_dependency.to_s
105
-
106
- log(<<~MSG, requirement_name)
107
- Adding concrete constraints for #{requirement_name}. Before: #{before_req}. After: #{after_req}.
108
- MSG
63
+ module Definition
64
+ def check_lockfile
65
+ super
66
+ invalids = @locked_specs.to_a.select { |lazy_specification|
67
+ reqs = Bundler::Resolutions.instance.resolutions_for(lazy_specification.name)
68
+ next if reqs.nil?
69
+
70
+ # rubocop:disable Layout/LineLength
71
+ if reqs.all? { |req| req.satisfied_by?(lazy_specification.version) }
72
+ Bundler::Resolutions.log "#{lazy_specification.name} (#{lazy_specification.version}) is satisfied by the current lockfile version."
73
+ nil
74
+ else
75
+ Bundler::Resolutions.log "#{lazy_specification.name} (#{lazy_specification.version}) is NOT satisfied by the current lockfile version."
76
+ lazy_specification
77
+ end
78
+ # rubocop:enable Layout/LineLength
79
+ }
80
+ @locked_specs.delete(invalids)
109
81
  end
110
82
  end
111
83
  end
112
84
  end
113
85
 
86
+ # Check if the methods exists before we prepend them, to avoid issues with Bundler versions
87
+ # that do not have this method.
88
+ {
89
+ Bundler::Resolver => :filtered_versions_for,
90
+ Bundler::Definition => :nothing_changed?,
91
+ }.each do |klass, method|
92
+ next if klass.instance_methods.include?(method) || klass.private_instance_methods.include?(method)
93
+
94
+ raise <<~ERR
95
+ Bundler version #{Bundler::VERSION} is not compatible with bundler-resolutions #{Bundler::Resolutions::VERSION}
96
+ The method '#{method}' is not defined in '#{klass}'. This is likely due to a refactoring of a new
97
+ Bundler version. Please check the bundler-resolutions changelog and the Bundler changelog
98
+ to see if this is a known issue, or submit a bug report to bundler-resolutions.
99
+ ERR
100
+ end
101
+
102
+ # This is needed so we can trigger a rebuild of the lock file if just the yaml has changed.
103
+ Bundler::Definition.prepend(Bundler::Resolutions::Definition)
104
+ # This removes the transitive dependency versions that do not satisfy the yaml config.
114
105
  Bundler::Resolver.prepend(Bundler::Resolutions::Resolver)
106
+
107
+ Bundler::Resolutions.log("bundler-resolutions #{Bundler::Resolutions::VERSION} loaded")
metadata CHANGED
@@ -1,13 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bundler-resolutions
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Harry Lascelles
8
+ autorequire:
8
9
  bindir: bin
9
10
  cert_chain: []
10
- date: 2025-05-03 00:00:00.000000000 Z
11
+ date: 2025-07-15 00:00:00.000000000 Z
11
12
  dependencies: []
12
13
  description: A bundler plugin to enforce resolutions without specifying a concrete
13
14
  dependency
@@ -32,6 +33,7 @@ metadata:
32
33
  source_code_uri: https://github.com/hlascelles/bundler-resolutions/
33
34
  bug_tracker_uri: https://github.com/hlascelles/bundler-resolutions/issues
34
35
  rubygems_mfa_required: 'true'
36
+ post_install_message:
35
37
  rdoc_options: []
36
38
  require_paths:
37
39
  - lib
@@ -46,7 +48,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
46
48
  - !ruby/object:Gem::Version
47
49
  version: '0'
48
50
  requirements: []
49
- rubygems_version: 3.6.6
51
+ rubygems_version: 3.5.22
52
+ signing_key:
50
53
  specification_version: 4
51
54
  summary: A bundler plugin to enforce resolutions without specifying a concrete dependency
52
55
  test_files: []