bundler 2.6.8 → 2.6.9
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 +4 -4
- data/CHANGELOG.md +13 -0
- data/lib/bundler/build_metadata.rb +1 -1
- data/lib/bundler/cli/doctor/diagnose.rb +167 -0
- data/lib/bundler/cli/doctor/ssl.rb +249 -0
- data/lib/bundler/cli/doctor.rb +27 -155
- data/lib/bundler/cli/issue.rb +2 -2
- data/lib/bundler/cli.rb +2 -11
- data/lib/bundler/definition.rb +35 -37
- data/lib/bundler/lazy_specification.rb +22 -13
- data/lib/bundler/resolver.rb +10 -6
- data/lib/bundler/spec_set.rb +21 -3
- data/lib/bundler/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 855ce06e8526e58b49de6b5872997ade47beb38288f5cf783ad551417accfa2c
|
4
|
+
data.tar.gz: bfa8b4371917aa0b993a31d9452196c00239de57803b0a233795040be5e158f0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bdf0dab626499ae76ef5fae750d9354c8c064fc19cb4b357de8e11bb1036246d8b0aed7fe12d2dbec3a81745db31b6268c4c4fec14111c5b224c96217834f8d9
|
7
|
+
data.tar.gz: c297223566644f831412d15723e0cd96a1f9d7c9d6d2985596716dd1a00d7b3f386014402403fbefb0fa0805e36662de35268bd3b2ab7168ce08f1ee34a31f29
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,16 @@
|
|
1
|
+
# 2.6.9 (May 13, 2025)
|
2
|
+
|
3
|
+
## Enhancements:
|
4
|
+
|
5
|
+
- Fix doctor command parsing of otool output [#8665](https://github.com/rubygems/rubygems/pull/8665)
|
6
|
+
- Add SSL troubleshooting to `bundle doctor` [#8624](https://github.com/rubygems/rubygems/pull/8624)
|
7
|
+
- Let `bundle lock --normalize-platforms` remove invalid platforms [#8631](https://github.com/rubygems/rubygems/pull/8631)
|
8
|
+
|
9
|
+
## Bug fixes:
|
10
|
+
|
11
|
+
- Fix `bundle lock` sometimes allowing invalid platforms into the lockfile [#8630](https://github.com/rubygems/rubygems/pull/8630)
|
12
|
+
- Fix false positive warning about insecure materialization in frozen mode [#8629](https://github.com/rubygems/rubygems/pull/8629)
|
13
|
+
|
1
14
|
# 2.6.8 (April 13, 2025)
|
2
15
|
|
3
16
|
## Enhancements:
|
@@ -0,0 +1,167 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rbconfig"
|
4
|
+
require "shellwords"
|
5
|
+
|
6
|
+
module Bundler
|
7
|
+
class CLI::Doctor::Diagnose
|
8
|
+
DARWIN_REGEX = /\s+(.+) \(compatibility /
|
9
|
+
LDD_REGEX = /\t\S+ => (\S+) \(\S+\)/
|
10
|
+
|
11
|
+
attr_reader :options
|
12
|
+
|
13
|
+
def initialize(options)
|
14
|
+
@options = options
|
15
|
+
end
|
16
|
+
|
17
|
+
def otool_available?
|
18
|
+
Bundler.which("otool")
|
19
|
+
end
|
20
|
+
|
21
|
+
def ldd_available?
|
22
|
+
Bundler.which("ldd")
|
23
|
+
end
|
24
|
+
|
25
|
+
def dylibs_darwin(path)
|
26
|
+
output = `/usr/bin/otool -L #{path.shellescape}`.chomp
|
27
|
+
dylibs = output.split("\n")[1..-1].filter_map {|l| l.match(DARWIN_REGEX)&.match(1) }.uniq
|
28
|
+
# ignore @rpath and friends
|
29
|
+
dylibs.reject {|dylib| dylib.start_with? "@" }
|
30
|
+
end
|
31
|
+
|
32
|
+
def dylibs_ldd(path)
|
33
|
+
output = `/usr/bin/ldd #{path.shellescape}`.chomp
|
34
|
+
output.split("\n").filter_map do |l|
|
35
|
+
match = l.match(LDD_REGEX)
|
36
|
+
next if match.nil?
|
37
|
+
match.captures[0]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def dylibs(path)
|
42
|
+
case RbConfig::CONFIG["host_os"]
|
43
|
+
when /darwin/
|
44
|
+
return [] unless otool_available?
|
45
|
+
dylibs_darwin(path)
|
46
|
+
when /(linux|solaris|bsd)/
|
47
|
+
return [] unless ldd_available?
|
48
|
+
dylibs_ldd(path)
|
49
|
+
else # Windows, etc.
|
50
|
+
Bundler.ui.warn("Dynamic library check not supported on this platform.")
|
51
|
+
[]
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def bundles_for_gem(spec)
|
56
|
+
Dir.glob("#{spec.full_gem_path}/**/*.bundle")
|
57
|
+
end
|
58
|
+
|
59
|
+
def lookup_with_fiddle(path)
|
60
|
+
require "fiddle"
|
61
|
+
Fiddle.dlopen(path)
|
62
|
+
false
|
63
|
+
rescue Fiddle::DLError
|
64
|
+
true
|
65
|
+
end
|
66
|
+
|
67
|
+
def check!
|
68
|
+
require_relative "../check"
|
69
|
+
Bundler::CLI::Check.new({}).run
|
70
|
+
end
|
71
|
+
|
72
|
+
def diagnose_ssl
|
73
|
+
require_relative "ssl"
|
74
|
+
Bundler::CLI::Doctor::SSL.new({}).run
|
75
|
+
end
|
76
|
+
|
77
|
+
def run
|
78
|
+
Bundler.ui.level = "warn" if options[:quiet]
|
79
|
+
Bundler.settings.validate!
|
80
|
+
check!
|
81
|
+
diagnose_ssl if options[:ssl]
|
82
|
+
|
83
|
+
definition = Bundler.definition
|
84
|
+
broken_links = {}
|
85
|
+
|
86
|
+
definition.specs.each do |spec|
|
87
|
+
bundles_for_gem(spec).each do |bundle|
|
88
|
+
bad_paths = dylibs(bundle).select do |f|
|
89
|
+
lookup_with_fiddle(f)
|
90
|
+
end
|
91
|
+
if bad_paths.any?
|
92
|
+
broken_links[spec] ||= []
|
93
|
+
broken_links[spec].concat(bad_paths)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
permissions_valid = check_home_permissions
|
99
|
+
|
100
|
+
if broken_links.any?
|
101
|
+
message = "The following gems are missing OS dependencies:"
|
102
|
+
broken_links.flat_map do |spec, paths|
|
103
|
+
paths.uniq.map do |path|
|
104
|
+
"\n * #{spec.name}: #{path}"
|
105
|
+
end
|
106
|
+
end.sort.each {|m| message += m }
|
107
|
+
raise ProductionError, message
|
108
|
+
elsif permissions_valid
|
109
|
+
Bundler.ui.info "No issues found with the installed bundle"
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
private
|
114
|
+
|
115
|
+
def check_home_permissions
|
116
|
+
require "find"
|
117
|
+
files_not_readable = []
|
118
|
+
files_not_readable_and_owned_by_different_user = []
|
119
|
+
files_not_owned_by_current_user_but_still_readable = []
|
120
|
+
broken_symlinks = []
|
121
|
+
Find.find(Bundler.bundle_path.to_s).each do |f|
|
122
|
+
if !File.exist?(f)
|
123
|
+
broken_symlinks << f
|
124
|
+
elsif !File.readable?(f)
|
125
|
+
if File.stat(f).uid != Process.uid
|
126
|
+
files_not_readable_and_owned_by_different_user << f
|
127
|
+
else
|
128
|
+
files_not_readable << f
|
129
|
+
end
|
130
|
+
elsif File.stat(f).uid != Process.uid
|
131
|
+
files_not_owned_by_current_user_but_still_readable << f
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
ok = true
|
136
|
+
|
137
|
+
if broken_symlinks.any?
|
138
|
+
Bundler.ui.warn "Broken links exist in the Bundler home. Please report them to the offending gem's upstream repo. These files are:\n - #{broken_symlinks.join("\n - ")}"
|
139
|
+
|
140
|
+
ok = false
|
141
|
+
end
|
142
|
+
|
143
|
+
if files_not_owned_by_current_user_but_still_readable.any?
|
144
|
+
Bundler.ui.warn "Files exist in the Bundler home that are owned by another " \
|
145
|
+
"user, but are still readable. These files are:\n - #{files_not_owned_by_current_user_but_still_readable.join("\n - ")}"
|
146
|
+
|
147
|
+
ok = false
|
148
|
+
end
|
149
|
+
|
150
|
+
if files_not_readable_and_owned_by_different_user.any?
|
151
|
+
Bundler.ui.warn "Files exist in the Bundler home that are owned by another " \
|
152
|
+
"user, and are not readable. These files are:\n - #{files_not_readable_and_owned_by_different_user.join("\n - ")}"
|
153
|
+
|
154
|
+
ok = false
|
155
|
+
end
|
156
|
+
|
157
|
+
if files_not_readable.any?
|
158
|
+
Bundler.ui.warn "Files exist in the Bundler home that are not " \
|
159
|
+
"readable by the current user. These files are:\n - #{files_not_readable.join("\n - ")}"
|
160
|
+
|
161
|
+
ok = false
|
162
|
+
end
|
163
|
+
|
164
|
+
ok
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
@@ -0,0 +1,249 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rubygems/remote_fetcher"
|
4
|
+
require "uri"
|
5
|
+
|
6
|
+
module Bundler
|
7
|
+
class CLI::Doctor::SSL
|
8
|
+
attr_reader :options
|
9
|
+
|
10
|
+
def initialize(options)
|
11
|
+
@options = options
|
12
|
+
end
|
13
|
+
|
14
|
+
def run
|
15
|
+
return unless openssl_installed?
|
16
|
+
|
17
|
+
output_ssl_environment
|
18
|
+
bundler_success = bundler_connection_successful?
|
19
|
+
rubygem_success = rubygem_connection_successful?
|
20
|
+
|
21
|
+
return unless net_http_connection_successful?
|
22
|
+
|
23
|
+
Explanation.summarize(bundler_success, rubygem_success, host)
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def host
|
29
|
+
@options[:host] || "rubygems.org"
|
30
|
+
end
|
31
|
+
|
32
|
+
def tls_version
|
33
|
+
@options[:"tls-version"].then do |version|
|
34
|
+
"TLS#{version.sub(".", "_")}".to_sym if version
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def verify_mode
|
39
|
+
mode = @options[:"verify-mode"] || :peer
|
40
|
+
|
41
|
+
@verify_mode ||= mode.then {|mod| OpenSSL::SSL.const_get("verify_#{mod}".upcase) }
|
42
|
+
end
|
43
|
+
|
44
|
+
def uri
|
45
|
+
@uri ||= URI("https://#{host}")
|
46
|
+
end
|
47
|
+
|
48
|
+
def openssl_installed?
|
49
|
+
require "openssl"
|
50
|
+
|
51
|
+
true
|
52
|
+
rescue LoadError
|
53
|
+
Bundler.ui.warn(<<~MSG)
|
54
|
+
Oh no! Your Ruby doesn't have OpenSSL, so it can't connect to #{host}.
|
55
|
+
You'll need to recompile or reinstall Ruby with OpenSSL support and try again.
|
56
|
+
MSG
|
57
|
+
|
58
|
+
false
|
59
|
+
end
|
60
|
+
|
61
|
+
def output_ssl_environment
|
62
|
+
Bundler.ui.info(<<~MESSAGE)
|
63
|
+
Here's your OpenSSL environment:
|
64
|
+
|
65
|
+
OpenSSL: #{OpenSSL::VERSION}
|
66
|
+
Compiled with: #{OpenSSL::OPENSSL_VERSION}
|
67
|
+
Loaded with: #{OpenSSL::OPENSSL_LIBRARY_VERSION}
|
68
|
+
MESSAGE
|
69
|
+
end
|
70
|
+
|
71
|
+
def bundler_connection_successful?
|
72
|
+
Bundler.ui.info("\nTrying connections to #{uri}:\n")
|
73
|
+
|
74
|
+
bundler_uri = Gem::URI(uri.to_s)
|
75
|
+
Bundler::Fetcher.new(
|
76
|
+
Bundler::Source::Rubygems::Remote.new(bundler_uri)
|
77
|
+
).send(:connection).request(bundler_uri)
|
78
|
+
|
79
|
+
Bundler.ui.info("Bundler: success")
|
80
|
+
|
81
|
+
true
|
82
|
+
rescue StandardError => error
|
83
|
+
Bundler.ui.warn("Bundler: failed (#{Explanation.explain_bundler_or_rubygems_error(error)})")
|
84
|
+
|
85
|
+
false
|
86
|
+
end
|
87
|
+
|
88
|
+
def rubygem_connection_successful?
|
89
|
+
Gem::RemoteFetcher.fetcher.fetch_path(uri)
|
90
|
+
Bundler.ui.info("RubyGems: success")
|
91
|
+
|
92
|
+
true
|
93
|
+
rescue StandardError => error
|
94
|
+
Bundler.ui.warn("RubyGems: failed (#{Explanation.explain_bundler_or_rubygems_error(error)})")
|
95
|
+
|
96
|
+
false
|
97
|
+
end
|
98
|
+
|
99
|
+
def net_http_connection_successful?
|
100
|
+
::Gem::Net::HTTP.new(uri.host, uri.port).tap do |http|
|
101
|
+
http.use_ssl = true
|
102
|
+
http.min_version = tls_version
|
103
|
+
http.max_version = tls_version
|
104
|
+
http.verify_mode = verify_mode
|
105
|
+
end.start
|
106
|
+
|
107
|
+
Bundler.ui.info("Ruby net/http: success")
|
108
|
+
warn_on_unsupported_tls12
|
109
|
+
|
110
|
+
true
|
111
|
+
rescue StandardError => error
|
112
|
+
Bundler.ui.warn(<<~MSG)
|
113
|
+
Ruby net/http: failed
|
114
|
+
|
115
|
+
Unfortunately, this Ruby can't connect to #{host}.
|
116
|
+
|
117
|
+
#{Explanation.explain_net_http_error(error, host, tls_version)}
|
118
|
+
MSG
|
119
|
+
|
120
|
+
false
|
121
|
+
end
|
122
|
+
|
123
|
+
def warn_on_unsupported_tls12
|
124
|
+
ctx = OpenSSL::SSL::SSLContext.new
|
125
|
+
supported = true
|
126
|
+
|
127
|
+
if ctx.respond_to?(:min_version=)
|
128
|
+
begin
|
129
|
+
ctx.min_version = ctx.max_version = OpenSSL::SSL::TLS1_2_VERSION
|
130
|
+
rescue OpenSSL::SSL::SSLError, NameError
|
131
|
+
supported = false
|
132
|
+
end
|
133
|
+
else
|
134
|
+
supported = OpenSSL::SSL::SSLContext::METHODS.include?(:TLSv1_2) # rubocop:disable Naming/VariableNumber
|
135
|
+
end
|
136
|
+
|
137
|
+
Bundler.ui.warn(<<~EOM) unless supported
|
138
|
+
|
139
|
+
WARNING: Although your Ruby can connect to #{host} today, your OpenSSL is very old!
|
140
|
+
WARNING: You will need to upgrade OpenSSL to use #{host}.
|
141
|
+
|
142
|
+
EOM
|
143
|
+
end
|
144
|
+
|
145
|
+
module Explanation
|
146
|
+
extend self
|
147
|
+
|
148
|
+
def explain_bundler_or_rubygems_error(error)
|
149
|
+
case error.message
|
150
|
+
when /certificate verify failed/
|
151
|
+
"certificate verification"
|
152
|
+
when /read server hello A/
|
153
|
+
"SSL/TLS protocol version mismatch"
|
154
|
+
when /tlsv1 alert protocol version/
|
155
|
+
"requested TLS version is too old"
|
156
|
+
else
|
157
|
+
error.message
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
def explain_net_http_error(error, host, tls_version)
|
162
|
+
case error.message
|
163
|
+
# Check for certificate errors
|
164
|
+
when /certificate verify failed/
|
165
|
+
<<~MSG
|
166
|
+
#{show_ssl_certs}
|
167
|
+
Your Ruby can't connect to #{host} because you are missing the certificate files OpenSSL needs to verify you are connecting to the genuine #{host} servers.
|
168
|
+
MSG
|
169
|
+
# Check for TLS version errors
|
170
|
+
when /read server hello A/, /tlsv1 alert protocol version/
|
171
|
+
if tls_version.to_s == "TLS1_3"
|
172
|
+
"Your Ruby can't connect to #{host} because #{tls_version} isn't supported yet.\n"
|
173
|
+
else
|
174
|
+
<<~MSG
|
175
|
+
Your Ruby can't connect to #{host} because your version of OpenSSL is too old.
|
176
|
+
You'll need to upgrade your OpenSSL install and/or recompile Ruby to use a newer OpenSSL.
|
177
|
+
MSG
|
178
|
+
end
|
179
|
+
# OpenSSL doesn't support TLS version specified by argument
|
180
|
+
when /unknown SSL method/
|
181
|
+
"Your Ruby can't connect because #{tls_version} isn't supported by your version of OpenSSL."
|
182
|
+
else
|
183
|
+
<<~MSG
|
184
|
+
Even worse, we're not sure why.
|
185
|
+
|
186
|
+
Here's the full error information:
|
187
|
+
#{error.class}: #{error.message}
|
188
|
+
#{error.backtrace.join("\n ")}
|
189
|
+
|
190
|
+
You might have more luck using Mislav's SSL doctor.rb script. You can get it here:
|
191
|
+
https://github.com/mislav/ssl-tools/blob/8b3dec4/doctor.rb
|
192
|
+
|
193
|
+
Read more about the script and how to use it in this blog post:
|
194
|
+
https://mislav.net/2013/07/ruby-openssl/
|
195
|
+
MSG
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
def summarize(bundler_success, rubygems_success, host)
|
200
|
+
guide_url = "http://ruby.to/ssl-check-failed"
|
201
|
+
|
202
|
+
message = if bundler_success && rubygems_success
|
203
|
+
<<~MSG
|
204
|
+
Hooray! This Ruby can connect to #{host}.
|
205
|
+
You are all set to use Bundler and RubyGems.
|
206
|
+
|
207
|
+
MSG
|
208
|
+
elsif !bundler_success && !rubygems_success
|
209
|
+
<<~MSG
|
210
|
+
For some reason, your Ruby installation can connect to #{host}, but neither RubyGems nor Bundler can.
|
211
|
+
The most likely fix is to manually upgrade RubyGems by following the instructions at #{guide_url}.
|
212
|
+
After you've done that, run `gem install bundler` to upgrade Bundler, and then run this script again to make sure everything worked. ❣
|
213
|
+
|
214
|
+
MSG
|
215
|
+
elsif !bundler_success
|
216
|
+
<<~MSG
|
217
|
+
Although your Ruby installation and RubyGems can both connect to #{host}, Bundler is having trouble.
|
218
|
+
The most likely way to fix this is to upgrade Bundler by running `gem install bundler`.
|
219
|
+
Run this script again after doing that to make sure everything is all set.
|
220
|
+
If you're still having trouble, check out the troubleshooting guide at #{guide_url}.
|
221
|
+
|
222
|
+
MSG
|
223
|
+
else
|
224
|
+
<<~MSG
|
225
|
+
It looks like Ruby and Bundler can connect to #{host}, but RubyGems itself cannot.
|
226
|
+
You can likely solve this by manually downloading and installing a RubyGems update.
|
227
|
+
Visit #{guide_url} for instructions on how to manually upgrade RubyGems.
|
228
|
+
|
229
|
+
MSG
|
230
|
+
end
|
231
|
+
|
232
|
+
Bundler.ui.info("\n#{message}")
|
233
|
+
end
|
234
|
+
|
235
|
+
private
|
236
|
+
|
237
|
+
def show_ssl_certs
|
238
|
+
ssl_cert_file = ENV["SSL_CERT_FILE"] || OpenSSL::X509::DEFAULT_CERT_FILE
|
239
|
+
ssl_cert_dir = ENV["SSL_CERT_DIR"] || OpenSSL::X509::DEFAULT_CERT_DIR
|
240
|
+
|
241
|
+
<<~MSG
|
242
|
+
Below affect only Ruby net/http connections:
|
243
|
+
SSL_CERT_FILE: #{File.exist?(ssl_cert_file) ? "exists #{ssl_cert_file}" : "is missing #{ssl_cert_file}"}
|
244
|
+
SSL_CERT_DIR: #{Dir.exist?(ssl_cert_dir) ? "exists #{ssl_cert_dir}" : "is missing #{ssl_cert_dir}"}
|
245
|
+
MSG
|
246
|
+
end
|
247
|
+
end
|
248
|
+
end
|
249
|
+
end
|
data/lib/bundler/cli/doctor.rb
CHANGED
@@ -1,161 +1,33 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "rbconfig"
|
4
|
-
require "shellwords"
|
5
|
-
|
6
3
|
module Bundler
|
7
|
-
class CLI::Doctor
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
output.split("\n").filter_map do |l|
|
35
|
-
match = l.match(LDD_REGEX)
|
36
|
-
next if match.nil?
|
37
|
-
match.captures[0]
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
def dylibs(path)
|
42
|
-
case RbConfig::CONFIG["host_os"]
|
43
|
-
when /darwin/
|
44
|
-
return [] unless otool_available?
|
45
|
-
dylibs_darwin(path)
|
46
|
-
when /(linux|solaris|bsd)/
|
47
|
-
return [] unless ldd_available?
|
48
|
-
dylibs_ldd(path)
|
49
|
-
else # Windows, etc.
|
50
|
-
Bundler.ui.warn("Dynamic library check not supported on this platform.")
|
51
|
-
[]
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
def bundles_for_gem(spec)
|
56
|
-
Dir.glob("#{spec.full_gem_path}/**/*.bundle")
|
57
|
-
end
|
58
|
-
|
59
|
-
def lookup_with_fiddle(path)
|
60
|
-
require "fiddle"
|
61
|
-
Fiddle.dlopen(path)
|
62
|
-
false
|
63
|
-
rescue Fiddle::DLError
|
64
|
-
true
|
65
|
-
end
|
66
|
-
|
67
|
-
def check!
|
68
|
-
require_relative "check"
|
69
|
-
Bundler::CLI::Check.new({}).run
|
70
|
-
end
|
71
|
-
|
72
|
-
def run
|
73
|
-
Bundler.ui.level = "warn" if options[:quiet]
|
74
|
-
Bundler.settings.validate!
|
75
|
-
check!
|
76
|
-
|
77
|
-
definition = Bundler.definition
|
78
|
-
broken_links = {}
|
79
|
-
|
80
|
-
definition.specs.each do |spec|
|
81
|
-
bundles_for_gem(spec).each do |bundle|
|
82
|
-
bad_paths = dylibs(bundle).select do |f|
|
83
|
-
lookup_with_fiddle(f)
|
84
|
-
end
|
85
|
-
if bad_paths.any?
|
86
|
-
broken_links[spec] ||= []
|
87
|
-
broken_links[spec].concat(bad_paths)
|
88
|
-
end
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
permissions_valid = check_home_permissions
|
93
|
-
|
94
|
-
if broken_links.any?
|
95
|
-
message = "The following gems are missing OS dependencies:"
|
96
|
-
broken_links.flat_map do |spec, paths|
|
97
|
-
paths.uniq.map do |path|
|
98
|
-
"\n * #{spec.name}: #{path}"
|
99
|
-
end
|
100
|
-
end.sort.each {|m| message += m }
|
101
|
-
raise ProductionError, message
|
102
|
-
elsif permissions_valid
|
103
|
-
Bundler.ui.info "No issues found with the installed bundle"
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
|
-
private
|
108
|
-
|
109
|
-
def check_home_permissions
|
110
|
-
require "find"
|
111
|
-
files_not_readable = []
|
112
|
-
files_not_readable_and_owned_by_different_user = []
|
113
|
-
files_not_owned_by_current_user_but_still_readable = []
|
114
|
-
broken_symlinks = []
|
115
|
-
Find.find(Bundler.bundle_path.to_s).each do |f|
|
116
|
-
if !File.exist?(f)
|
117
|
-
broken_symlinks << f
|
118
|
-
elsif !File.readable?(f)
|
119
|
-
if File.stat(f).uid != Process.uid
|
120
|
-
files_not_readable_and_owned_by_different_user << f
|
121
|
-
else
|
122
|
-
files_not_readable << f
|
123
|
-
end
|
124
|
-
elsif File.stat(f).uid != Process.uid
|
125
|
-
files_not_owned_by_current_user_but_still_readable << f
|
126
|
-
end
|
127
|
-
end
|
128
|
-
|
129
|
-
ok = true
|
130
|
-
|
131
|
-
if broken_symlinks.any?
|
132
|
-
Bundler.ui.warn "Broken links exist in the Bundler home. Please report them to the offending gem's upstream repo. These files are:\n - #{broken_symlinks.join("\n - ")}"
|
133
|
-
|
134
|
-
ok = false
|
135
|
-
end
|
136
|
-
|
137
|
-
if files_not_owned_by_current_user_but_still_readable.any?
|
138
|
-
Bundler.ui.warn "Files exist in the Bundler home that are owned by another " \
|
139
|
-
"user, but are still readable. These files are:\n - #{files_not_owned_by_current_user_but_still_readable.join("\n - ")}"
|
140
|
-
|
141
|
-
ok = false
|
142
|
-
end
|
143
|
-
|
144
|
-
if files_not_readable_and_owned_by_different_user.any?
|
145
|
-
Bundler.ui.warn "Files exist in the Bundler home that are owned by another " \
|
146
|
-
"user, and are not readable. These files are:\n - #{files_not_readable_and_owned_by_different_user.join("\n - ")}"
|
147
|
-
|
148
|
-
ok = false
|
149
|
-
end
|
150
|
-
|
151
|
-
if files_not_readable.any?
|
152
|
-
Bundler.ui.warn "Files exist in the Bundler home that are not " \
|
153
|
-
"readable by the current user. These files are:\n - #{files_not_readable.join("\n - ")}"
|
154
|
-
|
155
|
-
ok = false
|
156
|
-
end
|
157
|
-
|
158
|
-
ok
|
4
|
+
class CLI::Doctor < Thor
|
5
|
+
default_command(:diagnose)
|
6
|
+
|
7
|
+
desc "diagnose [OPTIONS]", "Checks the bundle for common problems"
|
8
|
+
long_desc <<-D
|
9
|
+
Doctor scans the OS dependencies of each of the gems requested in the Gemfile. If
|
10
|
+
missing dependencies are detected, Bundler prints them and exits status 1.
|
11
|
+
Otherwise, Bundler prints a success message and exits with a status of 0.
|
12
|
+
D
|
13
|
+
method_option "gemfile", type: :string, banner: "Use the specified gemfile instead of Gemfile"
|
14
|
+
method_option "quiet", type: :boolean, banner: "Only output warnings and errors."
|
15
|
+
method_option "ssl", type: :boolean, default: false, banner: "Diagnose SSL problems."
|
16
|
+
def diagnose
|
17
|
+
require_relative "doctor/diagnose"
|
18
|
+
Diagnose.new(options).run
|
19
|
+
end
|
20
|
+
|
21
|
+
desc "ssl [OPTIONS]", "Diagnose SSL problems"
|
22
|
+
long_desc <<-D
|
23
|
+
Diagnose SSL problems, especially related to certificates or TLS version while connecting to https://rubygems.org.
|
24
|
+
D
|
25
|
+
method_option "host", type: :string, banner: "The host to diagnose."
|
26
|
+
method_option "tls-version", type: :string, banner: "Specify the SSL/TLS version when running the diagnostic. Accepts either <1.1> or <1.2>"
|
27
|
+
method_option "verify-mode", type: :string, banner: "Specify the mode used for certification verification. Accepts either <peer> or <none>"
|
28
|
+
def ssl
|
29
|
+
require_relative "doctor/ssl"
|
30
|
+
SSL.new(options).run
|
159
31
|
end
|
160
32
|
end
|
161
33
|
end
|
data/lib/bundler/cli/issue.rb
CHANGED
data/lib/bundler/cli.rb
CHANGED
@@ -610,17 +610,8 @@ module Bundler
|
|
610
610
|
end
|
611
611
|
|
612
612
|
desc "doctor [OPTIONS]", "Checks the bundle for common problems"
|
613
|
-
|
614
|
-
|
615
|
-
missing dependencies are detected, Bundler prints them and exits status 1.
|
616
|
-
Otherwise, Bundler prints a success message and exits with a status of 0.
|
617
|
-
D
|
618
|
-
method_option "gemfile", type: :string, banner: "Use the specified gemfile instead of Gemfile"
|
619
|
-
method_option "quiet", type: :boolean, banner: "Only output warnings and errors."
|
620
|
-
def doctor
|
621
|
-
require_relative "cli/doctor"
|
622
|
-
Doctor.new(options).run
|
623
|
-
end
|
613
|
+
require_relative "cli/doctor"
|
614
|
+
subcommand("doctor", Doctor)
|
624
615
|
|
625
616
|
desc "issue", "Learn how to report an issue in Bundler"
|
626
617
|
def issue
|
data/lib/bundler/definition.rb
CHANGED
@@ -257,7 +257,7 @@ module Bundler
|
|
257
257
|
rescue BundlerError => e
|
258
258
|
@resolve = nil
|
259
259
|
@resolver = nil
|
260
|
-
@
|
260
|
+
@resolution_base = nil
|
261
261
|
@source_requirements = nil
|
262
262
|
@specs = nil
|
263
263
|
|
@@ -614,7 +614,7 @@ module Bundler
|
|
614
614
|
end
|
615
615
|
|
616
616
|
def resolver
|
617
|
-
@resolver ||= Resolver.new(
|
617
|
+
@resolver ||= Resolver.new(resolution_base, gem_version_promoter, @most_specific_locked_platform)
|
618
618
|
end
|
619
619
|
|
620
620
|
def expanded_dependencies
|
@@ -628,15 +628,15 @@ module Bundler
|
|
628
628
|
[Dependency.new("bundler", @unlocking_bundler)] + dependencies
|
629
629
|
end
|
630
630
|
|
631
|
-
def
|
632
|
-
@
|
631
|
+
def resolution_base
|
632
|
+
@resolution_base ||= begin
|
633
633
|
last_resolve = converge_locked_specs
|
634
634
|
remove_invalid_platforms!
|
635
635
|
new_resolution_platforms = @current_platform_missing ? @new_platforms + [local_platform] : @new_platforms
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
|
636
|
+
base = Resolver::Base.new(source_requirements, expanded_dependencies, last_resolve, @platforms, locked_specs: @originally_locked_specs, unlock: @unlocking_all || @gems_to_unlock, prerelease: gem_version_promoter.pre?, prefer_local: @prefer_local, new_platforms: new_resolution_platforms)
|
637
|
+
base = additional_base_requirements_to_prevent_downgrades(base)
|
638
|
+
base = additional_base_requirements_to_force_updates(base)
|
639
|
+
base
|
640
640
|
end
|
641
641
|
end
|
642
642
|
|
@@ -711,8 +711,7 @@ module Bundler
|
|
711
711
|
still_incomplete_specs = resolve.incomplete_specs
|
712
712
|
|
713
713
|
if still_incomplete_specs == incomplete_specs
|
714
|
-
|
715
|
-
resolver.raise_not_found! package
|
714
|
+
resolver.raise_incomplete! incomplete_specs
|
716
715
|
end
|
717
716
|
|
718
717
|
incomplete_specs = still_incomplete_specs
|
@@ -734,7 +733,7 @@ module Bundler
|
|
734
733
|
end
|
735
734
|
|
736
735
|
def reresolve_without(incomplete_specs)
|
737
|
-
|
736
|
+
resolution_base.delete(incomplete_specs)
|
738
737
|
@resolve = start_resolution
|
739
738
|
end
|
740
739
|
|
@@ -747,8 +746,16 @@ module Bundler
|
|
747
746
|
|
748
747
|
@resolved_bundler_version = result.find {|spec| spec.name == "bundler" }&.version
|
749
748
|
|
749
|
+
@new_platforms.each do |platform|
|
750
|
+
incomplete_specs = result.incomplete_specs_for_platform(current_dependencies, platform)
|
751
|
+
|
752
|
+
if incomplete_specs.any?
|
753
|
+
resolver.raise_incomplete! incomplete_specs
|
754
|
+
end
|
755
|
+
end
|
756
|
+
|
750
757
|
if @most_specific_non_local_locked_platform
|
751
|
-
if
|
758
|
+
if result.incomplete_for_platform?(current_dependencies, @most_specific_non_local_locked_platform)
|
752
759
|
@platforms.delete(@most_specific_non_local_locked_platform)
|
753
760
|
elsif local_platform_needed_for_resolvability
|
754
761
|
@platforms.delete(local_platform)
|
@@ -1124,27 +1131,27 @@ module Bundler
|
|
1124
1131
|
current == proposed
|
1125
1132
|
end
|
1126
1133
|
|
1127
|
-
def additional_base_requirements_to_prevent_downgrades(
|
1128
|
-
return
|
1134
|
+
def additional_base_requirements_to_prevent_downgrades(resolution_base)
|
1135
|
+
return resolution_base unless @locked_gems && !sources.expired_sources?(@locked_gems.sources)
|
1129
1136
|
@originally_locked_specs.each do |locked_spec|
|
1130
1137
|
next if locked_spec.source.is_a?(Source::Path)
|
1131
1138
|
|
1132
1139
|
name = locked_spec.name
|
1133
1140
|
next if @changed_dependencies.include?(name)
|
1134
1141
|
|
1135
|
-
|
1142
|
+
resolution_base.base_requirements[name] = Gem::Requirement.new(">= #{locked_spec.version}")
|
1136
1143
|
end
|
1137
|
-
|
1144
|
+
resolution_base
|
1138
1145
|
end
|
1139
1146
|
|
1140
|
-
def additional_base_requirements_to_force_updates(
|
1141
|
-
return
|
1147
|
+
def additional_base_requirements_to_force_updates(resolution_base)
|
1148
|
+
return resolution_base if @explicit_unlocks.empty?
|
1142
1149
|
full_update = dup_for_full_unlock.resolve
|
1143
1150
|
@explicit_unlocks.each do |name|
|
1144
1151
|
version = full_update.version_for(name)
|
1145
|
-
|
1152
|
+
resolution_base.base_requirements[name] = Gem::Requirement.new("= #{version}") if version
|
1146
1153
|
end
|
1147
|
-
|
1154
|
+
resolution_base
|
1148
1155
|
end
|
1149
1156
|
|
1150
1157
|
def dup_for_full_unlock
|
@@ -1161,25 +1168,16 @@ module Bundler
|
|
1161
1168
|
def remove_invalid_platforms!
|
1162
1169
|
return if Bundler.frozen_bundle?
|
1163
1170
|
|
1164
|
-
|
1165
|
-
next if local_platform == platform ||
|
1166
|
-
@new_platforms.include?(platform)
|
1171
|
+
skips = (@new_platforms + [local_platform]).uniq
|
1167
1172
|
|
1168
|
-
|
1169
|
-
|
1170
|
-
|
1171
|
-
|
1172
|
-
|
1173
|
-
|
1174
|
-
|
1175
|
-
spec_set_incomplete_for_platform?(@originally_locked_specs, platform)
|
1176
|
-
end
|
1177
|
-
|
1178
|
-
@platforms -= @originally_invalid_platforms
|
1179
|
-
end
|
1173
|
+
# We should probably avoid removing non-ruby platforms, since that means
|
1174
|
+
# lockfile will no longer install on those platforms, so a error to give
|
1175
|
+
# heads up to the user may be better. However, we have tests expecting
|
1176
|
+
# non ruby platform autoremoval to work, so leaving that in place for
|
1177
|
+
# now.
|
1178
|
+
skips |= platforms - [Gem::Platform::RUBY] if @dependency_changes
|
1180
1179
|
|
1181
|
-
|
1182
|
-
spec_set.incomplete_for_platform?(current_dependencies, platform)
|
1180
|
+
@originally_invalid_platforms = @originally_locked_specs.remove_invalid_platforms!(current_dependencies, platforms, skips: skips)
|
1183
1181
|
end
|
1184
1182
|
|
1185
1183
|
def source_map
|
@@ -213,22 +213,31 @@ module Bundler
|
|
213
213
|
end
|
214
214
|
if search.nil? && fallback_to_non_installable
|
215
215
|
search = candidates.last
|
216
|
-
|
217
|
-
# We don't validate locally installed dependencies but accept what's in
|
218
|
-
# the lockfile instead for performance, since loading locally installed
|
219
|
-
# dependencies would mean evaluating all gemspecs, which would affect
|
220
|
-
# `bundler/setup` performance
|
221
|
-
if search.is_a?(StubSpecification)
|
222
|
-
search.dependencies = dependencies
|
223
|
-
else
|
224
|
-
if !source.is_a?(Source::Path) && search.runtime_dependencies.sort != dependencies.sort
|
225
|
-
raise IncorrectLockfileDependencies.new(self)
|
226
|
-
end
|
216
|
+
end
|
227
217
|
|
228
|
-
|
229
|
-
|
218
|
+
if search
|
219
|
+
validate_dependencies(search) if search.platform == platform
|
220
|
+
|
221
|
+
search.locked_platform = platform if search.instance_of?(RemoteSpecification) || search.instance_of?(EndpointSpecification)
|
230
222
|
end
|
231
223
|
search
|
232
224
|
end
|
225
|
+
|
226
|
+
# Validate dependencies of this locked spec are consistent with dependencies
|
227
|
+
# of the actual spec that was materialized.
|
228
|
+
#
|
229
|
+
# Note that we don't validate dependencies of locally installed gems but
|
230
|
+
# accept what's in the lockfile instead for performance, since loading
|
231
|
+
# dependencies of locally installed gems would mean evaluating all gemspecs,
|
232
|
+
# which would affect `bundler/setup` performance.
|
233
|
+
def validate_dependencies(spec)
|
234
|
+
if spec.is_a?(StubSpecification)
|
235
|
+
spec.dependencies = dependencies
|
236
|
+
else
|
237
|
+
if !source.is_a?(Source::Path) && spec.runtime_dependencies.sort != dependencies.sort
|
238
|
+
raise IncorrectLockfileDependencies.new(self)
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
233
242
|
end
|
234
243
|
end
|
data/lib/bundler/resolver.rb
CHANGED
@@ -312,6 +312,16 @@ module Bundler
|
|
312
312
|
"Gemfile"
|
313
313
|
end
|
314
314
|
|
315
|
+
def raise_incomplete!(incomplete_specs)
|
316
|
+
raise_not_found!(@base.get_package(incomplete_specs.first.name))
|
317
|
+
end
|
318
|
+
|
319
|
+
def sort_versions_by_preferred(package, versions)
|
320
|
+
@gem_version_promoter.sort_versions(package, versions)
|
321
|
+
end
|
322
|
+
|
323
|
+
private
|
324
|
+
|
315
325
|
def raise_not_found!(package)
|
316
326
|
name = package.name
|
317
327
|
source = source_for(name)
|
@@ -348,12 +358,6 @@ module Bundler
|
|
348
358
|
raise GemNotFound, message
|
349
359
|
end
|
350
360
|
|
351
|
-
def sort_versions_by_preferred(package, versions)
|
352
|
-
@gem_version_promoter.sort_versions(package, versions)
|
353
|
-
end
|
354
|
-
|
355
|
-
private
|
356
|
-
|
357
361
|
def filtered_versions_for(package)
|
358
362
|
@gem_version_promoter.filter_versions(package, @all_versions[package])
|
359
363
|
end
|
data/lib/bundler/spec_set.rb
CHANGED
@@ -29,6 +29,7 @@ module Bundler
|
|
29
29
|
end
|
30
30
|
|
31
31
|
def normalize_platforms!(deps, platforms)
|
32
|
+
remove_invalid_platforms!(deps, platforms)
|
32
33
|
add_extra_platforms!(platforms)
|
33
34
|
|
34
35
|
platforms.map! do |platform|
|
@@ -53,6 +54,20 @@ module Bundler
|
|
53
54
|
end
|
54
55
|
end
|
55
56
|
|
57
|
+
def remove_invalid_platforms!(deps, platforms, skips: [])
|
58
|
+
invalid_platforms = []
|
59
|
+
|
60
|
+
platforms.reject! do |platform|
|
61
|
+
next false if skips.include?(platform)
|
62
|
+
|
63
|
+
invalid = incomplete_for_platform?(deps, platform)
|
64
|
+
invalid_platforms << platform if invalid
|
65
|
+
invalid
|
66
|
+
end
|
67
|
+
|
68
|
+
invalid_platforms
|
69
|
+
end
|
70
|
+
|
56
71
|
def add_extra_platforms!(platforms)
|
57
72
|
if @specs.empty?
|
58
73
|
platforms.concat([Gem::Platform::RUBY]).uniq
|
@@ -130,12 +145,15 @@ module Bundler
|
|
130
145
|
end
|
131
146
|
|
132
147
|
def incomplete_for_platform?(deps, platform)
|
133
|
-
|
148
|
+
incomplete_specs_for_platform(deps, platform).any?
|
149
|
+
end
|
150
|
+
|
151
|
+
def incomplete_specs_for_platform(deps, platform)
|
152
|
+
return [] if @specs.empty?
|
134
153
|
|
135
154
|
validation_set = self.class.new(@specs)
|
136
155
|
validation_set.for(deps, [platform])
|
137
|
-
|
138
|
-
validation_set.incomplete_specs.any?
|
156
|
+
validation_set.incomplete_specs
|
139
157
|
end
|
140
158
|
|
141
159
|
def missing_specs_for(deps)
|
data/lib/bundler/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bundler
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.6.
|
4
|
+
version: 2.6.9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- André Arko
|
@@ -55,6 +55,8 @@ files:
|
|
55
55
|
- lib/bundler/cli/config.rb
|
56
56
|
- lib/bundler/cli/console.rb
|
57
57
|
- lib/bundler/cli/doctor.rb
|
58
|
+
- lib/bundler/cli/doctor/diagnose.rb
|
59
|
+
- lib/bundler/cli/doctor/ssl.rb
|
58
60
|
- lib/bundler/cli/exec.rb
|
59
61
|
- lib/bundler/cli/fund.rb
|
60
62
|
- lib/bundler/cli/gem.rb
|
@@ -412,7 +414,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
412
414
|
- !ruby/object:Gem::Version
|
413
415
|
version: 3.3.3
|
414
416
|
requirements: []
|
415
|
-
rubygems_version: 3.6.
|
417
|
+
rubygems_version: 3.6.9
|
416
418
|
specification_version: 4
|
417
419
|
summary: The best way to manage your application's dependencies
|
418
420
|
test_files: []
|