bundler-resolutions 0.2.0 → 0.3.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: 7a6def9961c77861086b6b58177a934945938362e2125ba5dc49b8a6c06efdff
4
+ data.tar.gz: ccef554f38761f9916843d675f70ef7189ef8a7070bdb3d9b2e466e2eed998de
5
5
  SHA512:
6
- metadata.gz: dc87f9b2c8feea662f00ef0a7340aae3355c7b3e10864fb70214c970d600d7602e19745eb6882886b5fd84726371ce9caf2ef67ee5e8ff5e9b0f451ee3c4a2fc
7
- data.tar.gz: 741b9f3f5b10ad0a896b0776a0d4807f6eef446e5941554f6b95c6530f0df99e4cfc50223f848f4371639abc1e0912b9c7f8ef9b440b22b1841e12fd6c8a7f59
6
+ metadata.gz: '01507905d1b9402c1efc92f9509b5aeef2b858d18c3b3e8555af937e1b479dc4b2711f87eed3ef87b8d64bfb7285bb299322a5925d43013e6dac6afa2128b5ba'
7
+ data.tar.gz: c85660a42a9149ffcded4f649ef8b2d9413da63eb435d9c8231be3fcdf26dc6aebea0a6608bd1206298ac3caf47b01681a573cecc4e92b0dc3b6638a893b219d
data/README.md CHANGED
@@ -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 { |version| Gem::Requirement.new(version.split(",")) }
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 in the file tree
29
+ dir = 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
@@ -4,6 +4,6 @@ require_relative "../resolutions"
4
4
 
5
5
  module Bundler
6
6
  class Resolutions
7
- VERSION = "0.2.0"
7
+ VERSION = "0.3.0"
8
8
  end
9
9
  end
@@ -1,15 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "yaml"
4
+ require_relative "resolutions/config"
4
5
 
5
6
  module Bundler
6
7
  class Resolutions
7
- CONFIG_FILE_NAME = ".bundler-resolutions.yml"
8
+ DEFAULT_GEM_REQUIREMENT = Gem::Requirement.default
8
9
 
9
- attr_reader :resolutions
10
+ attr_reader :config
10
11
 
11
12
  def initialize(config = nil)
12
- load_config(config)
13
+ @config = Bundler::Resolutions::Config.load_config(config)
13
14
  end
14
15
 
15
16
  class << self
@@ -19,7 +20,14 @@ module Bundler
19
20
  end
20
21
 
21
22
  # A module we prepend to Bundler::Resolutions::Resolver
23
+ # :reek:ModuleInitialize
22
24
  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
+
23
31
  # This overrides the default behaviour of the resolver to filter out versions that don't
24
32
  # satisfy the requirements specified in .bundler-resolutions.yml.
25
33
  def filtered_versions_for(package)
@@ -29,11 +37,9 @@ module Bundler
29
37
 
30
38
  def constrain_versions_for(results, package)
31
39
  results.select do |pkg|
32
- req = resolutions[package.name]
40
+ req = resolutions_for(package.name)
33
41
  if req
34
- if ENV["BUNDLER_RESOLUTIONS_DEBUG"] == "true"
35
- puts "bundler-resolutions making sure #{package} is satisfied by #{req}"
36
- end
42
+ log("making sure #{package} is satisfied by #{req}")
37
43
  req.satisfied_by?(pkg.version)
38
44
  else
39
45
  true
@@ -41,33 +47,66 @@ module Bundler
41
47
  end
42
48
  end
43
49
 
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(",")) }
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
+ else
60
+ log("has no resolutions for concrete dependency '#{requirement_name}'", requirement_name)
61
+ next
62
+ end
63
+
64
+ bundler_resolutions_reqs = resolutions.requirements
65
+ apply_resolutions_for_concrete_gem(bundler_dependency, bundler_resolutions_reqs)
66
+ end
55
67
  end
56
68
 
57
- private def find_config(config = nil)
58
- return config if config # If present, assume it is a location
69
+ private def resolutions_for(package_name)
70
+ config[package_name]
71
+ end
72
+
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)
78
+
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
59
95
 
60
- # Use the ENV if present
61
- env_file = ENV["BUNDLER_RESOLUTIONS_CONFIG"]
62
- return env_file if env_file
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
63
105
 
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 == "/"
106
+ log(<<~MSG, requirement_name)
107
+ Adding concrete constraints for #{requirement_name}. Before: #{before_req}. After: #{after_req}.
108
+ MSG
69
109
  end
70
- File.join(dir, CONFIG_FILE_NAME)
71
110
  end
72
111
  end
73
112
  end
metadata CHANGED
@@ -1,13 +1,13 @@
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.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Harry Lascelles
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-03-23 00:00:00.000000000 Z
10
+ date: 2025-05-03 00:00:00.000000000 Z
11
11
  dependencies: []
12
12
  description: A bundler plugin to enforce resolutions without specifying a concrete
13
13
  dependency
@@ -19,6 +19,7 @@ extra_rdoc_files: []
19
19
  files:
20
20
  - README.md
21
21
  - lib/bundler/resolutions.rb
22
+ - lib/bundler/resolutions/config.rb
22
23
  - lib/bundler/resolutions/version.rb
23
24
  - plugins.rb
24
25
  homepage: https://github.com/hlascelles/bundler-resolutions
@@ -38,7 +39,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
38
39
  requirements:
39
40
  - - ">="
40
41
  - !ruby/object:Gem::Version
41
- version: '0'
42
+ version: '3.2'
42
43
  required_rubygems_version: !ruby/object:Gem::Requirement
43
44
  requirements:
44
45
  - - ">="