bundler 4.0.7 → 4.0.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.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +25 -0
  3. data/lib/bundler/build_metadata.rb +2 -2
  4. data/lib/bundler/cli/pristine.rb +1 -1
  5. data/lib/bundler/definition.rb +3 -1
  6. data/lib/bundler/fetcher/dependency.rb +7 -1
  7. data/lib/bundler/fetcher/gem_remote_fetcher.rb +1 -1
  8. data/lib/bundler/installer/gem_installer.rb +14 -0
  9. data/lib/bundler/installer/parallel_installer.rb +63 -17
  10. data/lib/bundler/installer.rb +1 -9
  11. data/lib/bundler/man/bundle-add.1 +1 -1
  12. data/lib/bundler/man/bundle-binstubs.1 +1 -1
  13. data/lib/bundler/man/bundle-cache.1 +1 -1
  14. data/lib/bundler/man/bundle-check.1 +1 -1
  15. data/lib/bundler/man/bundle-clean.1 +1 -1
  16. data/lib/bundler/man/bundle-config.1 +5 -2
  17. data/lib/bundler/man/bundle-config.1.ronn +7 -2
  18. data/lib/bundler/man/bundle-console.1 +1 -1
  19. data/lib/bundler/man/bundle-doctor.1 +1 -1
  20. data/lib/bundler/man/bundle-env.1 +1 -1
  21. data/lib/bundler/man/bundle-exec.1 +1 -1
  22. data/lib/bundler/man/bundle-fund.1 +1 -1
  23. data/lib/bundler/man/bundle-gem.1 +1 -1
  24. data/lib/bundler/man/bundle-help.1 +1 -1
  25. data/lib/bundler/man/bundle-info.1 +1 -1
  26. data/lib/bundler/man/bundle-init.1 +1 -1
  27. data/lib/bundler/man/bundle-install.1 +1 -1
  28. data/lib/bundler/man/bundle-issue.1 +1 -1
  29. data/lib/bundler/man/bundle-licenses.1 +1 -1
  30. data/lib/bundler/man/bundle-list.1 +1 -1
  31. data/lib/bundler/man/bundle-lock.1 +1 -1
  32. data/lib/bundler/man/bundle-open.1 +1 -1
  33. data/lib/bundler/man/bundle-outdated.1 +1 -1
  34. data/lib/bundler/man/bundle-platform.1 +1 -1
  35. data/lib/bundler/man/bundle-plugin.1 +1 -1
  36. data/lib/bundler/man/bundle-pristine.1 +1 -1
  37. data/lib/bundler/man/bundle-remove.1 +1 -1
  38. data/lib/bundler/man/bundle-show.1 +1 -1
  39. data/lib/bundler/man/bundle-update.1 +1 -1
  40. data/lib/bundler/man/bundle-version.1 +1 -1
  41. data/lib/bundler/man/bundle.1 +1 -1
  42. data/lib/bundler/man/gemfile.5 +1 -1
  43. data/lib/bundler/plugin/api/source.rb +8 -0
  44. data/lib/bundler/plugin/index.rb +6 -0
  45. data/lib/bundler/plugin/installer.rb +2 -1
  46. data/lib/bundler/plugin.rb +2 -2
  47. data/lib/bundler/retry.rb +30 -4
  48. data/lib/bundler/self_manager.rb +1 -0
  49. data/lib/bundler/settings.rb +4 -0
  50. data/lib/bundler/source/git/git_proxy.rb +49 -15
  51. data/lib/bundler/source/rubygems.rb +48 -26
  52. data/lib/bundler/source.rb +2 -0
  53. data/lib/bundler/templates/newgem/github/workflows/build-gems.yml.tt +8 -4
  54. data/lib/bundler/templates/newgem/github/workflows/main.yml.tt +4 -1
  55. data/lib/bundler/version.rb +1 -1
  56. data/lib/bundler/worker.rb +11 -3
  57. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5a885222998dfbe801692cb3fde27432339068b6273f33d0104e25509d18682d
4
- data.tar.gz: 5ec66951013a0e8b87db477eae402b15b3f37f768d846515f1b6101e0e47b388
3
+ metadata.gz: ff315eb3319300026b3ab3422e82399e6b0ce63ef727f94e125a226623101c27
4
+ data.tar.gz: 00bb903a5564f2565ebcbc350837283f724d1ef42d4f586b37a972fe568015da
5
5
  SHA512:
6
- metadata.gz: 20bdb8aab69b64c68f6bed37bbf1912113ab452b0fd4f6ffe1bb4455556c28df301287dea1cc0faeb45efbf37426300eada625efb34d57b4ab9929d3c05ffade
7
- data.tar.gz: 9a4019d82dd530eac1ac0c6724cad21f6136a78df3ec0a03c63251764ef9190a056a851675550ce3bd4f9965f6040ae9125517ca1038cdbe04a7b97c0c853053
6
+ metadata.gz: c6a4c93541bd76ee07370d4995487fe663f10d8aa021f1d9104d6adfeb373a36bbc47fd3498094da011c561702d7a84ebb1854c80c99176578b0fbc666e2dba2
7
+ data.tar.gz: 29943339931f1e5b37c836346171c51f2a00a2cadbd7d7b5f30da5dd5bacb1f1024b7601ded1e1d895a3da4e11cf1d6ea35768a7196b351e237179d63759cd13
data/CHANGELOG.md CHANGED
@@ -1,5 +1,30 @@
1
1
  # Changelog
2
2
 
3
+ ## 4.0.9 / 2026-03-25
4
+
5
+ ### Enhancements:
6
+
7
+ * Check the git version only **once** per `bundle install`. Pull request [#9406](https://github.com/ruby/rubygems/pull/9406) by Edouard-chin
8
+ * Normalize the number of workers when performing parallel operations. Pull request [#9400](https://github.com/ruby/rubygems/pull/9400) by Edouard-chin
9
+ * Add exponential backoff to bundler retries. Pull request [#9163](https://github.com/ruby/rubygems/pull/9163) by ChrisBr
10
+ * Introduce a priority queue. Pull request [#9389](https://github.com/ruby/rubygems/pull/9389) by Edouard-chin
11
+ * Split the download and install process of a gem. Pull request [#9381](https://github.com/ruby/rubygems/pull/9381) by Edouard-chin
12
+
13
+ ### Bug fixes:
14
+
15
+ * Retry git fetch without --depth for dumb HTTP transport. Pull request [#9405](https://github.com/ruby/rubygems/pull/9405) by hsbt
16
+
17
+ ## 4.0.8 (2026-03-11)
18
+
19
+ ### Enhancements:
20
+
21
+ - Add a new Bundler config to control how many specs are fetched [#9363](https://github.com/ruby/rubygems/pull/9363)
22
+ - Restrict GitHub Actions workflow permissions for newgem [#9361](https://github.com/ruby/rubygems/pull/9361)
23
+
24
+ ### Bug fixes:
25
+
26
+ - Fix plugin new version not registering [#9355](https://github.com/ruby/rubygems/pull/9355)
27
+
3
28
  ## 4.0.7 (2026-02-25)
4
29
 
5
30
  ### Enhancements:
@@ -4,8 +4,8 @@ module Bundler
4
4
  # Represents metadata from when the Bundler gem was built.
5
5
  module BuildMetadata
6
6
  # begin ivars
7
- @built_at = "2026-02-25".freeze
8
- @git_commit_sha = "049c458564".freeze
7
+ @built_at = nil
8
+ @git_commit_sha = "3ce4a32411".freeze
9
9
  # end ivars
10
10
 
11
11
  # A hash representation of the build metadata.
@@ -53,7 +53,7 @@ module Bundler
53
53
  true
54
54
  end.map(&:name)
55
55
 
56
- jobs = installer.send(:installation_parallelization)
56
+ jobs = Bundler.settings.installation_parallelization
57
57
  pristine_count = definition.specs.count - installed_specs.count
58
58
  # allow a pristining a single gem to skip the parallel worker
59
59
  jobs = [jobs, pristine_count].min
@@ -1122,7 +1122,9 @@ module Bundler
1122
1122
  end
1123
1123
 
1124
1124
  def preload_git_source_worker
1125
- @preload_git_source_worker ||= Bundler::Worker.new(5, "Git source preloading", ->(source, _) { source.specs })
1125
+ workers = Bundler.settings.installation_parallelization
1126
+
1127
+ @preload_git_source_worker ||= Bundler::Worker.new(workers, "Git source preloading", ->(source, _) { source.specs })
1126
1128
  end
1127
1129
 
1128
1130
  def preload_git_sources
@@ -50,7 +50,7 @@ module Bundler
50
50
 
51
51
  def unmarshalled_dep_gems(gem_names)
52
52
  gem_list = []
53
- gem_names.each_slice(Source::Rubygems::API_REQUEST_SIZE) do |names|
53
+ gem_names.each_slice(api_request_size) do |names|
54
54
  marshalled_deps = downloader.fetch(dependency_api_uri(names)).body
55
55
  gem_list.concat(Bundler.safe_load_marshal(marshalled_deps))
56
56
  end
@@ -74,6 +74,12 @@ module Bundler
74
74
  uri.query = "gems=#{CGI.escape(gem_names.sort.join(","))}" if gem_names.any?
75
75
  uri
76
76
  end
77
+
78
+ private
79
+
80
+ def api_request_size
81
+ Bundler.settings[:api_request_size]&.to_i || Source::Rubygems::API_REQUEST_SIZE
82
+ end
77
83
  end
78
84
  end
79
85
  end
@@ -8,7 +8,7 @@ module Bundler
8
8
  def initialize(*)
9
9
  super
10
10
 
11
- @pool_size = 5
11
+ @pool_size = Bundler.settings.installation_parallelization
12
12
  end
13
13
 
14
14
  def request(*args)
@@ -25,6 +25,20 @@ module Bundler
25
25
  [false, specific_failure_message(e)]
26
26
  end
27
27
 
28
+ def download
29
+ spec.source.download(
30
+ spec,
31
+ force: force,
32
+ local: local,
33
+ build_args: Array(spec_settings),
34
+ previous_spec: previous_spec,
35
+ )
36
+
37
+ [true, nil]
38
+ rescue Bundler::BundlerError => e
39
+ [false, specific_failure_message(e)]
40
+ end
41
+
28
42
  private
29
43
 
30
44
  def specific_failure_message(e)
@@ -24,6 +24,10 @@ module Bundler
24
24
  state == :enqueued
25
25
  end
26
26
 
27
+ def enqueue_with_priority?
28
+ state == :installable && spec.extensions.any?
29
+ end
30
+
27
31
  def failed?
28
32
  state == :failed
29
33
  end
@@ -32,6 +36,12 @@ module Bundler
32
36
  state == :none
33
37
  end
34
38
 
39
+ def ready_to_install?(installed_specs)
40
+ return false unless state == :downloaded
41
+
42
+ spec.extensions.none? || dependencies_installed?(installed_specs)
43
+ end
44
+
35
45
  def has_post_install_message?
36
46
  !post_install_message.empty?
37
47
  end
@@ -84,6 +94,7 @@ module Bundler
84
94
 
85
95
  def call
86
96
  if @rake
97
+ do_download(@rake, 0)
87
98
  do_install(@rake, 0)
88
99
  Gem::Specification.reset
89
100
  end
@@ -107,26 +118,54 @@ module Bundler
107
118
  end
108
119
 
109
120
  def install_with_worker
110
- enqueue_specs
111
- process_specs until finished_installing?
121
+ installed_specs = {}
122
+ enqueue_specs(installed_specs)
123
+
124
+ process_specs(installed_specs) until finished_installing?
112
125
  end
113
126
 
114
127
  def install_serially
115
128
  until finished_installing?
116
129
  raise "failed to find a spec to enqueue while installing serially" unless spec_install = @specs.find(&:ready_to_enqueue?)
117
130
  spec_install.state = :enqueued
131
+ do_download(spec_install, 0)
118
132
  do_install(spec_install, 0)
119
133
  end
120
134
  end
121
135
 
122
136
  def worker_pool
123
137
  @worker_pool ||= Bundler::Worker.new @size, "Parallel Installer", lambda {|spec_install, worker_num|
124
- do_install(spec_install, worker_num)
138
+ case spec_install.state
139
+ when :enqueued
140
+ do_download(spec_install, worker_num)
141
+ when :installable
142
+ do_install(spec_install, worker_num)
143
+ else
144
+ spec_install
145
+ end
125
146
  }
126
147
  end
127
148
 
128
- def do_install(spec_install, worker_num)
149
+ def do_download(spec_install, worker_num)
129
150
  Plugin.hook(Plugin::Events::GEM_BEFORE_INSTALL, spec_install)
151
+
152
+ gem_installer = Bundler::GemInstaller.new(
153
+ spec_install.spec, @installer, @standalone, worker_num, @force, @local
154
+ )
155
+
156
+ success, message = gem_installer.download
157
+
158
+ if success
159
+ spec_install.state = :downloaded
160
+ else
161
+ spec_install.error = "#{message}\n\n#{require_tree_for_spec(spec_install.spec)}"
162
+ spec_install.state = :failed
163
+ end
164
+
165
+ spec_install
166
+ end
167
+
168
+ def do_install(spec_install, worker_num)
130
169
  gem_installer = Bundler::GemInstaller.new(
131
170
  spec_install.spec, @installer, @standalone, worker_num, @force, @local
132
171
  )
@@ -147,9 +186,19 @@ module Bundler
147
186
  # Some specs might've had to wait til this spec was installed to be
148
187
  # processed so the call to `enqueue_specs` is important after every
149
188
  # dequeue.
150
- def process_specs
151
- worker_pool.deq
152
- enqueue_specs
189
+ def process_specs(installed_specs)
190
+ spec = worker_pool.deq
191
+
192
+ if spec.installed?
193
+ installed_specs[spec.name] = true
194
+ return
195
+ elsif spec.failed?
196
+ return
197
+ elsif spec.ready_to_install?(installed_specs)
198
+ spec.state = :installable
199
+ end
200
+
201
+ worker_pool.enq(spec, priority: spec.enqueue_with_priority?)
153
202
  end
154
203
 
155
204
  def finished_installing?
@@ -185,18 +234,15 @@ module Bundler
185
234
  # Later we call this lambda again to install specs that depended on
186
235
  # previously installed specifications. We continue until all specs
187
236
  # are installed.
188
- def enqueue_specs
189
- installed_specs = {}
190
- @specs.each do |spec|
191
- next unless spec.installed?
192
- installed_specs[spec.name] = true
193
- end
194
-
237
+ def enqueue_specs(installed_specs)
195
238
  @specs.each do |spec|
196
- if spec.ready_to_enqueue? && spec.dependencies_installed?(installed_specs)
197
- spec.state = :enqueued
198
- worker_pool.enq spec
239
+ if spec.installed?
240
+ installed_specs[spec.name] = true
241
+ next
199
242
  end
243
+
244
+ spec.state = :enqueued
245
+ worker_pool.enq spec
200
246
  end
201
247
  end
202
248
  end
@@ -189,21 +189,13 @@ module Bundler
189
189
  standalone = options[:standalone]
190
190
  force = options[:force]
191
191
  local = options[:local] || options[:"prefer-local"]
192
- jobs = installation_parallelization
192
+ jobs = Bundler.settings.installation_parallelization
193
193
  spec_installations = ParallelInstaller.call(self, @definition.specs, jobs, standalone, force, local: local)
194
194
  spec_installations.each do |installation|
195
195
  post_install_messages[installation.name] = installation.post_install_message if installation.has_post_install_message?
196
196
  end
197
197
  end
198
198
 
199
- def installation_parallelization
200
- if jobs = Bundler.settings[:jobs]
201
- return jobs
202
- end
203
-
204
- Bundler.settings.processor_count
205
- end
206
-
207
199
  def load_plugins
208
200
  Gem.load_plugins
209
201
 
@@ -1,6 +1,6 @@
1
1
  .\" generated with Ronn-NG/v0.10.1
2
2
  .\" http://github.com/apjanke/ronn-ng/tree/0.10.1
3
- .TH "BUNDLE\-ADD" "1" "February 2026" ""
3
+ .TH "BUNDLE\-ADD" "1" "March 2026" ""
4
4
  .SH "NAME"
5
5
  \fBbundle\-add\fR \- Add gem to the Gemfile and run bundle install
6
6
  .SH "SYNOPSIS"
@@ -1,6 +1,6 @@
1
1
  .\" generated with Ronn-NG/v0.10.1
2
2
  .\" http://github.com/apjanke/ronn-ng/tree/0.10.1
3
- .TH "BUNDLE\-BINSTUBS" "1" "February 2026" ""
3
+ .TH "BUNDLE\-BINSTUBS" "1" "March 2026" ""
4
4
  .SH "NAME"
5
5
  \fBbundle\-binstubs\fR \- Install the binstubs of the listed gems
6
6
  .SH "SYNOPSIS"
@@ -1,6 +1,6 @@
1
1
  .\" generated with Ronn-NG/v0.10.1
2
2
  .\" http://github.com/apjanke/ronn-ng/tree/0.10.1
3
- .TH "BUNDLE\-CACHE" "1" "February 2026" ""
3
+ .TH "BUNDLE\-CACHE" "1" "March 2026" ""
4
4
  .SH "NAME"
5
5
  \fBbundle\-cache\fR \- Package your needed \fB\.gem\fR files into your application
6
6
  .SH "SYNOPSIS"
@@ -1,6 +1,6 @@
1
1
  .\" generated with Ronn-NG/v0.10.1
2
2
  .\" http://github.com/apjanke/ronn-ng/tree/0.10.1
3
- .TH "BUNDLE\-CHECK" "1" "February 2026" ""
3
+ .TH "BUNDLE\-CHECK" "1" "March 2026" ""
4
4
  .SH "NAME"
5
5
  \fBbundle\-check\fR \- Verifies if dependencies are satisfied by installed gems
6
6
  .SH "SYNOPSIS"
@@ -1,6 +1,6 @@
1
1
  .\" generated with Ronn-NG/v0.10.1
2
2
  .\" http://github.com/apjanke/ronn-ng/tree/0.10.1
3
- .TH "BUNDLE\-CLEAN" "1" "February 2026" ""
3
+ .TH "BUNDLE\-CLEAN" "1" "March 2026" ""
4
4
  .SH "NAME"
5
5
  \fBbundle\-clean\fR \- Cleans up unused gems in your bundler directory
6
6
  .SH "SYNOPSIS"
@@ -1,6 +1,6 @@
1
1
  .\" generated with Ronn-NG/v0.10.1
2
2
  .\" http://github.com/apjanke/ronn-ng/tree/0.10.1
3
- .TH "BUNDLE\-CONFIG" "1" "February 2026" ""
3
+ .TH "BUNDLE\-CONFIG" "1" "March 2026" ""
4
4
  .SH "NAME"
5
5
  \fBbundle\-config\fR \- Set bundler configuration options
6
6
  .SH "SYNOPSIS"
@@ -70,6 +70,9 @@ Any periods in the configuration keys must be replaced with two underscores when
70
70
  .SH "LIST OF AVAILABLE KEYS"
71
71
  The following is a list of all configuration keys and their purpose\. You can learn more about their operation in bundle install(1) \fIbundle\-install\.1\.html\fR\.
72
72
  .TP
73
+ \fBapi_request_size\fR (\fBBUNDLE_API_REQUEST_SIZE\fR)
74
+ Configure how many dependencies to fetch when resolving the specifications\. This configuration is only used when fetchig specifications from RubyGems servers that didn't implement the Compact Index API\. Defaults to 100\.
75
+ .TP
73
76
  \fBauto_install\fR (\fBBUNDLE_AUTO_INSTALL\fR)
74
77
  Automatically run \fBbundle install\fR when gems are missing\.
75
78
  .TP
@@ -143,7 +146,7 @@ When set, no post install messages will be printed\. To silence a single gem, us
143
146
  Generate a \fBgems\.rb\fR instead of a \fBGemfile\fR when running \fBbundle init\fR\.
144
147
  .TP
145
148
  \fBjobs\fR (\fBBUNDLE_JOBS\fR)
146
- The number of gems Bundler can install in parallel\. Defaults to the number of available processors\.
149
+ The number of gems Bundler can download and install in parallel\. Defaults to the number of available processors\.
147
150
  .TP
148
151
  \fBlockfile\fR (\fBBUNDLE_LOCKFILE\fR)
149
152
  The path to the lockfile that bundler should use\. By default, Bundler adds \fB\.lock\fR to the end of the \fBgemfile\fR entry\. Can be set to \fBfalse\fR in the Gemfile to disable lockfile creation entirely (see gemfile(5))\.
@@ -106,6 +106,11 @@ the environment variable `BUNDLE_LOCAL__RACK`.
106
106
  The following is a list of all configuration keys and their purpose. You can
107
107
  learn more about their operation in [bundle install(1)](bundle-install.1.html).
108
108
 
109
+ * `api_request_size` (`BUNDLE_API_REQUEST_SIZE`):
110
+ Configure how many dependencies to fetch when resolving the specifications.
111
+ This configuration is only used when fetchig specifications from RubyGems
112
+ servers that didn't implement the Compact Index API.
113
+ Defaults to 100.
109
114
  * `auto_install` (`BUNDLE_AUTO_INSTALL`):
110
115
  Automatically run `bundle install` when gems are missing.
111
116
  * `bin` (`BUNDLE_BIN`):
@@ -187,8 +192,8 @@ learn more about their operation in [bundle install(1)](bundle-install.1.html).
187
192
  * `init_gems_rb` (`BUNDLE_INIT_GEMS_RB`):
188
193
  Generate a `gems.rb` instead of a `Gemfile` when running `bundle init`.
189
194
  * `jobs` (`BUNDLE_JOBS`):
190
- The number of gems Bundler can install in parallel. Defaults to the number of
191
- available processors.
195
+ The number of gems Bundler can download and install in parallel.
196
+ Defaults to the number of available processors.
192
197
  * `lockfile` (`BUNDLE_LOCKFILE`):
193
198
  The path to the lockfile that bundler should use. By default, Bundler adds
194
199
  `.lock` to the end of the `gemfile` entry. Can be set to `false` in the
@@ -1,6 +1,6 @@
1
1
  .\" generated with Ronn-NG/v0.10.1
2
2
  .\" http://github.com/apjanke/ronn-ng/tree/0.10.1
3
- .TH "BUNDLE\-CONSOLE" "1" "February 2026" ""
3
+ .TH "BUNDLE\-CONSOLE" "1" "March 2026" ""
4
4
  .SH "NAME"
5
5
  \fBbundle\-console\fR \- Open an IRB session with the bundle pre\-loaded
6
6
  .SH "SYNOPSIS"
@@ -1,6 +1,6 @@
1
1
  .\" generated with Ronn-NG/v0.10.1
2
2
  .\" http://github.com/apjanke/ronn-ng/tree/0.10.1
3
- .TH "BUNDLE\-DOCTOR" "1" "February 2026" ""
3
+ .TH "BUNDLE\-DOCTOR" "1" "March 2026" ""
4
4
  .SH "NAME"
5
5
  \fBbundle\-doctor\fR \- Checks the bundle for common problems
6
6
  .SH "SYNOPSIS"
@@ -1,6 +1,6 @@
1
1
  .\" generated with Ronn-NG/v0.10.1
2
2
  .\" http://github.com/apjanke/ronn-ng/tree/0.10.1
3
- .TH "BUNDLE\-ENV" "1" "February 2026" ""
3
+ .TH "BUNDLE\-ENV" "1" "March 2026" ""
4
4
  .SH "NAME"
5
5
  \fBbundle\-env\fR \- Print information about the environment Bundler is running under
6
6
  .SH "SYNOPSIS"
@@ -1,6 +1,6 @@
1
1
  .\" generated with Ronn-NG/v0.10.1
2
2
  .\" http://github.com/apjanke/ronn-ng/tree/0.10.1
3
- .TH "BUNDLE\-EXEC" "1" "February 2026" ""
3
+ .TH "BUNDLE\-EXEC" "1" "March 2026" ""
4
4
  .SH "NAME"
5
5
  \fBbundle\-exec\fR \- Execute a command in the context of the bundle
6
6
  .SH "SYNOPSIS"
@@ -1,6 +1,6 @@
1
1
  .\" generated with Ronn-NG/v0.10.1
2
2
  .\" http://github.com/apjanke/ronn-ng/tree/0.10.1
3
- .TH "BUNDLE\-FUND" "1" "February 2026" ""
3
+ .TH "BUNDLE\-FUND" "1" "March 2026" ""
4
4
  .SH "NAME"
5
5
  \fBbundle\-fund\fR \- Lists information about gems seeking funding assistance
6
6
  .SH "SYNOPSIS"
@@ -1,6 +1,6 @@
1
1
  .\" generated with Ronn-NG/v0.10.1
2
2
  .\" http://github.com/apjanke/ronn-ng/tree/0.10.1
3
- .TH "BUNDLE\-GEM" "1" "February 2026" ""
3
+ .TH "BUNDLE\-GEM" "1" "March 2026" ""
4
4
  .SH "NAME"
5
5
  \fBbundle\-gem\fR \- Generate a project skeleton for creating a rubygem
6
6
  .SH "SYNOPSIS"
@@ -1,6 +1,6 @@
1
1
  .\" generated with Ronn-NG/v0.10.1
2
2
  .\" http://github.com/apjanke/ronn-ng/tree/0.10.1
3
- .TH "BUNDLE\-HELP" "1" "February 2026" ""
3
+ .TH "BUNDLE\-HELP" "1" "March 2026" ""
4
4
  .SH "NAME"
5
5
  \fBbundle\-help\fR \- Displays detailed help for each subcommand
6
6
  .SH "SYNOPSIS"
@@ -1,6 +1,6 @@
1
1
  .\" generated with Ronn-NG/v0.10.1
2
2
  .\" http://github.com/apjanke/ronn-ng/tree/0.10.1
3
- .TH "BUNDLE\-INFO" "1" "February 2026" ""
3
+ .TH "BUNDLE\-INFO" "1" "March 2026" ""
4
4
  .SH "NAME"
5
5
  \fBbundle\-info\fR \- Show information for the given gem in your bundle
6
6
  .SH "SYNOPSIS"
@@ -1,6 +1,6 @@
1
1
  .\" generated with Ronn-NG/v0.10.1
2
2
  .\" http://github.com/apjanke/ronn-ng/tree/0.10.1
3
- .TH "BUNDLE\-INIT" "1" "February 2026" ""
3
+ .TH "BUNDLE\-INIT" "1" "March 2026" ""
4
4
  .SH "NAME"
5
5
  \fBbundle\-init\fR \- Generates a Gemfile into the current working directory
6
6
  .SH "SYNOPSIS"
@@ -1,6 +1,6 @@
1
1
  .\" generated with Ronn-NG/v0.10.1
2
2
  .\" http://github.com/apjanke/ronn-ng/tree/0.10.1
3
- .TH "BUNDLE\-INSTALL" "1" "February 2026" ""
3
+ .TH "BUNDLE\-INSTALL" "1" "March 2026" ""
4
4
  .SH "NAME"
5
5
  \fBbundle\-install\fR \- Install the dependencies specified in your Gemfile
6
6
  .SH "SYNOPSIS"
@@ -1,6 +1,6 @@
1
1
  .\" generated with Ronn-NG/v0.10.1
2
2
  .\" http://github.com/apjanke/ronn-ng/tree/0.10.1
3
- .TH "BUNDLE\-ISSUE" "1" "February 2026" ""
3
+ .TH "BUNDLE\-ISSUE" "1" "March 2026" ""
4
4
  .SH "NAME"
5
5
  \fBbundle\-issue\fR \- Get help reporting Bundler issues
6
6
  .SH "SYNOPSIS"
@@ -1,6 +1,6 @@
1
1
  .\" generated with Ronn-NG/v0.10.1
2
2
  .\" http://github.com/apjanke/ronn-ng/tree/0.10.1
3
- .TH "BUNDLE\-LICENSES" "1" "February 2026" ""
3
+ .TH "BUNDLE\-LICENSES" "1" "March 2026" ""
4
4
  .SH "NAME"
5
5
  \fBbundle\-licenses\fR \- Print the license of all gems in the bundle
6
6
  .SH "SYNOPSIS"
@@ -1,6 +1,6 @@
1
1
  .\" generated with Ronn-NG/v0.10.1
2
2
  .\" http://github.com/apjanke/ronn-ng/tree/0.10.1
3
- .TH "BUNDLE\-LIST" "1" "February 2026" ""
3
+ .TH "BUNDLE\-LIST" "1" "March 2026" ""
4
4
  .SH "NAME"
5
5
  \fBbundle\-list\fR \- List all the gems in the bundle
6
6
  .SH "SYNOPSIS"
@@ -1,6 +1,6 @@
1
1
  .\" generated with Ronn-NG/v0.10.1
2
2
  .\" http://github.com/apjanke/ronn-ng/tree/0.10.1
3
- .TH "BUNDLE\-LOCK" "1" "February 2026" ""
3
+ .TH "BUNDLE\-LOCK" "1" "March 2026" ""
4
4
  .SH "NAME"
5
5
  \fBbundle\-lock\fR \- Creates / Updates a lockfile without installing
6
6
  .SH "SYNOPSIS"
@@ -1,6 +1,6 @@
1
1
  .\" generated with Ronn-NG/v0.10.1
2
2
  .\" http://github.com/apjanke/ronn-ng/tree/0.10.1
3
- .TH "BUNDLE\-OPEN" "1" "February 2026" ""
3
+ .TH "BUNDLE\-OPEN" "1" "March 2026" ""
4
4
  .SH "NAME"
5
5
  \fBbundle\-open\fR \- Opens the source directory for a gem in your bundle
6
6
  .SH "SYNOPSIS"
@@ -1,6 +1,6 @@
1
1
  .\" generated with Ronn-NG/v0.10.1
2
2
  .\" http://github.com/apjanke/ronn-ng/tree/0.10.1
3
- .TH "BUNDLE\-OUTDATED" "1" "February 2026" ""
3
+ .TH "BUNDLE\-OUTDATED" "1" "March 2026" ""
4
4
  .SH "NAME"
5
5
  \fBbundle\-outdated\fR \- List installed gems with newer versions available
6
6
  .SH "SYNOPSIS"
@@ -1,6 +1,6 @@
1
1
  .\" generated with Ronn-NG/v0.10.1
2
2
  .\" http://github.com/apjanke/ronn-ng/tree/0.10.1
3
- .TH "BUNDLE\-PLATFORM" "1" "February 2026" ""
3
+ .TH "BUNDLE\-PLATFORM" "1" "March 2026" ""
4
4
  .SH "NAME"
5
5
  \fBbundle\-platform\fR \- Displays platform compatibility information
6
6
  .SH "SYNOPSIS"
@@ -1,6 +1,6 @@
1
1
  .\" generated with Ronn-NG/v0.10.1
2
2
  .\" http://github.com/apjanke/ronn-ng/tree/0.10.1
3
- .TH "BUNDLE\-PLUGIN" "1" "February 2026" ""
3
+ .TH "BUNDLE\-PLUGIN" "1" "March 2026" ""
4
4
  .SH "NAME"
5
5
  \fBbundle\-plugin\fR \- Manage Bundler plugins
6
6
  .SH "SYNOPSIS"
@@ -1,6 +1,6 @@
1
1
  .\" generated with Ronn-NG/v0.10.1
2
2
  .\" http://github.com/apjanke/ronn-ng/tree/0.10.1
3
- .TH "BUNDLE\-PRISTINE" "1" "February 2026" ""
3
+ .TH "BUNDLE\-PRISTINE" "1" "March 2026" ""
4
4
  .SH "NAME"
5
5
  \fBbundle\-pristine\fR \- Restores installed gems to their pristine condition
6
6
  .SH "SYNOPSIS"
@@ -1,6 +1,6 @@
1
1
  .\" generated with Ronn-NG/v0.10.1
2
2
  .\" http://github.com/apjanke/ronn-ng/tree/0.10.1
3
- .TH "BUNDLE\-REMOVE" "1" "February 2026" ""
3
+ .TH "BUNDLE\-REMOVE" "1" "March 2026" ""
4
4
  .SH "NAME"
5
5
  \fBbundle\-remove\fR \- Removes gems from the Gemfile
6
6
  .SH "SYNOPSIS"
@@ -1,6 +1,6 @@
1
1
  .\" generated with Ronn-NG/v0.10.1
2
2
  .\" http://github.com/apjanke/ronn-ng/tree/0.10.1
3
- .TH "BUNDLE\-SHOW" "1" "February 2026" ""
3
+ .TH "BUNDLE\-SHOW" "1" "March 2026" ""
4
4
  .SH "NAME"
5
5
  \fBbundle\-show\fR \- Shows all the gems in your bundle, or the path to a gem
6
6
  .SH "SYNOPSIS"
@@ -1,6 +1,6 @@
1
1
  .\" generated with Ronn-NG/v0.10.1
2
2
  .\" http://github.com/apjanke/ronn-ng/tree/0.10.1
3
- .TH "BUNDLE\-UPDATE" "1" "February 2026" ""
3
+ .TH "BUNDLE\-UPDATE" "1" "March 2026" ""
4
4
  .SH "NAME"
5
5
  \fBbundle\-update\fR \- Update your gems to the latest available versions
6
6
  .SH "SYNOPSIS"
@@ -1,6 +1,6 @@
1
1
  .\" generated with Ronn-NG/v0.10.1
2
2
  .\" http://github.com/apjanke/ronn-ng/tree/0.10.1
3
- .TH "BUNDLE\-VERSION" "1" "February 2026" ""
3
+ .TH "BUNDLE\-VERSION" "1" "March 2026" ""
4
4
  .SH "NAME"
5
5
  \fBbundle\-version\fR \- Prints Bundler version information
6
6
  .SH "SYNOPSIS"
@@ -1,6 +1,6 @@
1
1
  .\" generated with Ronn-NG/v0.10.1
2
2
  .\" http://github.com/apjanke/ronn-ng/tree/0.10.1
3
- .TH "BUNDLE" "1" "February 2026" ""
3
+ .TH "BUNDLE" "1" "March 2026" ""
4
4
  .SH "NAME"
5
5
  \fBbundle\fR \- Ruby Dependency Management
6
6
  .SH "SYNOPSIS"
@@ -1,6 +1,6 @@
1
1
  .\" generated with Ronn-NG/v0.10.1
2
2
  .\" http://github.com/apjanke/ronn-ng/tree/0.10.1
3
- .TH "GEMFILE" "5" "February 2026" ""
3
+ .TH "GEMFILE" "5" "March 2026" ""
4
4
  .SH "NAME"
5
5
  \fBGemfile\fR \- A format for describing gem dependencies for Ruby programs
6
6
  .SH "SYNOPSIS"
@@ -74,6 +74,14 @@ module Bundler
74
74
  {}
75
75
  end
76
76
 
77
+ # Download the gem specified by the spec at appropriate path.
78
+ #
79
+ # A source plugin can implement this method to split the download and the
80
+ # installation of a gem.
81
+ #
82
+ # @return [Boolean] Whether the download of the gem succeeded.
83
+ def download(spec, opts); end
84
+
77
85
  # Install the gem specified by the spec at appropriate path.
78
86
  # `install_path` provides a sufficient default, if the source can only
79
87
  # satisfy one gem, but is not binding.
@@ -119,6 +119,12 @@ module Bundler
119
119
  @plugin_paths[name]
120
120
  end
121
121
 
122
+ def up_to_date?(spec)
123
+ path = installed?(spec.name)
124
+
125
+ path == spec.full_gem_path
126
+ end
127
+
122
128
  def installed_plugins
123
129
  @plugin_paths.keys
124
130
  end
@@ -110,7 +110,8 @@ module Bundler
110
110
  paths = {}
111
111
 
112
112
  specs.each do |spec|
113
- spec.source.install spec
113
+ spec.source.download(spec)
114
+ spec.source.install(spec)
114
115
 
115
116
  paths[spec.name] = spec
116
117
  end
@@ -113,7 +113,7 @@ module Bundler
113
113
 
114
114
  return if definition.dependencies.empty?
115
115
 
116
- plugins = definition.dependencies.map(&:name).reject {|p| index.installed? p }
116
+ plugins = definition.dependencies.map(&:name)
117
117
  installed_specs = Installer.new.install_definition(definition)
118
118
 
119
119
  save_plugins plugins, installed_specs, builder.inferred_plugins
@@ -258,7 +258,7 @@ module Bundler
258
258
  # It's possible that the `plugin` found in the Gemfile don't appear in the specs. For instance when
259
259
  # calling `BUNDLE_WITHOUT=default bundle install`, the plugins will not get installed.
260
260
  next if spec.nil?
261
- next if index.installed?(name)
261
+ next if index.up_to_date?(spec)
262
262
 
263
263
  save_plugin(name, spec, optional_plugins.include?(name))
264
264
  end
data/lib/bundler/retry.rb CHANGED
@@ -6,6 +6,8 @@ module Bundler
6
6
  attr_accessor :name, :total_runs, :current_run
7
7
 
8
8
  class << self
9
+ attr_accessor :default_base_delay
10
+
9
11
  def default_attempts
10
12
  default_retries + 1
11
13
  end
@@ -16,11 +18,17 @@ module Bundler
16
18
  end
17
19
  end
18
20
 
19
- def initialize(name, exceptions = nil, retries = self.class.default_retries)
21
+ # Set default base delay for exponential backoff
22
+ self.default_base_delay = 1.0
23
+
24
+ def initialize(name, exceptions = nil, retries = self.class.default_retries, opts = {})
20
25
  @name = name
21
26
  @retries = retries
22
27
  @exceptions = Array(exceptions) || []
23
28
  @total_runs = @retries + 1 # will run once, then upto attempts.times
29
+ @base_delay = opts[:base_delay] || self.class.default_base_delay
30
+ @max_delay = opts[:max_delay] || 60.0
31
+ @jitter = opts[:jitter] || 0.5
24
32
  end
25
33
 
26
34
  def attempt(&block)
@@ -48,9 +56,27 @@ module Bundler
48
56
  Bundler.ui.info "" unless Bundler.ui.debug?
49
57
  raise e
50
58
  end
51
- return true unless name
52
- Bundler.ui.info "" unless Bundler.ui.debug? # Add new line in case dots preceded this
53
- Bundler.ui.warn "Retrying #{name} due to error (#{current_run.next}/#{total_runs}): #{e.class} #{e.message}", true
59
+ if name
60
+ Bundler.ui.info "" unless Bundler.ui.debug? # Add new line in case dots preceded this
61
+ Bundler.ui.warn "Retrying #{name} due to error (#{current_run.next}/#{total_runs}): #{e.class} #{e.message}", true
62
+ end
63
+ backoff_sleep if @base_delay > 0
64
+ true
65
+ end
66
+
67
+ def backoff_sleep
68
+ # Exponential backoff: delay = base_delay * 2^(attempt - 1)
69
+ # Add jitter to prevent thundering herd: random value between 0 and jitter seconds
70
+ delay = @base_delay * (2**(@current_run - 1))
71
+ delay = [@max_delay, delay].min
72
+ jitter_amount = rand * @jitter
73
+ total_delay = delay + jitter_amount
74
+ Bundler.ui.debug "Sleeping for #{total_delay.round(2)} seconds before retry"
75
+ sleep(total_delay)
76
+ end
77
+
78
+ def sleep(duration)
79
+ Kernel.sleep(duration)
54
80
  end
55
81
 
56
82
  def keep_trying?
@@ -63,6 +63,7 @@ module Bundler
63
63
  end
64
64
 
65
65
  def install(spec)
66
+ spec.source.download(spec)
66
67
  spec.source.install(spec)
67
68
  end
68
69
 
@@ -303,6 +303,10 @@ module Bundler
303
303
  @app_cache_path ||= self[:cache_path] || "vendor/cache"
304
304
  end
305
305
 
306
+ def installation_parallelization
307
+ self[:jobs] || processor_count
308
+ end
309
+
306
310
  def validate!
307
311
  all.each do |raw_key|
308
312
  [@local_config, @env_config, @global_config].each do |settings|
@@ -57,6 +57,29 @@ module Bundler
57
57
  attr_accessor :path, :uri, :branch, :tag, :ref, :explicit_ref
58
58
  attr_writer :revision
59
59
 
60
+ def self.version
61
+ @version ||= full_version[/((\.?\d+)+).*/, 1]
62
+ end
63
+
64
+ def self.full_version
65
+ @full_version ||= begin
66
+ raise GitNotInstalledError.new unless Bundler.git_present?
67
+
68
+ require "open3"
69
+ out, err, status = Open3.capture3("git", "--version")
70
+
71
+ raise GitCommandError.new("--version", SharedHelpers.pwd, err) unless status.success?
72
+ Bundler.ui.warn err unless err.empty?
73
+
74
+ out.sub(/git version\s*/, "").strip
75
+ end
76
+ end
77
+
78
+ def self.reset
79
+ @version = nil
80
+ @full_version = nil
81
+ end
82
+
60
83
  def initialize(path, uri, options = {}, revision = nil, git = nil)
61
84
  @path = path
62
85
  @uri = uri
@@ -92,11 +115,11 @@ module Bundler
92
115
  end
93
116
 
94
117
  def version
95
- @version ||= full_version.match(/((\.?\d+)+).*/)[1]
118
+ self.class.version
96
119
  end
97
120
 
98
121
  def full_version
99
- @full_version ||= git_local("--version").sub(/git version\s*/, "").strip
122
+ self.class.full_version
100
123
  end
101
124
 
102
125
  def checkout
@@ -156,7 +179,7 @@ module Bundler
156
179
  private
157
180
 
158
181
  def git_remote_fetch(args)
159
- command = ["fetch", "--force", "--quiet", "--no-tags", *args, "--", configured_uri, refspec].compact
182
+ command = fetch_command(args)
160
183
  command_with_no_credentials = check_allowed(command)
161
184
 
162
185
  Bundler::Retry.new("`#{command_with_no_credentials}` at #{path}", [MissingGitRevisionError]).attempts do
@@ -166,6 +189,11 @@ module Bundler
166
189
  if err.include?("couldn't find remote ref") || err.include?("not our ref")
167
190
  raise MissingGitRevisionError.new(command_with_no_credentials, path, commit || explicit_ref, credential_filtered_uri)
168
191
  else
192
+ if shallow?
193
+ args -= depth_args
194
+ command = fetch_command(args)
195
+ command_with_no_credentials = check_allowed(command)
196
+ end
169
197
  raise GitCommandError.new(command_with_no_credentials, path, err)
170
198
  end
171
199
  end
@@ -178,7 +206,8 @@ module Bundler
178
206
  FileUtils.mkdir_p(p)
179
207
  end
180
208
 
181
- command = ["clone", "--bare", "--no-hardlinks", "--quiet", *extra_clone_args, "--", configured_uri, path.to_s]
209
+ clone_args = extra_clone_args
210
+ command = clone_command(clone_args)
182
211
  command_with_no_credentials = check_allowed(command)
183
212
 
184
213
  Bundler::Retry.new("`#{command_with_no_credentials}`", [MissingGitRevisionError]).attempts do
@@ -189,13 +218,10 @@ module Bundler
189
218
  err.include?("Remote branch #{branch_option} not found") # git 2.49 or higher
190
219
  raise MissingGitRevisionError.new(command_with_no_credentials, nil, explicit_ref, credential_filtered_uri)
191
220
  else
192
- idx = command.index("--depth")
193
- if idx
194
- command.delete_at(idx)
195
- command.delete_at(idx)
221
+ if shallow?
222
+ clone_args -= depth_args
223
+ command = clone_command(clone_args)
196
224
  command_with_no_credentials = check_allowed(command)
197
-
198
- err += "Retrying without --depth argument."
199
225
  end
200
226
  raise GitCommandError.new(command_with_no_credentials, path, err)
201
227
  end
@@ -204,14 +230,14 @@ module Bundler
204
230
 
205
231
  def clone_needs_unshallow?
206
232
  return false unless path.join("shallow").exist?
207
- return true if full_clone?
233
+ return true unless shallow?
208
234
 
209
235
  @revision && @revision != head_revision
210
236
  end
211
237
 
212
238
  def extra_ref
213
239
  return false if not_pinned?
214
- return true unless full_clone?
240
+ return true if shallow?
215
241
 
216
242
  ref.start_with?("refs/")
217
243
  end
@@ -427,8 +453,16 @@ module Bundler
427
453
  args
428
454
  end
429
455
 
456
+ def fetch_command(args)
457
+ ["fetch", "--force", "--quiet", "--no-tags", *args, "--", configured_uri, refspec].compact
458
+ end
459
+
460
+ def clone_command(args)
461
+ ["clone", "--bare", "--no-hardlinks", "--quiet", *args, "--", configured_uri, path.to_s]
462
+ end
463
+
430
464
  def depth_args
431
- return [] if full_clone?
465
+ return [] unless shallow?
432
466
 
433
467
  ["--depth", depth.to_s]
434
468
  end
@@ -443,8 +477,8 @@ module Bundler
443
477
  branch || tag
444
478
  end
445
479
 
446
- def full_clone?
447
- depth.nil?
480
+ def shallow?
481
+ !depth.nil?
448
482
  end
449
483
 
450
484
  def needs_allow_any_sha1_in_want?
@@ -9,6 +9,7 @@ module Bundler
9
9
 
10
10
  # Ask for X gems per API request
11
11
  API_REQUEST_SIZE = 100
12
+ REQUIRE_MUTEX = Mutex.new
12
13
 
13
14
  attr_accessor :remotes
14
15
 
@@ -21,6 +22,8 @@ module Bundler
21
22
  @allow_local = options["allow_local"] || false
22
23
  @prefer_local = false
23
24
  @checksum_store = Checksum::Store.new
25
+ @gem_installers = {}
26
+ @gem_installers_mutex = Mutex.new
24
27
 
25
28
  Array(options["remotes"]).reverse_each {|r| add_remote(r) }
26
29
 
@@ -162,49 +165,40 @@ module Bundler
162
165
  end
163
166
  end
164
167
 
165
- def install(spec, options = {})
168
+ def download(spec, options = {})
166
169
  if (spec.default_gem? && !cached_built_in_gem(spec, local: options[:local])) || (installed?(spec) && !options[:force])
167
- print_using_message "Using #{version_message(spec, options[:previous_spec])}"
168
- return nil # no post-install message
170
+ return true
169
171
  end
170
172
 
171
- path = fetch_gem_if_possible(spec, options[:previous_spec])
172
- raise GemNotFound, "Could not find #{spec.file_name} for installation" unless path
173
-
174
- return if Bundler.settings[:no_install]
175
-
176
- install_path = rubygems_dir
177
- bin_path = Bundler.system_bindir
178
-
179
- require_relative "../rubygems_gem_installer"
180
-
181
- installer = Bundler::RubyGemsGemInstaller.at(
182
- path,
183
- security_policy: Bundler.rubygems.security_policies[Bundler.settings["trust-policy"]],
184
- install_dir: install_path.to_s,
185
- bin_dir: bin_path.to_s,
186
- ignore_dependencies: true,
187
- wrappers: true,
188
- env_shebang: true,
189
- build_args: options[:build_args],
190
- bundler_extension_cache_path: extension_cache_path(spec)
191
- )
173
+ installer = rubygems_gem_installer(spec, options)
192
174
 
193
175
  if spec.remote
194
176
  s = begin
195
177
  installer.spec
196
178
  rescue Gem::Package::FormatError
197
- Bundler.rm_rf(path)
179
+ Bundler.rm_rf(installer.gem)
198
180
  raise
199
181
  rescue Gem::Security::Exception => e
200
182
  raise SecurityError,
201
- "The gem #{File.basename(path, ".gem")} can't be installed because " \
183
+ "The gem #{installer.gem} can't be installed because " \
202
184
  "the security policy didn't allow it, with the message: #{e.message}"
203
185
  end
204
186
 
205
187
  spec.__swap__(s)
206
188
  end
207
189
 
190
+ spec
191
+ end
192
+
193
+ def install(spec, options = {})
194
+ if (spec.default_gem? && !cached_built_in_gem(spec, local: options[:local])) || (installed?(spec) && !options[:force])
195
+ print_using_message "Using #{version_message(spec, options[:previous_spec])}"
196
+ return nil # no post-install message
197
+ end
198
+
199
+ return if Bundler.settings[:no_install]
200
+
201
+ installer = rubygems_gem_installer(spec, options)
208
202
  spec.source.checksum_store.register(spec, installer.gem_checksum)
209
203
 
210
204
  message = "Installing #{version_message(spec, options[:previous_spec])}"
@@ -511,6 +505,34 @@ module Bundler
511
505
  return unless remote = spec.remote
512
506
  remote.cache_slug
513
507
  end
508
+
509
+ # We are using a mutex to reaed and write from/to the hash.
510
+ # The reason this double synchronization was added is for performance
511
+ # and lock the mutex for the shortest possible amount of time. Otherwise,
512
+ # all threads are fighting over this mutex and when it gets acquired it gets locked
513
+ # until a threads finishes downloading a gem, leaving the other threads waiting
514
+ # doing nothing.
515
+ def rubygems_gem_installer(spec, options)
516
+ @gem_installers_mutex.synchronize { @gem_installers[spec.name] } || begin
517
+ path = fetch_gem_if_possible(spec, options[:previous_spec])
518
+ raise GemNotFound, "Could not find #{spec.file_name} for installation" unless path
519
+
520
+ REQUIRE_MUTEX.synchronize { require_relative "../rubygems_gem_installer" }
521
+
522
+ installer = Bundler::RubyGemsGemInstaller.at(
523
+ path,
524
+ security_policy: Bundler.rubygems.security_policies[Bundler.settings["trust-policy"]],
525
+ install_dir: rubygems_dir.to_s,
526
+ bin_dir: Bundler.system_bindir.to_s,
527
+ ignore_dependencies: true,
528
+ wrappers: true,
529
+ env_shebang: true,
530
+ build_args: options[:build_args],
531
+ bundler_extension_cache_path: extension_cache_path(spec)
532
+ )
533
+ @gem_installers_mutex.synchronize { @gem_installers[spec.name] ||= installer }
534
+ end
535
+ end
514
536
  end
515
537
  end
516
538
  end
@@ -31,6 +31,8 @@ module Bundler
31
31
  message
32
32
  end
33
33
 
34
+ def download(*); end
35
+
34
36
  def can_lock?(spec)
35
37
  spec.source == self
36
38
  end
@@ -8,6 +8,10 @@ on:
8
8
  - "cross-gem/*"
9
9
  workflow_dispatch:
10
10
 
11
+ permissions:
12
+ contents: read
13
+ packages: write
14
+
11
15
  jobs:
12
16
  ci-data:
13
17
  runs-on: ubuntu-latest
@@ -25,7 +29,7 @@ jobs:
25
29
  source-gem:
26
30
  runs-on: ubuntu-latest
27
31
  steps:
28
- - uses: actions/checkout@v4
32
+ - uses: actions/checkout@v6
29
33
 
30
34
  - uses: ruby/setup-ruby@v1
31
35
  with:
@@ -34,7 +38,7 @@ jobs:
34
38
  - name: Build gem
35
39
  run: bundle exec rake build
36
40
 
37
- - uses: actions/upload-artifact@v3
41
+ - uses: actions/upload-artifact@v7
38
42
  with:
39
43
  name: source-gem
40
44
  path: pkg/*.gem
@@ -47,7 +51,7 @@ jobs:
47
51
  matrix:
48
52
  platform: ${{ fromJSON(needs.ci-data.outputs.result).supported-ruby-platforms }}
49
53
  steps:
50
- - uses: actions/checkout@v4
54
+ - uses: actions/checkout@v6
51
55
 
52
56
  - uses: ruby/setup-ruby@v1
53
57
  with:
@@ -59,7 +63,7 @@ jobs:
59
63
  platform: ${{ matrix.platform }}
60
64
  ruby-versions: ${{ join(fromJSON(needs.ci-data.outputs.result).stable-ruby-versions, ',') }}
61
65
 
62
- - uses: actions/upload-artifact@v3
66
+ - uses: actions/upload-artifact@v7
63
67
  with:
64
68
  name: cross-gem
65
69
  path: ${{ steps.cross-gem.outputs.gem-path }}
@@ -7,6 +7,9 @@ on:
7
7
 
8
8
  pull_request:
9
9
 
10
+ permissions:
11
+ contents: read
12
+
10
13
  jobs:
11
14
  build:
12
15
  runs-on: ubuntu-latest
@@ -17,7 +20,7 @@ jobs:
17
20
  - '<%= RUBY_VERSION %>'
18
21
 
19
22
  steps:
20
- - uses: actions/checkout@v4
23
+ - uses: actions/checkout@v6
21
24
  with:
22
25
  persist-credentials: false
23
26
  <%- if config[:ext] == 'rust' -%>
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: false
2
2
 
3
3
  module Bundler
4
- VERSION = "4.0.7".freeze
4
+ VERSION = "4.0.9".freeze
5
5
 
6
6
  def self.bundler_major_version
7
7
  @bundler_major_version ||= gem_version.segments.first
@@ -22,6 +22,7 @@ module Bundler
22
22
  def initialize(size, name, func)
23
23
  @name = name
24
24
  @request_queue = Thread::Queue.new
25
+ @request_queue_with_priority = Thread::Queue.new
25
26
  @response_queue = Thread::Queue.new
26
27
  @func = func
27
28
  @size = size
@@ -32,9 +33,10 @@ module Bundler
32
33
  # Enqueue a request to be executed in the worker pool
33
34
  #
34
35
  # @param obj [String] mostly it is name of spec that should be downloaded
35
- def enq(obj)
36
+ def enq(obj, priority: false)
37
+ queue = priority ? @request_queue_with_priority : @request_queue
36
38
  create_threads unless @threads
37
- @request_queue.enq obj
39
+ queue.enq obj
38
40
  end
39
41
 
40
42
  # Retrieves results of job function being executed in worker pool
@@ -52,7 +54,13 @@ module Bundler
52
54
 
53
55
  def process_queue(i)
54
56
  loop do
55
- obj = @request_queue.deq
57
+ obj = begin
58
+ @request_queue_with_priority.deq(true)
59
+ rescue ThreadError
60
+ @request_queue.deq(false, timeout: 0.05)
61
+ end
62
+
63
+ next if obj.nil?
56
64
  break if obj.equal? POISON
57
65
  @response_queue.enq apply_func(obj, i)
58
66
  end
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.7
4
+ version: 4.0.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - André Arko
@@ -402,7 +402,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
402
402
  - !ruby/object:Gem::Version
403
403
  version: 3.4.1
404
404
  requirements: []
405
- rubygems_version: 4.0.3
405
+ rubygems_version: 4.0.6
406
406
  specification_version: 4
407
407
  summary: The best way to manage your application's dependencies
408
408
  test_files: []