bundler 4.0.14 → 4.0.15
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 +17 -0
- data/lib/bundler/build_metadata.rb +1 -1
- data/lib/bundler/definition.rb +15 -4
- data/lib/bundler/fetcher/base.rb +3 -0
- data/lib/bundler/fetcher/compact_index.rb +13 -0
- data/lib/bundler/fetcher/gem_remote_fetcher.rb +2 -0
- data/lib/bundler/fetcher.rb +4 -0
- data/lib/bundler/installer/parallel_installer.rb +42 -3
- data/lib/bundler/installer.rb +7 -1
- data/lib/bundler/mirror.rb +7 -7
- data/lib/bundler/plugin/unloaded_source.rb +25 -0
- data/lib/bundler/plugin.rb +12 -6
- data/lib/bundler/resolver/base.rb +9 -0
- data/lib/bundler/resolver.rb +17 -13
- data/lib/bundler/rubygems_gem_installer.rb +34 -1
- data/lib/bundler/source/git/git_proxy.rb +6 -0
- data/lib/bundler/source/rubygems.rb +27 -7
- data/lib/bundler/source_list.rb +4 -0
- data/lib/bundler/templates/newgem/newgem.gemspec.tt +1 -1
- data/lib/bundler/version.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b500418ab2bb689238b11172e7713d90513adbeeea8b4a27e3e9e12e1ac61cdb
|
|
4
|
+
data.tar.gz: 5c6463f83fcec1c3310af6cf30fdbe50774e8016a6ac8c277a65fcb14ca52c0a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e36d0b9bc29b9ca34a12ccb44ec995e774f55348d2181ea220ed70d68232d65f89c338ac799f35ea3a97cddbc73db10ef584756cfa1e480cc0c1be8b97c454ce
|
|
7
|
+
data.tar.gz: f5dfd925657ea59c8fde6a4c5bf658e7e3b5b9357a1ccd9d03167dc7cfbcd40db826d3b909a65101e24bd15d15287d4e6505884a6117e152feb516e0597ca2ab
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 4.0.15 / 2026-06-24
|
|
4
|
+
|
|
5
|
+
### Enhancements:
|
|
6
|
+
|
|
7
|
+
* Resolve Git LFS files in git sources from the real remote. Pull request [#9632](https://github.com/ruby/rubygems/pull/9632) by hsbt
|
|
8
|
+
* Suggest access issues, not only yanking, for missing locked gems. Pull request [#9631](https://github.com/ruby/rubygems/pull/9631) by hsbt
|
|
9
|
+
* Implement a make jobserver (continuation of #9210). Pull request [#9625](https://github.com/ruby/rubygems/pull/9625) by hsbt
|
|
10
|
+
* Reduce peak memory usage of full index loading and bundle install. Pull request [#9618](https://github.com/ruby/rubygems/pull/9618) by hsbt
|
|
11
|
+
* Bump up to rb-sys 0.9.128. Pull request [#9569](https://github.com/ruby/rubygems/pull/9569) by hsbt
|
|
12
|
+
|
|
13
|
+
### Bug fixes:
|
|
14
|
+
|
|
15
|
+
* Skip the make jobserver on Windows. Pull request [#9630](https://github.com/ruby/rubygems/pull/9630) by hsbt
|
|
16
|
+
* 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
|
|
17
|
+
* Exempt lockfile versions from cooldown on every resolution path. Pull request [#9619](https://github.com/ruby/rubygems/pull/9619) by hsbt
|
|
18
|
+
* Set `Bundler.settings[:ssl_ca_cert]` to download gems. Pull request [#9610](https://github.com/ruby/rubygems/pull/9610) by junaruga
|
|
19
|
+
|
|
3
20
|
## 4.0.14 / 2026-06-10
|
|
4
21
|
|
|
5
22
|
### Bug fixes:
|
data/lib/bundler/definition.rb
CHANGED
|
@@ -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
|
|
@@ -688,9 +698,10 @@ module Bundler
|
|
|
688
698
|
"available locally before rerunning Bundler."
|
|
689
699
|
else
|
|
690
700
|
"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
|
|
701
|
+
"no longer be found in that source. That means either the author of #{locked_gem} has removed it, " \
|
|
702
|
+
"or you no longer have access to that source. You'll need to update your bundle to a version other " \
|
|
703
|
+
"than #{locked_gem} that hasn't been removed, or check your credentials and access rights for " \
|
|
704
|
+
"#{locked_gem.source}, in order to install."
|
|
694
705
|
end
|
|
695
706
|
|
|
696
707
|
raise GemNotFound, message
|
|
@@ -1283,7 +1294,7 @@ module Bundler
|
|
|
1283
1294
|
|
|
1284
1295
|
def new_resolution_base(last_resolve:, unlock:)
|
|
1285
1296
|
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)
|
|
1297
|
+
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
1298
|
end
|
|
1288
1299
|
|
|
1289
1300
|
def new_resolver(base)
|
data/lib/bundler/fetcher/base.rb
CHANGED
|
@@ -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
|
data/lib/bundler/fetcher.rb
CHANGED
|
@@ -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"
|
|
@@ -110,10 +110,49 @@ 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?
|
|
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)
|
|
117
156
|
end
|
|
118
157
|
|
|
119
158
|
def install_serially
|
data/lib/bundler/installer.rb
CHANGED
|
@@ -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
|
data/lib/bundler/mirror.rb
CHANGED
|
@@ -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
|
data/lib/bundler/plugin.rb
CHANGED
|
@@ -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
|
data/lib/bundler/resolver.rb
CHANGED
|
@@ -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]
|
|
@@ -25,6 +25,7 @@ module Bundler
|
|
|
25
25
|
@checksum_store = Checksum::Store.new
|
|
26
26
|
@gem_installers = {}
|
|
27
27
|
@gem_installers_mutex = Mutex.new
|
|
28
|
+
@remote_specs_mutex = Mutex.new
|
|
28
29
|
|
|
29
30
|
cooldown = options["cooldown"]
|
|
30
31
|
Array(options["remotes"]).reverse_each {|r| add_remote(r, cooldown: cooldown) }
|
|
@@ -243,7 +244,7 @@ module Bundler
|
|
|
243
244
|
def cached_built_in_gem(spec, local: false)
|
|
244
245
|
cached_path = cached_gem(spec)
|
|
245
246
|
if cached_path.nil? && !local
|
|
246
|
-
remote_spec =
|
|
247
|
+
remote_spec = remote_spec_for(spec)
|
|
247
248
|
if remote_spec
|
|
248
249
|
cached_path = fetch_gem(remote_spec)
|
|
249
250
|
spec.remote = remote_spec.remote
|
|
@@ -337,6 +338,12 @@ module Bundler
|
|
|
337
338
|
@cached_specs = nil
|
|
338
339
|
end
|
|
339
340
|
|
|
341
|
+
def release_resolution_memory!
|
|
342
|
+
@specs = nil
|
|
343
|
+
@remote_specs_mutex.synchronize { @remote_specs = nil }
|
|
344
|
+
@fetchers&.each(&:release_resolution_memory!)
|
|
345
|
+
end
|
|
346
|
+
|
|
340
347
|
protected
|
|
341
348
|
|
|
342
349
|
def remote_names
|
|
@@ -414,17 +421,30 @@ module Bundler
|
|
|
414
421
|
end
|
|
415
422
|
|
|
416
423
|
def remote_specs
|
|
417
|
-
@remote_specs ||=
|
|
418
|
-
|
|
424
|
+
@remote_specs ||= @remote_specs_mutex.synchronize do
|
|
425
|
+
@remote_specs ||= Index.build do |idx|
|
|
426
|
+
index_fetchers = fetchers - api_fetchers
|
|
419
427
|
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
428
|
+
if index_fetchers.empty?
|
|
429
|
+
fetch_names(api_fetchers, dependency_names, idx)
|
|
430
|
+
else
|
|
431
|
+
fetch_names(fetchers, nil, idx)
|
|
432
|
+
end
|
|
424
433
|
end
|
|
425
434
|
end
|
|
426
435
|
end
|
|
427
436
|
|
|
437
|
+
# Looks up a single spec in the remote sources, fetching only its own
|
|
438
|
+
# name when the full remote index is not already materialized.
|
|
439
|
+
def remote_spec_for(spec)
|
|
440
|
+
return remote_specs.search(spec).first if @remote_specs || api_fetchers.empty?
|
|
441
|
+
|
|
442
|
+
index = Index.build do |idx|
|
|
443
|
+
fetch_names(api_fetchers, [spec.name], idx)
|
|
444
|
+
end
|
|
445
|
+
index.search(spec).first
|
|
446
|
+
end
|
|
447
|
+
|
|
428
448
|
def fetch_names(fetchers, dependency_names, index)
|
|
429
449
|
fetchers.each do |f|
|
|
430
450
|
if dependency_names
|
data/lib/bundler/source_list.rb
CHANGED
|
@@ -47,7 +47,7 @@ Gem::Specification.new do |spec|
|
|
|
47
47
|
# Uncomment to register a new dependency of your gem
|
|
48
48
|
# spec.add_dependency "example-gem", "~> 1.0"
|
|
49
49
|
<%- if config[:ext] == 'rust' -%>
|
|
50
|
-
spec.add_dependency "rb_sys", "~> 0.9.
|
|
50
|
+
spec.add_dependency "rb_sys", "~> 0.9.128"
|
|
51
51
|
<%- end -%>
|
|
52
52
|
<%- if config[:ext] == 'go' -%>
|
|
53
53
|
spec.add_dependency "go_gem", "~> 0.2"
|
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: 4.0.
|
|
4
|
+
version: 4.0.15
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- André Arko
|
|
@@ -194,6 +194,7 @@ files:
|
|
|
194
194
|
- lib/bundler/plugin/installer/path.rb
|
|
195
195
|
- lib/bundler/plugin/installer/rubygems.rb
|
|
196
196
|
- lib/bundler/plugin/source_list.rb
|
|
197
|
+
- lib/bundler/plugin/unloaded_source.rb
|
|
197
198
|
- lib/bundler/process_lock.rb
|
|
198
199
|
- lib/bundler/remote_specification.rb
|
|
199
200
|
- lib/bundler/resolver.rb
|