bundler-resolutions 0.2.0 → 0.4.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: 25173fc2b7df9fe3efd07150e315f38c6584199627bef4ea1ec44ec44e3fbb2d
4
- data.tar.gz: 7728a62a42e3c9244932cb568e6a918d745a2bf9003f61d62bfb6402061610df
3
+ metadata.gz: a4f090d8833033fef1949501d9b8876333cfbc8797c954306f4ee57d963981dc
4
+ data.tar.gz: 0400ae342a0e9b8c50f7644daf99b7642954fda1d2758e68b8b06041472244b6
5
5
  SHA512:
6
- metadata.gz: dc87f9b2c8feea662f00ef0a7340aae3355c7b3e10864fb70214c970d600d7602e19745eb6882886b5fd84726371ce9caf2ef67ee5e8ff5e9b0f451ee3c4a2fc
7
- data.tar.gz: 741b9f3f5b10ad0a896b0776a0d4807f6eef446e5941554f6b95c6530f0df99e4cfc50223f848f4371639abc1e0912b9c7f8ef9b440b22b1841e12fd6c8a7f59
6
+ metadata.gz: cc78cd3cfacb3b56a160fe9a59acffb848360f082a2e326a647f2c547ddd31a692198fdcb13faf1ac50a224b6595c00cf11aa725f98cbfc98a0cd5453f95d263
7
+ data.tar.gz: 77cdeadf7f8fda11a215e8ab50d9c1506e400991a087b07e5edceef0d796c2faad2681979a2bbd41324ae5d99820377e8c710467895b21932b91990396756721
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
 
@@ -53,6 +53,24 @@ gem 'bundler-resolutions'
53
53
  gem "thor"
54
54
  ```
55
55
 
56
+ ## Config file
57
+
58
+ The config file is a YAML file with a `gems` key that contains a mapping of gem names to version
59
+ requirements. The version requirements are the same as those used in the `Gemfile`.
60
+
61
+ Example:
62
+
63
+ ```yaml
64
+ gems:
65
+ nokogiri: ">= 1.16.5" # CVE-2024-34459
66
+ thor: ">= 1.0.1, < 2.0"
67
+ ```
68
+
69
+ By default, `bundler-resolutions` will look for a file named `.bundler-resolutions.yml` in the
70
+ current directory, or the parent, and continue looking up to the root dir.
71
+
72
+ You can also specify a file location by setting the `BUNDLER_RESOLUTIONS_CONFIG` ENV var.
73
+
56
74
  ## Detail
57
75
 
58
76
  `bundler-resolutions` allows you to specify version requirements in a config file
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "yaml"
4
+
5
+ module Bundler
6
+ class Resolutions
7
+ class Config
8
+ CONFIG_FILE_NAME = ".bundler-resolutions.yml"
9
+
10
+ class << self
11
+ def load_config(config = nil)
12
+ raw_hash = if config.is_a?(Hash)
13
+ config
14
+ else
15
+ YAML.safe_load_file(find_config(config))
16
+ end
17
+ gems = raw_hash.fetch("gems")
18
+ gems.transform_values { |reqs| Array(reqs).map { |req| Gem::Requirement.new(req) } }
19
+ end
20
+
21
+ private def find_config(config = nil)
22
+ return config if config # If present, assume it is a location
23
+
24
+ # Use the ENV if present
25
+ env_file = ENV["BUNDLER_RESOLUTIONS_CONFIG"]
26
+ return env_file if env_file
27
+
28
+ # Otherwise find it above where `BUNDLE_GEMFILE` is located, or pwd if not set
29
+ dir = ENV["BUNDLE_GEMFILE"] ? File.dirname(ENV["BUNDLE_GEMFILE"]) : Dir.pwd
30
+ until File.exist?(File.join(dir, CONFIG_FILE_NAME))
31
+ dir = File.dirname(dir)
32
+ raise "Could not find #{CONFIG_FILE_NAME}" if dir == "/"
33
+ end
34
+ File.join(dir, CONFIG_FILE_NAME)
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -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.2.0"
5
+ VERSION = "0.4.0"
8
6
  end
9
7
  end
8
+
9
+ require_relative "../resolutions"
@@ -1,20 +1,34 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "yaml"
4
+ require_relative "resolutions/config"
5
+ require_relative "resolutions/version"
4
6
 
5
7
  module Bundler
6
8
  class Resolutions
7
- CONFIG_FILE_NAME = ".bundler-resolutions.yml"
9
+ DEFAULT_GEM_REQUIREMENT = Gem::Requirement.default
8
10
 
9
- attr_reader :resolutions
11
+ attr_reader :config
10
12
 
11
13
  def initialize(config = nil)
12
- load_config(config)
14
+ @config = Bundler::Resolutions::Config.load_config(config)
13
15
  end
14
16
 
15
17
  class << self
16
18
  def instance
17
- @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}"
18
32
  end
19
33
  end
20
34
 
@@ -28,48 +42,66 @@ module Bundler
28
42
  end
29
43
 
30
44
  def constrain_versions_for(results, package)
45
+ log("Constraining versions for #{package} with results: #{results.map(&:to_s)}")
31
46
  results.select do |pkg|
32
- req = resolutions[package.name]
33
- if req
34
- if ENV["BUNDLER_RESOLUTIONS_DEBUG"] == "true"
35
- puts "bundler-resolutions making sure #{package} is satisfied by #{req}"
36
- end
37
- req.satisfied_by?(pkg.version)
38
- else
47
+ reqs = resolutions_for(package.name)
48
+ if reqs.nil?
39
49
  true
50
+ else
51
+ log("making sure #{package} / #{pkg} is satisfied by #{reqs.map(&:to_s)}")
52
+ reqs.all? { |req| req.satisfied_by?(pkg.version) }
40
53
  end
41
54
  end
42
55
  end
43
56
 
44
- private def load_config(config = nil)
45
- # Safe load yaml file whose location is given by an argument, an ENV, and if no
46
- # env given then work up dir hierarchy until found.
47
- # The file should be called .bundler-resolutions.yml
48
- raw_hash = if config.is_a?(Hash)
49
- config
50
- else
51
- YAML.safe_load_file(find_config(config))
52
- end
53
- gems = raw_hash.fetch("gems")
54
- @resolutions = gems.transform_values { |version| Gem::Requirement.new(version.split(",")) }
57
+ def resolutions_for(package_name)
58
+ config[package_name]
55
59
  end
56
60
 
57
- private def find_config(config = nil)
58
- return config if config # If present, assume it is a location
61
+ def log(message, gem = nil) = self.class.log(message, gem)
59
62
 
60
- # Use the ENV if present
61
- env_file = ENV["BUNDLER_RESOLUTIONS_CONFIG"]
62
- return env_file if env_file
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?
63
69
 
64
- # Otherwise find it in the file tree
65
- dir = Dir.pwd
66
- until File.exist?(File.join(dir, CONFIG_FILE_NAME))
67
- dir = File.dirname(dir)
68
- raise "Could not find #{CONFIG_FILE_NAME}" if dir == "/"
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)
69
81
  end
70
- File.join(dir, CONFIG_FILE_NAME)
71
82
  end
72
83
  end
73
84
  end
74
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.
75
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.2.0
4
+ version: 0.4.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-03-23 00:00:00.000000000 Z
11
+ date: 2025-06-03 00:00:00.000000000 Z
11
12
  dependencies: []
12
13
  description: A bundler plugin to enforce resolutions without specifying a concrete
13
14
  dependency
@@ -19,6 +20,7 @@ extra_rdoc_files: []
19
20
  files:
20
21
  - README.md
21
22
  - lib/bundler/resolutions.rb
23
+ - lib/bundler/resolutions/config.rb
22
24
  - lib/bundler/resolutions/version.rb
23
25
  - plugins.rb
24
26
  homepage: https://github.com/hlascelles/bundler-resolutions
@@ -31,6 +33,7 @@ metadata:
31
33
  source_code_uri: https://github.com/hlascelles/bundler-resolutions/
32
34
  bug_tracker_uri: https://github.com/hlascelles/bundler-resolutions/issues
33
35
  rubygems_mfa_required: 'true'
36
+ post_install_message:
34
37
  rdoc_options: []
35
38
  require_paths:
36
39
  - lib
@@ -38,14 +41,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
38
41
  requirements:
39
42
  - - ">="
40
43
  - !ruby/object:Gem::Version
41
- version: '0'
44
+ version: '3.2'
42
45
  required_rubygems_version: !ruby/object:Gem::Requirement
43
46
  requirements:
44
47
  - - ">="
45
48
  - !ruby/object:Gem::Version
46
49
  version: '0'
47
50
  requirements: []
48
- rubygems_version: 3.6.6
51
+ rubygems_version: 3.5.22
52
+ signing_key:
49
53
  specification_version: 4
50
54
  summary: A bundler plugin to enforce resolutions without specifying a concrete dependency
51
55
  test_files: []