bundler-resolutions 0.4.0 → 0.6.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: a4f090d8833033fef1949501d9b8876333cfbc8797c954306f4ee57d963981dc
4
- data.tar.gz: 0400ae342a0e9b8c50f7644daf99b7642954fda1d2758e68b8b06041472244b6
3
+ metadata.gz: 77ebf87d3236f7c7d81a4e3daba2c8cb4cd78b284221ae6447092b0351a31fa1
4
+ data.tar.gz: ac7ef028df610a46f008d7e82467a5f5327cfafc1582f4e0ef9e00ff2669f9ac
5
5
  SHA512:
6
- metadata.gz: cc78cd3cfacb3b56a160fe9a59acffb848360f082a2e326a647f2c547ddd31a692198fdcb13faf1ac50a224b6595c00cf11aa725f98cbfc98a0cd5453f95d263
7
- data.tar.gz: 77cdeadf7f8fda11a215e8ab50d9c1506e400991a087b07e5edceef0d796c2faad2681979a2bbd41324ae5d99820377e8c710467895b21932b91990396756721
6
+ metadata.gz: e5ed756652c94a05540e6c0e7928355f9e959d1f5dd832a4cc72787828b76e63d9ff6ba6daad249e46dba5aebe4b36894f32589cbe264fc03f6cc2bd7684d983
7
+ data.tar.gz: 6ee1730a6c0dbf5ed27a1247375785beeb13aabbef1678798f07f69b323e65593de17e588e1e701476b310933d432a1b0732ed6189ef3c218a71a85131a43474
data/README.md CHANGED
@@ -15,15 +15,17 @@ a concrete dependency on those gems. It acts much like the
15
15
 
16
16
  ## Usage
17
17
 
18
- Add `bundler-resolutions` to your Gemfile, and add a `.bundler-resolutions.yml` file to
19
- specify the gems you want to specify versions requirements for.
18
+ Add `bundler-resolutions` to your Gemfile. Note, it must come before all other gems, and be followed
19
+ by `require "bundler/resolutions"`. This is because it patches the `bundler` resolver. This means
20
+ the first time you run `bundle install` it will not work, as the gem has not been installed yet.
20
21
 
21
- ### Example 1
22
+ `Gemfile`:
23
+ ```ruby
24
+ gem "bundler-resolutions"
25
+ require "bundler/resolutions" if Gem::Specification.find_all_by_name('bundler-resolutions').any?
26
+ ```
22
27
 
23
- In this example the resulting `Gemfile.lock` will have nokogiri locked to `1.16.5` or above, but
24
- nokogiri will not be present in the `DEPENDENCIES` section of the lock file. Also, if `rails` were
25
- to change to a version that did not depend on nokogiri, then the resolution would not be used or
26
- appear in the lock file at all.
28
+ Then, and add a `.bundler-resolutions.yml` file to specify the gems you want to specify versions requirements for.
27
29
 
28
30
  `.bundler-resolutions.yml`:
29
31
  ```yaml
@@ -31,28 +33,43 @@ gems:
31
33
  nokogiri: ">= 1.16.5" # CVE-2024-34459
32
34
  ```
33
35
 
36
+ ### Example 1
37
+
38
+ In this example the resulting `Gemfile.lock` will have nokogiri locked to `1.16.5` or above, but
39
+ nokogiri will not be present in the `DEPENDENCIES` section of the lock file. Also, if `rails` were
40
+ to change to a version that did not depend on nokogiri, then the resolution would not be used or
41
+ appear in the lock file at all.
42
+
34
43
  `Gemfile`:
35
44
  ```ruby
36
- gem "bundler-resolutions", path: "../", install_if: -> { require Gem::Specification.find_by_name('bundler-resolutions').gem_dir + "/lib/bundler/resolutions"; true }
45
+ gem "bundler-resolutions"
46
+ require "bundler/resolutions" if Gem::Specification.find_all_by_name('bundler-resolutions').any?
37
47
  gem "rails"
38
48
  ```
39
49
 
40
- ### Example 2
41
-
42
- Here, the `Gemfile.lock` from this example will not have nokogiri at all, as it is neither
43
- explicitly declared in the Gemfile, nor brought in as a transitive dependency.
44
-
45
50
  `.bundler-resolutions.yml`:
46
51
  ```yaml
47
52
  gems:
48
53
  nokogiri: ">= 1.16.5" # CVE-2024-34459
49
54
  ```
50
55
 
56
+ ### Example 2
57
+
58
+ Here, the `Gemfile.lock` from this example will not have nokogiri at all, as it is neither
59
+ explicitly declared in the Gemfile, nor brought in as a transitive dependency.
60
+
51
61
  ```ruby
52
62
  gem 'bundler-resolutions'
63
+ require "bundler/resolutions" if Gem::Specification.find_all_by_name('bundler-resolutions').any?
53
64
  gem "thor"
54
65
  ```
55
66
 
67
+ `.bundler-resolutions.yml`:
68
+ ```yaml
69
+ gems:
70
+ nokogiri: ">= 1.16.5" # CVE-2024-34459
71
+ ```
72
+
56
73
  ## Config file
57
74
 
58
75
  The config file is a YAML file with a `gems` key that contains a mapping of gem names to version
@@ -85,6 +102,22 @@ bundler lock resolution.
85
102
  The other difference is that even if it does take part in the resolutions, it will not be
86
103
  present in the `DEPENDENCIES` section of the lock file, as it is not a direct dependency.
87
104
 
105
+ ## The Gemfile require
106
+
107
+ You will see that the `Gemfile` requires `bundler/resolutions` to make it work fully. This is
108
+ because it patches the `bundler` resolver to allow for the resolution restrictions. Unfortunately,
109
+ if the `Gemfile.lock` file is already present, and all the gems are already resolved and installed then no
110
+ patching will take place, and the bundler-resolutions code will never be run.
111
+
112
+ The bundler-resolutions code will only run without the extra require if:
113
+
114
+ 1. The `Gemfile.lock` file is not present.
115
+ 2. Any of the locked gems are not installed.
116
+ 3. `bundle update` is run.
117
+
118
+ The one scenario where the require line is needed is when `bundle install` is run with a valid,
119
+ preinstalled `Gemfile.lock`.
120
+
88
121
  ## Use cases
89
122
 
90
123
  There are a number of reasons you may want to prevent the usage of some gem versions, but without
@@ -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 { |reqs| Array(reqs).map { |req| Gem::Requirement.new(req) } }
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)
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Bundler
4
4
  class Resolutions
5
- VERSION = "0.4.0"
5
+ VERSION = "0.6.0"
6
6
  end
7
7
  end
8
8
 
@@ -63,21 +63,28 @@ module Bundler
63
63
  module Definition
64
64
  def check_lockfile
65
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
- }
66
+ invalids = @locked_specs.to_a.select { |s| spec_invalid?(s) }
67
+ return if invalids.empty?
68
+
80
69
  @locked_specs.delete(invalids)
70
+ # Signal to Bundler that re-resolution is needed due to invalid transitive dependencies.
71
+ # This prevents confusing "Could not find gems valid for all resolution platforms" errors.
72
+ @locked_spec_with_invalid_deps = invalids.first.name if @locked_spec_with_invalid_deps.nil?
73
+ end
74
+
75
+ private def spec_invalid?(lazy_specification)
76
+ reqs = Bundler::Resolutions.instance.resolutions_for(lazy_specification.name)
77
+ return false if reqs.nil?
78
+
79
+ # rubocop:disable Layout/LineLength
80
+ if reqs.all? { |req| req.satisfied_by?(lazy_specification.version) }
81
+ Bundler::Resolutions.log "#{lazy_specification.name} (#{lazy_specification.version}) is satisfied by the current lockfile version."
82
+ false
83
+ else
84
+ Bundler::Resolutions.log "#{lazy_specification.name} (#{lazy_specification.version}) is NOT satisfied by the current lockfile version."
85
+ true
86
+ end
87
+ # rubocop:enable Layout/LineLength
81
88
  end
82
89
  end
83
90
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bundler-resolutions
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Harry Lascelles
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-06-03 00:00:00.000000000 Z
11
+ date: 2026-02-06 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: A bundler plugin to enforce resolutions without specifying a concrete
14
14
  dependency