rubygems-update 4.0.14 → 4.0.16
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 +22 -0
- data/Manifest.txt +1 -2
- data/bundler/CHANGELOG.md +34 -0
- data/bundler/bundler.gemspec +2 -2
- data/bundler/lib/bundler/build_metadata.rb +1 -1
- data/bundler/lib/bundler/cli/gem.rb +1 -2
- data/bundler/lib/bundler/definition.rb +23 -8
- data/bundler/lib/bundler/dsl.rb +4 -1
- data/bundler/lib/bundler/fetcher/base.rb +3 -0
- data/bundler/lib/bundler/fetcher/compact_index.rb +13 -0
- data/bundler/lib/bundler/fetcher/gem_remote_fetcher.rb +2 -0
- data/bundler/lib/bundler/fetcher.rb +5 -1
- data/bundler/lib/bundler/installer/parallel_installer.rb +48 -3
- data/bundler/lib/bundler/installer.rb +7 -1
- data/bundler/lib/bundler/mirror.rb +7 -7
- data/bundler/lib/bundler/plugin/unloaded_source.rb +25 -0
- data/bundler/lib/bundler/plugin.rb +12 -6
- data/bundler/lib/bundler/resolver/base.rb +9 -0
- data/bundler/lib/bundler/resolver.rb +17 -13
- data/bundler/lib/bundler/rubygems_gem_installer.rb +34 -1
- data/bundler/lib/bundler/source/git/git_proxy.rb +6 -0
- data/bundler/lib/bundler/source/rubygems.rb +27 -7
- data/bundler/lib/bundler/source_list.rb +4 -0
- data/bundler/lib/bundler/spec_set.rb +2 -2
- data/bundler/lib/bundler/templates/newgem/newgem.gemspec.tt +1 -1
- data/bundler/lib/bundler/vendored_tsort.rb +19 -2
- data/bundler/lib/bundler/version.rb +1 -1
- data/lib/rubygems/ext/builder.rb +2 -1
- data/lib/rubygems/package.rb +1 -1
- data/lib/rubygems/request.rb +1 -1
- data/lib/rubygems/safe_marshal/reader.rb +4 -36
- data/lib/rubygems.rb +1 -1
- metadata +2 -3
- data/bundler/lib/bundler/vendor/tsort/LICENSE.txt +0 -22
- data/bundler/lib/bundler/vendor/tsort/lib/tsort.rb +0 -455
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: aebfb272578fbad19c368078f6ffda6bdd58efeb1dc8e88a5b5a427e0acd87aa
|
|
4
|
+
data.tar.gz: 2f644c0c209a61d2e958720eff22877e3a48956b8aef92d78ee1f4e872e42c2c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e28f143143e460bf1dd04e184e202723cccc1f686602b012585a662e52f334b8eec464f4d094378a83a630d4ffcc8e398d46f7b03cf0fdac06a57ac364dd3070
|
|
7
|
+
data.tar.gz: e8ba8134f48a8fbf5319e4bd4f238ad9bb51cc31f734a7bde9d84c3ebee67fa5426b023f91ed6f0d34c36c151e82518fcfb4715cda9c566a253cdd61178758ab
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 4.0.16 / 2026-07-10
|
|
4
|
+
|
|
5
|
+
### Enhancements:
|
|
6
|
+
|
|
7
|
+
* Installs bundler 4.0.16 as a default gem.
|
|
8
|
+
|
|
9
|
+
### Bug fixes:
|
|
10
|
+
|
|
11
|
+
* Skip the make job server when using BSD make. Pull request [#9676](https://github.com/ruby/rubygems/pull/9676) by flavorjones
|
|
12
|
+
|
|
13
|
+
## 4.0.15 / 2026-06-24
|
|
14
|
+
|
|
15
|
+
### Enhancements:
|
|
16
|
+
|
|
17
|
+
* Rubygems: Fix Gem::Request for PQC support, adding integration connection tests. Pull request [#9615](https://github.com/ruby/rubygems/pull/9615) by junaruga
|
|
18
|
+
* Reduce peak memory usage of full index loading and bundle install. Pull request [#9618](https://github.com/ruby/rubygems/pull/9618) by hsbt
|
|
19
|
+
* Installs bundler 4.0.15 as a default gem.
|
|
20
|
+
|
|
21
|
+
### Bug fixes:
|
|
22
|
+
|
|
23
|
+
* Forward security policy to old-format gems. Pull request [#9611](https://github.com/ruby/rubygems/pull/9611) by hsbt
|
|
24
|
+
|
|
3
25
|
## 4.0.14 / 2026-06-10
|
|
4
26
|
|
|
5
27
|
### Enhancements:
|
data/Manifest.txt
CHANGED
|
@@ -167,6 +167,7 @@ bundler/lib/bundler/plugin/installer/git.rb
|
|
|
167
167
|
bundler/lib/bundler/plugin/installer/path.rb
|
|
168
168
|
bundler/lib/bundler/plugin/installer/rubygems.rb
|
|
169
169
|
bundler/lib/bundler/plugin/source_list.rb
|
|
170
|
+
bundler/lib/bundler/plugin/unloaded_source.rb
|
|
170
171
|
bundler/lib/bundler/process_lock.rb
|
|
171
172
|
bundler/lib/bundler/remote_specification.rb
|
|
172
173
|
bundler/lib/bundler/resolver.rb
|
|
@@ -322,8 +323,6 @@ bundler/lib/bundler/vendor/thor/lib/thor/shell/terminal.rb
|
|
|
322
323
|
bundler/lib/bundler/vendor/thor/lib/thor/shell/wrapped_printer.rb
|
|
323
324
|
bundler/lib/bundler/vendor/thor/lib/thor/util.rb
|
|
324
325
|
bundler/lib/bundler/vendor/thor/lib/thor/version.rb
|
|
325
|
-
bundler/lib/bundler/vendor/tsort/LICENSE.txt
|
|
326
|
-
bundler/lib/bundler/vendor/tsort/lib/tsort.rb
|
|
327
326
|
bundler/lib/bundler/vendor/uri/COPYING
|
|
328
327
|
bundler/lib/bundler/vendor/uri/lib/uri.rb
|
|
329
328
|
bundler/lib/bundler/vendor/uri/lib/uri/common.rb
|
data/bundler/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,39 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 4.0.16 / 2026-07-10
|
|
4
|
+
|
|
5
|
+
### Enhancements:
|
|
6
|
+
|
|
7
|
+
* Bundler: Fix Bundler::Fetcher for PQC support, adding integration connection tests. Pull request [#9637](https://github.com/ruby/rubygems/pull/9637) by junaruga
|
|
8
|
+
* Reuse RubyGems' vendored tsort in Bundler. Pull request [#9647](https://github.com/ruby/rubygems/pull/9647) by hsbt
|
|
9
|
+
|
|
10
|
+
### Bug fixes:
|
|
11
|
+
|
|
12
|
+
* Initialize the new gem's git repo without a subshell. Pull request [#9670](https://github.com/ruby/rubygems/pull/9670) by hsbt
|
|
13
|
+
* Preserve CRLF lockfile line endings on Windows. Pull request [#9669](https://github.com/ruby/rubygems/pull/9669) by hsbt
|
|
14
|
+
* Fix the gemspec error snippet on Windows drive-letter paths. Pull request [#9668](https://github.com/ruby/rubygems/pull/9668) by hsbt
|
|
15
|
+
|
|
16
|
+
### Documentation:
|
|
17
|
+
|
|
18
|
+
* Point Bundler gemspec metadata at the moved docs. Pull request [#9648](https://github.com/ruby/rubygems/pull/9648) by hsbt
|
|
19
|
+
|
|
20
|
+
## 4.0.15 / 2026-06-24
|
|
21
|
+
|
|
22
|
+
### Enhancements:
|
|
23
|
+
|
|
24
|
+
* Resolve Git LFS files in git sources from the real remote. Pull request [#9632](https://github.com/ruby/rubygems/pull/9632) by hsbt
|
|
25
|
+
* Suggest access issues, not only yanking, for missing locked gems. Pull request [#9631](https://github.com/ruby/rubygems/pull/9631) by hsbt
|
|
26
|
+
* Implement a make jobserver (continuation of #9210). Pull request [#9625](https://github.com/ruby/rubygems/pull/9625) by hsbt
|
|
27
|
+
* Reduce peak memory usage of full index loading and bundle install. Pull request [#9618](https://github.com/ruby/rubygems/pull/9618) by hsbt
|
|
28
|
+
* Bump up to rb-sys 0.9.128. Pull request [#9569](https://github.com/ruby/rubygems/pull/9569) by hsbt
|
|
29
|
+
|
|
30
|
+
### Bug fixes:
|
|
31
|
+
|
|
32
|
+
* Skip the make jobserver on Windows. Pull request [#9630](https://github.com/ruby/rubygems/pull/9630) by hsbt
|
|
33
|
+
* Don't require source plugins to be installed to parse a lockfile: 4.0.x. Pull request [#9621](https://github.com/ruby/rubygems/pull/9621) by hsbt
|
|
34
|
+
* Exempt lockfile versions from cooldown on every resolution path. Pull request [#9619](https://github.com/ruby/rubygems/pull/9619) by hsbt
|
|
35
|
+
* Set `Bundler.settings[:ssl_ca_cert]` to download gems. Pull request [#9610](https://github.com/ruby/rubygems/pull/9610) by junaruga
|
|
36
|
+
|
|
3
37
|
## 4.0.14 / 2026-06-10
|
|
4
38
|
|
|
5
39
|
### Bug fixes:
|
data/bundler/bundler.gemspec
CHANGED
|
@@ -24,9 +24,9 @@ Gem::Specification.new do |s|
|
|
|
24
24
|
|
|
25
25
|
s.metadata = {
|
|
26
26
|
"bug_tracker_uri" => "https://github.com/ruby/rubygems/issues?q=is%3Aopen+is%3Aissue+label%3ABundler",
|
|
27
|
-
"changelog_uri" => "https://github.com/ruby/rubygems/blob/master/bundler
|
|
27
|
+
"changelog_uri" => "https://github.com/ruby/rubygems/blob/master/CHANGELOG-bundler.md",
|
|
28
28
|
"homepage_uri" => "https://bundler.io/",
|
|
29
|
-
"source_code_uri" => "https://github.com/ruby/rubygems
|
|
29
|
+
"source_code_uri" => "https://github.com/ruby/rubygems",
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
s.required_ruby_version = ">= 3.2.0"
|
|
@@ -256,8 +256,7 @@ module Bundler
|
|
|
256
256
|
|
|
257
257
|
if use_git
|
|
258
258
|
Bundler.ui.info "\nInitializing git repo in #{target}"
|
|
259
|
-
|
|
260
|
-
`git init #{target.to_s.shellescape}`
|
|
259
|
+
IO.popen(["git", "init", target.to_s], &:read)
|
|
261
260
|
|
|
262
261
|
config[:git_default_branch] = File.read("#{target}/.git/HEAD").split("/").last.chomp
|
|
263
262
|
end
|
|
@@ -230,6 +230,16 @@ module Bundler
|
|
|
230
230
|
sources.prefer_local!
|
|
231
231
|
end
|
|
232
232
|
|
|
233
|
+
# Releases memory only needed during resolution, such as remote spec
|
|
234
|
+
# indexes and resolver state. Only safe to call once resolution is
|
|
235
|
+
# complete and the result has been materialized, since any further
|
|
236
|
+
# resolution will need to refetch remote specs.
|
|
237
|
+
def release_resolution_memory!
|
|
238
|
+
@resolver = nil
|
|
239
|
+
@resolution_base = nil
|
|
240
|
+
sources.release_resolution_memory!
|
|
241
|
+
end
|
|
242
|
+
|
|
233
243
|
# For given dependency list returns a SpecSet with Gemspec of all the required
|
|
234
244
|
# dependencies.
|
|
235
245
|
# 1. The method first resolves the dependencies specified in Gemfile
|
|
@@ -387,10 +397,6 @@ module Bundler
|
|
|
387
397
|
|
|
388
398
|
contents = to_lock
|
|
389
399
|
|
|
390
|
-
# Convert to \r\n if the existing lock has them
|
|
391
|
-
# i.e., Windows with `git config core.autocrlf=true`
|
|
392
|
-
contents.gsub!(/\n/, "\r\n") if @lockfile_contents.match?("\r\n")
|
|
393
|
-
|
|
394
400
|
if @locked_bundler_version
|
|
395
401
|
locked_major = @locked_bundler_version.segments.first
|
|
396
402
|
current_major = bundler_version_to_lock.segments.first
|
|
@@ -411,6 +417,14 @@ module Bundler
|
|
|
411
417
|
return
|
|
412
418
|
end
|
|
413
419
|
|
|
420
|
+
# Convert to \r\n if the existing lock has them, i.e., Windows with
|
|
421
|
+
# `git config core.autocrlf=true`. Detect from the bytes on disk because
|
|
422
|
+
# reading in text mode strips carriage returns on Windows, which would
|
|
423
|
+
# otherwise defeat this check and rewrite a `\r\n` lockfile with `\n`.
|
|
424
|
+
if File.exist?(file) && SharedHelpers.filesystem_access(file, :read) {|p| File.binread(p).include?("\r\n") }
|
|
425
|
+
contents.gsub!(/\n/, "\r\n")
|
|
426
|
+
end
|
|
427
|
+
|
|
414
428
|
begin
|
|
415
429
|
SharedHelpers.filesystem_access(file) do |p|
|
|
416
430
|
File.open(p, "wb") {|f| f.puts(contents) }
|
|
@@ -688,9 +702,10 @@ module Bundler
|
|
|
688
702
|
"available locally before rerunning Bundler."
|
|
689
703
|
else
|
|
690
704
|
"Your bundle is locked to #{locked_gem} from #{locked_gem.source}, but that version can " \
|
|
691
|
-
"no longer be found in that source. That means the author of #{locked_gem} has removed it
|
|
692
|
-
"You'll need to update your bundle to a version other
|
|
693
|
-
"removed
|
|
705
|
+
"no longer be found in that source. That means either the author of #{locked_gem} has removed it, " \
|
|
706
|
+
"or you no longer have access to that source. You'll need to update your bundle to a version other " \
|
|
707
|
+
"than #{locked_gem} that hasn't been removed, or check your credentials and access rights for " \
|
|
708
|
+
"#{locked_gem.source}, in order to install."
|
|
694
709
|
end
|
|
695
710
|
|
|
696
711
|
raise GemNotFound, message
|
|
@@ -1283,7 +1298,7 @@ module Bundler
|
|
|
1283
1298
|
|
|
1284
1299
|
def new_resolution_base(last_resolve:, unlock:)
|
|
1285
1300
|
new_resolution_platforms = @current_platform_missing ? @new_platforms + [Bundler.local_platform] : @new_platforms
|
|
1286
|
-
Resolver::Base.new(source_requirements, expanded_dependencies, last_resolve, @platforms, locked_specs: @originally_locked_specs, unlock: unlock, prerelease: gem_version_promoter.pre?, prefer_local: @prefer_local, new_platforms: new_resolution_platforms)
|
|
1301
|
+
Resolver::Base.new(source_requirements, expanded_dependencies, last_resolve, @platforms, locked_specs: @originally_locked_specs, unlock: unlock, prerelease: gem_version_promoter.pre?, prefer_local: @prefer_local, new_platforms: new_resolution_platforms, explicit_unlocks: @explicit_unlocks)
|
|
1287
1302
|
end
|
|
1288
1303
|
|
|
1289
1304
|
def new_resolver(base)
|
data/bundler/lib/bundler/dsl.rb
CHANGED
|
@@ -602,8 +602,11 @@ module Bundler
|
|
|
602
602
|
|
|
603
603
|
trace_line = backtrace.find {|l| l.include?(dsl_path) } || trace_line
|
|
604
604
|
return m unless trace_line
|
|
605
|
-
|
|
605
|
+
# Match the line number right before `:in` or the end of the line so a
|
|
606
|
+
# Windows drive letter like `C:` does not get mistaken for the number.
|
|
607
|
+
line_number = trace_line[/:(\d+)(?::in\b|\z)/, 1]
|
|
606
608
|
return m unless line_number
|
|
609
|
+
line_number = line_number.to_i - 1
|
|
607
610
|
|
|
608
611
|
lines = contents.lines.to_a
|
|
609
612
|
indent = " # "
|
|
@@ -63,6 +63,13 @@ module Bundler
|
|
|
63
63
|
true
|
|
64
64
|
end
|
|
65
65
|
|
|
66
|
+
# The client holds the parsed checksums of all info files in the
|
|
67
|
+
# index. Dropping it is always safe because it is rebuilt from the
|
|
68
|
+
# local cache on demand.
|
|
69
|
+
def release_resolution_memory!
|
|
70
|
+
@compact_index_client = nil
|
|
71
|
+
end
|
|
72
|
+
|
|
66
73
|
private
|
|
67
74
|
|
|
68
75
|
def compact_index_client
|
|
@@ -73,6 +80,12 @@ module Bundler
|
|
|
73
80
|
end
|
|
74
81
|
|
|
75
82
|
def fetch_gem_infos(names)
|
|
83
|
+
# Create the client and update the versions file on this thread.
|
|
84
|
+
# Otherwise the workers race to lazily create the client and update
|
|
85
|
+
# the versions file concurrently, e.g. when the client was released
|
|
86
|
+
# after resolution and is being rebuilt for `bundle cache`.
|
|
87
|
+
compact_index_client.available?
|
|
88
|
+
|
|
76
89
|
in_parallel(names) {|name| compact_index_client.info(name) }
|
|
77
90
|
rescue TooManyRequestsError # rubygems.org is rate limiting us, slow down.
|
|
78
91
|
@bundle_worker&.stop
|
|
@@ -243,6 +243,10 @@ module Bundler
|
|
|
243
243
|
fetchers.first.api_fetcher?
|
|
244
244
|
end
|
|
245
245
|
|
|
246
|
+
def release_resolution_memory!
|
|
247
|
+
@fetchers&.each(&:release_resolution_memory!)
|
|
248
|
+
end
|
|
249
|
+
|
|
246
250
|
def gem_remote_fetcher
|
|
247
251
|
@gem_remote_fetcher ||= begin
|
|
248
252
|
require_relative "fetcher/gem_remote_fetcher"
|
|
@@ -314,7 +318,7 @@ module Bundler
|
|
|
314
318
|
if ssl_client_cert
|
|
315
319
|
pem = File.read(ssl_client_cert)
|
|
316
320
|
con.cert = OpenSSL::X509::Certificate.new(pem)
|
|
317
|
-
con.key = OpenSSL::PKey
|
|
321
|
+
con.key = OpenSSL::PKey.read(pem)
|
|
318
322
|
end
|
|
319
323
|
|
|
320
324
|
con.read_timeout = Fetcher.api_timeout
|
|
@@ -110,10 +110,55 @@ module Bundler
|
|
|
110
110
|
end
|
|
111
111
|
|
|
112
112
|
def install_with_worker
|
|
113
|
-
|
|
114
|
-
|
|
113
|
+
with_jobserver do
|
|
114
|
+
installed_specs = {}
|
|
115
|
+
enqueue_specs(installed_specs)
|
|
115
116
|
|
|
116
|
-
|
|
117
|
+
process_specs(installed_specs) until finished_installing?
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def with_jobserver
|
|
122
|
+
# The jobserver hands tokens to child `make` processes through MAKEFLAGS
|
|
123
|
+
# using the GNU make `--jobserver-auth` protocol. nmake, the default make
|
|
124
|
+
# on mswin, instead reads MAKEFLAGS as bare option letters and aborts
|
|
125
|
+
# every native extension build with `fatal error U1065: invalid option
|
|
126
|
+
# '-'`. Skip the jobserver when nmake is in use. Other Windows toolchains
|
|
127
|
+
# such as mingw use GNU make and keep working through the inherited pipe.
|
|
128
|
+
return yield if nmake? || bsd_make?
|
|
129
|
+
|
|
130
|
+
begin
|
|
131
|
+
r, w = IO.pipe
|
|
132
|
+
r.close_on_exec = false
|
|
133
|
+
w.close_on_exec = false
|
|
134
|
+
w.write("*" * @size)
|
|
135
|
+
|
|
136
|
+
old_makeflags = ENV["MAKEFLAGS"]
|
|
137
|
+
ENV["MAKEFLAGS"] = [old_makeflags, "--jobserver-auth=#{r.fileno},#{w.fileno}"].compact.join(" ")
|
|
138
|
+
|
|
139
|
+
yield
|
|
140
|
+
ensure
|
|
141
|
+
# Restore MAKEFLAGS before closing the pipe so a close failure can't
|
|
142
|
+
# leave the process with descriptors that point at a closed pipe.
|
|
143
|
+
old_makeflags ? ENV["MAKEFLAGS"] = old_makeflags : ENV.delete("MAKEFLAGS")
|
|
144
|
+
|
|
145
|
+
r&.close
|
|
146
|
+
w&.close
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# Mirror how RubyGems' extension builder picks the make program so the
|
|
151
|
+
# jobserver is only set up when a GNU-compatible make will consume it.
|
|
152
|
+
def nmake?
|
|
153
|
+
make = ENV["MAKE"] || ENV["make"]
|
|
154
|
+
make ||= "nmake" if RUBY_PLATFORM.include?("mswin")
|
|
155
|
+
/\bnmake/i.match?(make.to_s)
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def bsd_make?
|
|
159
|
+
return false unless Gem.freebsd_platform?
|
|
160
|
+
make = ENV["MAKE"] || ENV["make"] || "make"
|
|
161
|
+
!/\bgmake/i.match?(make)
|
|
117
162
|
end
|
|
118
163
|
|
|
119
164
|
def install_serially
|
|
@@ -195,7 +195,13 @@ module Bundler
|
|
|
195
195
|
force = options[:force]
|
|
196
196
|
local = options[:local] || options[:"prefer-local"]
|
|
197
197
|
jobs = Bundler.settings.installation_parallelization
|
|
198
|
-
|
|
198
|
+
specs = @definition.specs
|
|
199
|
+
# Installing default gems may need the remote index again to cache
|
|
200
|
+
# their .gem files, so keep resolution memory around in that case.
|
|
201
|
+
# The bundler spec itself is excluded because it comes from the
|
|
202
|
+
# metadata source and never goes through that path.
|
|
203
|
+
@definition.release_resolution_memory! if specs.none? {|s| s.default_gem? && s.source.is_a?(Source::Rubygems) }
|
|
204
|
+
spec_installations = ParallelInstaller.call(self, specs, jobs, standalone, force, local: local)
|
|
199
205
|
spec_installations.each do |installation|
|
|
200
206
|
post_install_messages[installation.name] = installation.post_install_message if installation.has_post_install_message?
|
|
201
207
|
end
|
|
@@ -160,18 +160,18 @@ module Bundler
|
|
|
160
160
|
|
|
161
161
|
def wait_for_writtable_socket(socket, address, timeout)
|
|
162
162
|
if IO.select(nil, [socket], nil, timeout)
|
|
163
|
-
probe_writtable_socket(socket
|
|
163
|
+
probe_writtable_socket(socket)
|
|
164
164
|
else # TCP Handshake timed out, or there is something dropping packets
|
|
165
165
|
false
|
|
166
166
|
end
|
|
167
167
|
end
|
|
168
168
|
|
|
169
|
-
def probe_writtable_socket(socket
|
|
170
|
-
socket
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
169
|
+
def probe_writtable_socket(socket)
|
|
170
|
+
# Check the pending error on the socket rather than calling
|
|
171
|
+
# +connect_nonblock+ a second time. On BSD-based systems such as macOS a
|
|
172
|
+
# second connect returns +EISCONN+ even when the asynchronous connect
|
|
173
|
+
# failed, which would make a down mirror look reachable.
|
|
174
|
+
socket.getsockopt(Socket::SOL_SOCKET, Socket::SO_ERROR).int == 0
|
|
175
175
|
end
|
|
176
176
|
end
|
|
177
177
|
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Bundler
|
|
4
|
+
module Plugin
|
|
5
|
+
# Stands in for a source handled by a plugin that is not loaded yet, so
|
|
6
|
+
# that the lockfile can still be parsed during the plugin install pass,
|
|
7
|
+
# and by external tools reading a lockfile without the plugin installed.
|
|
8
|
+
class UnloadedSource
|
|
9
|
+
include API::Source
|
|
10
|
+
|
|
11
|
+
# Unlike real plugin sources, where the handling class encodes the
|
|
12
|
+
# source type, all unloaded sources share this class, so the type must
|
|
13
|
+
# be compared explicitly.
|
|
14
|
+
def ==(other)
|
|
15
|
+
super && options["type"] == other.options["type"]
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
alias_method :eql?, :==
|
|
19
|
+
|
|
20
|
+
def hash
|
|
21
|
+
[super, options["type"]].hash
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -4,11 +4,12 @@ require_relative "plugin/api"
|
|
|
4
4
|
|
|
5
5
|
module Bundler
|
|
6
6
|
module Plugin
|
|
7
|
-
autoload :DSL,
|
|
8
|
-
autoload :Events,
|
|
9
|
-
autoload :Index,
|
|
10
|
-
autoload :Installer,
|
|
11
|
-
autoload :SourceList,
|
|
7
|
+
autoload :DSL, File.expand_path("plugin/dsl", __dir__)
|
|
8
|
+
autoload :Events, File.expand_path("plugin/events", __dir__)
|
|
9
|
+
autoload :Index, File.expand_path("plugin/index", __dir__)
|
|
10
|
+
autoload :Installer, File.expand_path("plugin/installer", __dir__)
|
|
11
|
+
autoload :SourceList, File.expand_path("plugin/source_list", __dir__)
|
|
12
|
+
autoload :UnloadedSource, File.expand_path("plugin/unloaded_source", __dir__)
|
|
12
13
|
|
|
13
14
|
class MalformattedPlugin < PluginError; end
|
|
14
15
|
class UndefinedCommandError < PluginError; end
|
|
@@ -199,9 +200,14 @@ module Bundler
|
|
|
199
200
|
# @return [API::Source] the instance of the class that handles the source
|
|
200
201
|
# type passed in locked_opts
|
|
201
202
|
def from_lock(locked_opts)
|
|
203
|
+
opts = locked_opts.merge("uri" => locked_opts["remote"])
|
|
204
|
+
# use an inert placeholder when the plugin handling this source is not
|
|
205
|
+
# installed, so that the lockfile can still be parsed
|
|
206
|
+
return UnloadedSource.new(opts) unless source?(locked_opts["type"])
|
|
207
|
+
|
|
202
208
|
src = source(locked_opts["type"])
|
|
203
209
|
|
|
204
|
-
src.new(
|
|
210
|
+
src.new(opts)
|
|
205
211
|
end
|
|
206
212
|
|
|
207
213
|
# To be called via the API to register a hooks and corresponding block that
|
|
@@ -8,6 +8,7 @@ module Bundler
|
|
|
8
8
|
attr_reader :packages, :requirements, :source_requirements, :locked_specs
|
|
9
9
|
|
|
10
10
|
def initialize(source_requirements, dependencies, base, platforms, options)
|
|
11
|
+
@explicit_unlocks = options.delete(:explicit_unlocks) || []
|
|
11
12
|
@source_requirements = source_requirements
|
|
12
13
|
@locked_specs = options[:locked_specs]
|
|
13
14
|
|
|
@@ -43,6 +44,14 @@ module Bundler
|
|
|
43
44
|
@packages[name]
|
|
44
45
|
end
|
|
45
46
|
|
|
47
|
+
# Gems the user named on a `bundle update GEM` / `bundle lock --update GEM`
|
|
48
|
+
# command line. These are the only ones meant to move off their locked
|
|
49
|
+
# version, so cooldown keeps applying to them while every other locked gem
|
|
50
|
+
# stays exempt.
|
|
51
|
+
def explicitly_unlocked?(name)
|
|
52
|
+
@explicit_unlocks.include?(name)
|
|
53
|
+
end
|
|
54
|
+
|
|
46
55
|
def base_requirements
|
|
47
56
|
@base_requirements ||= build_base_requirements
|
|
48
57
|
end
|
|
@@ -437,25 +437,29 @@ module Bundler
|
|
|
437
437
|
def cooldown_excluded?(spec)
|
|
438
438
|
return false unless spec.respond_to?(:created_at) && spec.created_at
|
|
439
439
|
return false unless spec.respond_to?(:remote) && spec.remote
|
|
440
|
-
return false if
|
|
440
|
+
return false if locked_by_lockfile?(spec)
|
|
441
441
|
days = spec.remote.effective_cooldown
|
|
442
442
|
return false if days.nil? || days <= 0
|
|
443
443
|
(cooldown_now - spec.created_at) < (days * 86_400)
|
|
444
444
|
end
|
|
445
445
|
|
|
446
|
-
# A
|
|
447
|
-
#
|
|
448
|
-
#
|
|
449
|
-
#
|
|
450
|
-
#
|
|
451
|
-
#
|
|
452
|
-
#
|
|
453
|
-
#
|
|
454
|
-
|
|
446
|
+
# A version already written to the lockfile has been adopted, and cooldown
|
|
447
|
+
# only governs the adoption of *new* versions, so it must never retract one
|
|
448
|
+
# the lockfile already pins. Keying this off the locked specs rather than the
|
|
449
|
+
# prevent-downgrade floor matters because that floor is absent on resolutions
|
|
450
|
+
# that re-pick a gem from scratch: the auxiliary full update run to compute
|
|
451
|
+
# `--update` targets, and the from-scratch retries after a conflict unlocks a
|
|
452
|
+
# gem. In those passes the locked version is the only candidate, so filtering
|
|
453
|
+
# it out makes an unrelated operation impossible whenever every published
|
|
454
|
+
# version matching the requirement sits inside the cooldown window.
|
|
455
|
+
#
|
|
456
|
+
# Gems named on a `bundle update GEM` command are the exception: the user
|
|
457
|
+
# asked to move them, so they stay subject to cooldown and a locked-but-fresh
|
|
458
|
+
# release is pushed back to an older one (or fails loudly when none exists).
|
|
459
|
+
def locked_by_lockfile?(spec)
|
|
455
460
|
return false unless defined?(@base) && @base
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
requirement.requirements.any? {|op, version| op == ">=" && version == spec.version }
|
|
461
|
+
return false if @base.explicitly_unlocked?(spec.name)
|
|
462
|
+
@base.locked_specs[spec.name].any? {|locked| locked.version == spec.version }
|
|
459
463
|
end
|
|
460
464
|
|
|
461
465
|
def cooldown_now
|
|
@@ -4,6 +4,11 @@ require "rubygems/installer"
|
|
|
4
4
|
|
|
5
5
|
module Bundler
|
|
6
6
|
class RubyGemsGemInstaller < Gem::Installer
|
|
7
|
+
# Cap how many jobserver slots a single gem's `make` may grab so that one
|
|
8
|
+
# gem with many recipes doesn't starve the others sharing the pool. Beyond
|
|
9
|
+
# a handful of jobs the extra parallelism rarely pays off in practice.
|
|
10
|
+
MAX_JOBS_PER_GEM = 3
|
|
11
|
+
|
|
7
12
|
def check_executable_overwrite(filename)
|
|
8
13
|
# Bundler needs to install gems regardless of binstub overwriting
|
|
9
14
|
end
|
|
@@ -101,10 +106,18 @@ module Bundler
|
|
|
101
106
|
end
|
|
102
107
|
|
|
103
108
|
def build_jobs
|
|
104
|
-
|
|
109
|
+
@jobserver_read_io&.read_nonblock(MAX_JOBS_PER_GEM, @jobserver_tokens)
|
|
110
|
+
acquired_jobs = @jobserver_tokens.empty? ? nil : @jobserver_tokens.size
|
|
111
|
+
|
|
112
|
+
acquired_jobs || Bundler.settings[:jobs] || super
|
|
113
|
+
rescue IO::WaitReadable, EOFError
|
|
114
|
+
1
|
|
105
115
|
end
|
|
106
116
|
|
|
107
117
|
def build_extensions
|
|
118
|
+
@jobserver_tokens = +""
|
|
119
|
+
@jobserver_read_io, @jobserver_write_io = connect_to_jobserver
|
|
120
|
+
|
|
108
121
|
extension_cache_path = options[:bundler_extension_cache_path]
|
|
109
122
|
extension_dir = spec.extension_dir
|
|
110
123
|
unless extension_cache_path && extension_dir
|
|
@@ -128,6 +141,11 @@ module Bundler
|
|
|
128
141
|
FileUtils.cp_r extension_dir, extension_cache_path
|
|
129
142
|
end
|
|
130
143
|
end
|
|
144
|
+
ensure
|
|
145
|
+
unless @jobserver_tokens.empty?
|
|
146
|
+
@jobserver_write_io.write(@jobserver_tokens)
|
|
147
|
+
@jobserver_write_io.flush
|
|
148
|
+
end
|
|
131
149
|
end
|
|
132
150
|
|
|
133
151
|
def spec
|
|
@@ -144,6 +162,21 @@ module Bundler
|
|
|
144
162
|
|
|
145
163
|
private
|
|
146
164
|
|
|
165
|
+
def connect_to_jobserver
|
|
166
|
+
return unless ENV["MAKEFLAGS"]
|
|
167
|
+
# We append our own --jobserver-auth, so read the last one. Otherwise a
|
|
168
|
+
# parent jobserver's descriptors (e.g. `bundle install` run under
|
|
169
|
+
# `make -j`) would be picked up instead of the pool ParallelInstaller created.
|
|
170
|
+
read_fd, write_fd = ENV["MAKEFLAGS"].scan(/--jobserver-auth=(\d+),(\d+)/).last
|
|
171
|
+
|
|
172
|
+
return unless read_fd && write_fd
|
|
173
|
+
|
|
174
|
+
# Pass explicit modes. On POSIX, IO.new detects the descriptor's access
|
|
175
|
+
# mode, but on Windows it can't, so the write end would default to read
|
|
176
|
+
# mode and raise "IOError: not opened for writing" when releasing slots.
|
|
177
|
+
[IO.new(read_fd.to_i, "r", autoclose: false), IO.new(write_fd.to_i, "w", autoclose: false)]
|
|
178
|
+
end
|
|
179
|
+
|
|
147
180
|
def prepare_extension_build(extension_dir)
|
|
148
181
|
SharedHelpers.filesystem_access(extension_dir, :create) do
|
|
149
182
|
FileUtils.mkdir_p extension_dir
|
|
@@ -144,6 +144,12 @@ module Bundler
|
|
|
144
144
|
FileUtils.rm_rf(p)
|
|
145
145
|
end
|
|
146
146
|
git "clone", "--no-checkout", "--quiet", path.to_s, destination.to_s
|
|
147
|
+
# The copy is cloned from the local bare cache, which holds no Git LFS
|
|
148
|
+
# objects, so point origin back at the real remote and let git-lfs derive
|
|
149
|
+
# its endpoint from there when checking out. Use the credential-filtered
|
|
150
|
+
# URI to avoid persisting secrets in the copy's .git/config; auth is left
|
|
151
|
+
# to git's credential helper.
|
|
152
|
+
git "remote", "set-url", "origin", credential_filtered_uri, dir: destination
|
|
147
153
|
File.chmod((File.stat(destination).mode | 0o777) & ~File.umask, destination)
|
|
148
154
|
rescue Errno::EEXIST => e
|
|
149
155
|
file_path = e.message[%r{.*?((?:[a-zA-Z]:)?/.*)}, 1]
|