bundler 4.0.10 → 4.0.14
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 +56 -0
- data/lib/bundler/build_metadata.rb +1 -1
- data/lib/bundler/cli/add.rb +3 -0
- data/lib/bundler/cli/common.rb +6 -0
- data/lib/bundler/cli/config.rb +8 -3
- data/lib/bundler/cli/gem.rb +1 -1
- data/lib/bundler/cli/install.rb +3 -0
- data/lib/bundler/cli/outdated.rb +42 -2
- data/lib/bundler/cli/update.rb +2 -0
- data/lib/bundler/cli.rb +16 -12
- data/lib/bundler/compact_index_client/parser.rb +4 -1
- data/lib/bundler/definition.rb +28 -4
- data/lib/bundler/dsl.rb +6 -2
- data/lib/bundler/endpoint_specification.rb +11 -1
- data/lib/bundler/installer/parallel_installer.rb +11 -19
- data/lib/bundler/installer.rb +5 -0
- data/lib/bundler/lockfile_generator.rb +16 -1
- data/lib/bundler/lockfile_parser.rb +23 -1
- data/lib/bundler/man/bundle-add.1 +5 -2
- data/lib/bundler/man/bundle-add.1.ronn +6 -1
- data/lib/bundler/man/bundle-binstubs.1 +1 -1
- data/lib/bundler/man/bundle-cache.1 +1 -1
- data/lib/bundler/man/bundle-check.1 +1 -1
- data/lib/bundler/man/bundle-clean.1 +1 -1
- data/lib/bundler/man/bundle-config.1 +121 -157
- data/lib/bundler/man/bundle-config.1.ronn +31 -1
- data/lib/bundler/man/bundle-console.1 +1 -1
- data/lib/bundler/man/bundle-doctor.1 +1 -1
- data/lib/bundler/man/bundle-env.1 +1 -1
- data/lib/bundler/man/bundle-exec.1 +1 -1
- data/lib/bundler/man/bundle-fund.1 +1 -1
- data/lib/bundler/man/bundle-gem.1 +1 -1
- data/lib/bundler/man/bundle-help.1 +1 -1
- data/lib/bundler/man/bundle-info.1 +1 -1
- data/lib/bundler/man/bundle-init.1 +1 -1
- data/lib/bundler/man/bundle-install.1 +5 -2
- data/lib/bundler/man/bundle-install.1.ronn +9 -1
- data/lib/bundler/man/bundle-issue.1 +1 -1
- data/lib/bundler/man/bundle-licenses.1 +1 -1
- data/lib/bundler/man/bundle-list.1 +1 -1
- data/lib/bundler/man/bundle-lock.1 +1 -1
- data/lib/bundler/man/bundle-open.1 +1 -1
- data/lib/bundler/man/bundle-outdated.1 +17 -14
- data/lib/bundler/man/bundle-outdated.1.ronn +19 -12
- data/lib/bundler/man/bundle-platform.1 +1 -1
- data/lib/bundler/man/bundle-plugin.1 +1 -1
- data/lib/bundler/man/bundle-pristine.1 +1 -1
- data/lib/bundler/man/bundle-remove.1 +1 -1
- data/lib/bundler/man/bundle-show.1 +1 -1
- data/lib/bundler/man/bundle-update.1 +5 -2
- data/lib/bundler/man/bundle-update.1.ronn +8 -0
- data/lib/bundler/man/bundle-version.1 +1 -1
- data/lib/bundler/man/bundle.1 +1 -1
- data/lib/bundler/man/gemfile.5 +1 -1
- data/lib/bundler/remote_specification.rb +1 -1
- data/lib/bundler/resolver.rb +58 -1
- data/lib/bundler/rubygems_ext.rb +22 -0
- data/lib/bundler/rubygems_gem_installer.rb +1 -1
- data/lib/bundler/settings.rb +1 -0
- data/lib/bundler/source/git/git_proxy.rb +7 -2
- data/lib/bundler/source/metadata.rb +4 -0
- data/lib/bundler/source/path.rb +3 -2
- data/lib/bundler/source/rubygems/remote.rb +12 -2
- data/lib/bundler/source/rubygems.rb +53 -5
- data/lib/bundler/source_list.rb +10 -2
- data/lib/bundler/templates/newgem/newgem.gemspec.tt +7 -1
- data/lib/bundler/version.rb +1 -1
- data/lib/bundler.rb +10 -0
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2c7cfeed01a046a9b186ddcdb1039e59c4605673529e12fb08b3fb32f424650f
|
|
4
|
+
data.tar.gz: 375e9b2540c5493fb90cb4e2004372c4fdbac9ed67d09a680bc23fdb71e04c58
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 55a5604bfb508d0c3b509702ce84a42bd4811f3f42222176133942fed42ff0f224ed64fcf68873e163cfe63d8cc183f11b67f4ec915814d3e121bb70f575296d
|
|
7
|
+
data.tar.gz: 4178ca638dd4f4ede2977526c4edba39cb6ae21f9d7d0ba6cab55d47235556d9ea41bfc4353b2349a1085746af76825c84e5597deba5130d874150f8ae1f91ef
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,61 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 4.0.14 / 2026-06-10
|
|
4
|
+
|
|
5
|
+
### Bug fixes:
|
|
6
|
+
|
|
7
|
+
* Preserve per-source cooldown when converging sources from the lockfile. Pull request [#9601](https://github.com/ruby/rubygems/pull/9601) by bryanwoods
|
|
8
|
+
* Don't exclude the locked version from cooldown during bundle update. Pull request [#9599](https://github.com/ruby/rubygems/pull/9599) by hsbt
|
|
9
|
+
|
|
10
|
+
## 4.0.13 / 2026-06-03
|
|
11
|
+
|
|
12
|
+
### Enhancements:
|
|
13
|
+
|
|
14
|
+
* Do not hard-code permissions for new gem directories during bundle install. Pull request [#9557](https://github.com/ruby/rubygems/pull/9557) by maxfelsher-cgi
|
|
15
|
+
* Clear gem specification cache after acquiring process lock. Pull request [#9310](https://github.com/ruby/rubygems/pull/9310) by ngan
|
|
16
|
+
* Show release date with bundle outdated. Pull request [#9337](https://github.com/ruby/rubygems/pull/9337) by hsbt
|
|
17
|
+
|
|
18
|
+
### Bug fixes:
|
|
19
|
+
|
|
20
|
+
* Apply cooldown to locally installed gem versions. Pull request [#9582](https://github.com/ruby/rubygems/pull/9582) by hsbt
|
|
21
|
+
|
|
22
|
+
### Security:
|
|
23
|
+
|
|
24
|
+
* Add `cooldown` to delay newly published gem. Pull request [#9576](https://github.com/ruby/rubygems/pull/9576) by hsbt
|
|
25
|
+
|
|
26
|
+
## 4.0.12 / 2026-05-20
|
|
27
|
+
|
|
28
|
+
### Enhancements:
|
|
29
|
+
|
|
30
|
+
* Make `bundle config get` return status 1 when the value is not set. Pull request [#9505](https://github.com/ruby/rubygems/pull/9505) by willnet
|
|
31
|
+
* Use Pathname#absolute?. Pull request [#9529](https://github.com/ruby/rubygems/pull/9529) by nobu
|
|
32
|
+
* Deprecate parsing non-lockfile content in LockfileParser. Pull request [#9502](https://github.com/ruby/rubygems/pull/9502) by kurotaky
|
|
33
|
+
* Print a warning for a potential confusion from the indirect dependencies. Pull request [#5029](https://github.com/ruby/rubygems/pull/5029) by junaruga
|
|
34
|
+
* Respect Gemfile bundler setting in `Bundler.setup`. Pull request [#4892](https://github.com/ruby/rubygems/pull/4892) by godfat
|
|
35
|
+
|
|
36
|
+
### Bug fixes:
|
|
37
|
+
|
|
38
|
+
* Gracefully handle missing checksums in Compact Index. Pull request [#9492](https://github.com/ruby/rubygems/pull/9492) by jneen
|
|
39
|
+
* Skip git source exclusion when lockfile cannot backfill. Pull request [#9544](https://github.com/ruby/rubygems/pull/9544) by yahonda
|
|
40
|
+
* Fix bundle config gemfile unset behavior. Pull request [#9514](https://github.com/ruby/rubygems/pull/9514) by afurm
|
|
41
|
+
|
|
42
|
+
## 4.0.11 / 2026-04-30
|
|
43
|
+
|
|
44
|
+
### Enhancements:
|
|
45
|
+
|
|
46
|
+
* Update gem creation guide URL to rubygems.org. Pull request [#9500](https://github.com/ruby/rubygems/pull/9500) by nissyi-gh
|
|
47
|
+
* Lock the checksum of Bundler itself in the lockfile. Pull request [#9366](https://github.com/ruby/rubygems/pull/9366) by Edouard-chin
|
|
48
|
+
|
|
49
|
+
### Bug fixes:
|
|
50
|
+
|
|
51
|
+
* Fix installing gems with native extensions + transitive dependencies. Pull request [#9477](https://github.com/ruby/rubygems/pull/9477) by nicholasdower
|
|
52
|
+
* Fix the bundler version not being updated in dev/test lockfile. Pull request [#9463](https://github.com/ruby/rubygems/pull/9463) by Edouard-chin
|
|
53
|
+
* Ensure the release CI doesn't break due to the Bundler checksum feature. Pull request [#9436](https://github.com/ruby/rubygems/pull/9436) by Edouard-chin
|
|
54
|
+
|
|
55
|
+
### Documentation:
|
|
56
|
+
|
|
57
|
+
* Fix formatting for BUNDLE_PREFER_PATCH variable in man page. Pull request [#9474](https://github.com/ruby/rubygems/pull/9474) by toy
|
|
58
|
+
|
|
3
59
|
## 4.0.10 / 2026-04-08
|
|
4
60
|
|
|
5
61
|
### Enhancements:
|
data/lib/bundler/cli/add.rb
CHANGED
|
@@ -14,6 +14,9 @@ module Bundler
|
|
|
14
14
|
def run
|
|
15
15
|
Bundler.ui.level = "warn" if options[:quiet]
|
|
16
16
|
|
|
17
|
+
Bundler::CLI::Common.validate_cooldown!(options[:cooldown])
|
|
18
|
+
Bundler.settings.set_command_option_if_given :cooldown, options[:cooldown]
|
|
19
|
+
|
|
17
20
|
validate_options!
|
|
18
21
|
inject_dependencies
|
|
19
22
|
perform_bundle_install unless options["skip-install"]
|
data/lib/bundler/cli/common.rb
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
module Bundler
|
|
4
4
|
module CLI::Common
|
|
5
|
+
def self.validate_cooldown!(value)
|
|
6
|
+
return if value.nil?
|
|
7
|
+
return if value.is_a?(Integer) && value >= 0
|
|
8
|
+
raise InvalidOption, "Expected `--cooldown` to be a non-negative integer, got #{value.inspect}"
|
|
9
|
+
end
|
|
10
|
+
|
|
5
11
|
def self.output_post_install_messages(messages)
|
|
6
12
|
return if Bundler.settings["ignore_messages"]
|
|
7
13
|
messages.to_a.each do |name, msg|
|
data/lib/bundler/cli/config.rb
CHANGED
|
@@ -87,16 +87,21 @@ module Bundler
|
|
|
87
87
|
|
|
88
88
|
if value.nil?
|
|
89
89
|
warn_unused_scope "Ignoring --#{scope} since no value to set was given"
|
|
90
|
+
current_value = Bundler.settings[name]
|
|
90
91
|
|
|
91
92
|
if options[:parseable]
|
|
92
93
|
if value = Bundler.settings[name]
|
|
93
94
|
Bundler.ui.info("#{name}=#{value}")
|
|
94
95
|
end
|
|
95
|
-
|
|
96
|
+
else
|
|
97
|
+
confirm(name)
|
|
96
98
|
end
|
|
97
99
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
+
if current_value.nil?
|
|
101
|
+
exit 1
|
|
102
|
+
else
|
|
103
|
+
return
|
|
104
|
+
end
|
|
100
105
|
end
|
|
101
106
|
|
|
102
107
|
Bundler.ui.info(message) if message
|
data/lib/bundler/cli/gem.rb
CHANGED
|
@@ -288,7 +288,7 @@ module Bundler
|
|
|
288
288
|
open_editor(options["edit"], target.join("#{name}.gemspec")) if options[:edit]
|
|
289
289
|
|
|
290
290
|
Bundler.ui.info "\nGem '#{name}' was successfully created. " \
|
|
291
|
-
"For more information on making a RubyGem visit https://
|
|
291
|
+
"For more information on making a RubyGem visit https://guides.rubygems.org/make-your-own-gem/"
|
|
292
292
|
end
|
|
293
293
|
|
|
294
294
|
private
|
data/lib/bundler/cli/install.rb
CHANGED
|
@@ -112,6 +112,9 @@ module Bundler
|
|
|
112
112
|
|
|
113
113
|
Bundler.settings.set_command_option_if_given :jobs, options["jobs"]
|
|
114
114
|
|
|
115
|
+
Bundler::CLI::Common.validate_cooldown!(options["cooldown"])
|
|
116
|
+
Bundler.settings.set_command_option_if_given :cooldown, options["cooldown"]
|
|
117
|
+
|
|
115
118
|
Bundler.settings.set_command_option_if_given :no_prune, options["no-prune"]
|
|
116
119
|
|
|
117
120
|
Bundler.settings.set_command_option_if_given :no_install, options["no-install"]
|
data/lib/bundler/cli/outdated.rb
CHANGED
|
@@ -26,6 +26,9 @@ module Bundler
|
|
|
26
26
|
def run
|
|
27
27
|
check_for_deployment_mode!
|
|
28
28
|
|
|
29
|
+
Bundler::CLI::Common.validate_cooldown!(options[:cooldown])
|
|
30
|
+
Bundler.settings.set_command_option_if_given :cooldown, options[:cooldown]
|
|
31
|
+
|
|
29
32
|
Bundler.definition.validate_runtime!
|
|
30
33
|
current_specs = Bundler.ui.silence { Bundler.definition.resolve }
|
|
31
34
|
|
|
@@ -199,7 +202,15 @@ module Bundler
|
|
|
199
202
|
end
|
|
200
203
|
|
|
201
204
|
spec_outdated_info = "#{active_spec.name} (newest #{spec_version}, " \
|
|
202
|
-
"installed #{current_version}#{dependency_version}
|
|
205
|
+
"installed #{current_version}#{dependency_version}"
|
|
206
|
+
|
|
207
|
+
release_date = release_date_for(active_spec)
|
|
208
|
+
spec_outdated_info += ", released #{release_date}" unless release_date.empty?
|
|
209
|
+
|
|
210
|
+
remaining = cooldown_days_remaining(active_spec)
|
|
211
|
+
spec_outdated_info += ", in cooldown for #{remaining} more day#{"s" if remaining > 1}" if remaining
|
|
212
|
+
|
|
213
|
+
spec_outdated_info += ")"
|
|
203
214
|
|
|
204
215
|
output_message = if options[:parseable]
|
|
205
216
|
spec_outdated_info.to_s
|
|
@@ -215,13 +226,25 @@ module Bundler
|
|
|
215
226
|
def gem_column_for(current_spec, active_spec, dependency, groups)
|
|
216
227
|
current_version = "#{current_spec.version}#{current_spec.git_version}"
|
|
217
228
|
spec_version = "#{active_spec.version}#{active_spec.git_version}"
|
|
229
|
+
remaining = cooldown_days_remaining(active_spec)
|
|
230
|
+
spec_version += " (cooldown #{remaining}d)" if remaining
|
|
218
231
|
dependency = dependency.requirement if dependency
|
|
219
232
|
|
|
220
233
|
ret_val = [active_spec.name, current_version, spec_version, dependency.to_s, groups.to_s]
|
|
234
|
+
ret_val << release_date_for(active_spec)
|
|
221
235
|
ret_val << loaded_from_for(active_spec).to_s if Bundler.ui.debug?
|
|
222
236
|
ret_val
|
|
223
237
|
end
|
|
224
238
|
|
|
239
|
+
def cooldown_days_remaining(spec, now = Time.now)
|
|
240
|
+
return nil unless spec.respond_to?(:created_at) && spec.created_at
|
|
241
|
+
return nil unless spec.respond_to?(:remote) && spec.remote
|
|
242
|
+
days = spec.remote.effective_cooldown
|
|
243
|
+
return nil if days.nil? || days <= 0
|
|
244
|
+
remaining = days - ((now - spec.created_at) / 86_400.0)
|
|
245
|
+
remaining > 0 ? remaining.ceil : nil
|
|
246
|
+
end
|
|
247
|
+
|
|
225
248
|
def check_for_deployment_mode!
|
|
226
249
|
return unless Bundler.frozen_bundle?
|
|
227
250
|
suggested_command = if Bundler.settings.locations("frozen").keys.&([:global, :local]).any?
|
|
@@ -283,11 +306,28 @@ module Bundler
|
|
|
283
306
|
end
|
|
284
307
|
|
|
285
308
|
def table_header
|
|
286
|
-
header = ["Gem", "Current", "Latest", "Requested", "Groups"]
|
|
309
|
+
header = ["Gem", "Current", "Latest", "Requested", "Groups", "Release Date"]
|
|
287
310
|
header << "Path" if Bundler.ui.debug?
|
|
288
311
|
header
|
|
289
312
|
end
|
|
290
313
|
|
|
314
|
+
def release_date_for(spec)
|
|
315
|
+
return "" unless spec.respond_to?(:date)
|
|
316
|
+
|
|
317
|
+
date = spec.date
|
|
318
|
+
return "" unless date
|
|
319
|
+
|
|
320
|
+
return "" unless Gem.const_defined?(:DEFAULT_SOURCE_DATE_EPOCH)
|
|
321
|
+
default_date = Time.at(Gem::DEFAULT_SOURCE_DATE_EPOCH).utc
|
|
322
|
+
default_date = Time.utc(default_date.year, default_date.month, default_date.day)
|
|
323
|
+
|
|
324
|
+
date = date.utc if date.respond_to?(:utc)
|
|
325
|
+
|
|
326
|
+
return "" if date == default_date
|
|
327
|
+
|
|
328
|
+
date.strftime("%Y-%m-%d")
|
|
329
|
+
end
|
|
330
|
+
|
|
291
331
|
def justify(row, sizes)
|
|
292
332
|
row.each_with_index.map do |element, index|
|
|
293
333
|
element.ljust(sizes[index])
|
data/lib/bundler/cli/update.rb
CHANGED
|
@@ -66,6 +66,8 @@ module Bundler
|
|
|
66
66
|
opts["force"] = options[:redownload] if options[:redownload]
|
|
67
67
|
|
|
68
68
|
Bundler.settings.set_command_option_if_given :jobs, opts["jobs"]
|
|
69
|
+
Bundler::CLI::Common.validate_cooldown!(options[:cooldown])
|
|
70
|
+
Bundler.settings.set_command_option_if_given :cooldown, options[:cooldown]
|
|
69
71
|
|
|
70
72
|
Bundler.definition.validate_runtime!
|
|
71
73
|
|
data/lib/bundler/cli.rb
CHANGED
|
@@ -61,18 +61,18 @@ module Bundler
|
|
|
61
61
|
|
|
62
62
|
current_cmd = args.last[:current_command].name
|
|
63
63
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
64
|
+
# `bundle config` manages stored settings, so avoid promoting settings
|
|
65
|
+
# like `gemfile` or `lockfile` to environment variables before it runs.
|
|
66
|
+
unless current_cmd == "config"
|
|
67
|
+
Bundler.configure_custom_gemfile(options[:gemfile])
|
|
68
|
+
|
|
69
|
+
# lock --lockfile works differently than install --lockfile
|
|
70
|
+
unless current_cmd == "lock"
|
|
71
|
+
custom_lockfile = options[:lockfile] || ENV["BUNDLE_LOCKFILE"] || Bundler.settings[:lockfile]
|
|
72
|
+
if custom_lockfile && !custom_lockfile.empty?
|
|
73
|
+
Bundler::SharedHelpers.set_env "BUNDLE_LOCKFILE", File.expand_path(custom_lockfile)
|
|
74
|
+
reset_settings = true
|
|
75
|
+
end
|
|
76
76
|
end
|
|
77
77
|
end
|
|
78
78
|
|
|
@@ -274,6 +274,7 @@ module Bundler
|
|
|
274
274
|
method_option "target-rbconfig", type: :string, banner: "Path to rbconfig.rb for the deployment target platform"
|
|
275
275
|
method_option "without", type: :array, banner: "Exclude gems that are part of the specified named group (removed)."
|
|
276
276
|
method_option "with", type: :array, banner: "Include gems that are part of the specified named group (removed)."
|
|
277
|
+
method_option "cooldown", type: :numeric, banner: "Only consider gem versions published at least N days ago. Use 0 to disable."
|
|
277
278
|
def install
|
|
278
279
|
%w[clean deployment frozen no-prune path shebang without with].each do |option|
|
|
279
280
|
remembered_flag_deprecation(option)
|
|
@@ -324,6 +325,7 @@ module Bundler
|
|
|
324
325
|
method_option "strict", type: :boolean, banner: "Do not allow any gem to be updated past latest --patch | --minor | --major"
|
|
325
326
|
method_option "conservative", type: :boolean, banner: "Use bundle install conservative update behavior and do not allow shared dependencies to be updated."
|
|
326
327
|
method_option "all", type: :boolean, banner: "Update everything."
|
|
328
|
+
method_option "cooldown", type: :numeric, banner: "Only consider gem versions published at least N days ago. Use 0 to disable."
|
|
327
329
|
def update(*gems)
|
|
328
330
|
require_relative "cli/update"
|
|
329
331
|
Bundler.settings.temporary(no_install: false) do
|
|
@@ -405,6 +407,7 @@ module Bundler
|
|
|
405
407
|
method_option "skip-install", type: :boolean, banner: "Adds gem to the Gemfile but does not install it"
|
|
406
408
|
method_option "optimistic", type: :boolean, banner: "Adds optimistic declaration of version to gem"
|
|
407
409
|
method_option "strict", type: :boolean, banner: "Adds strict declaration of version to gem"
|
|
410
|
+
method_option "cooldown", type: :numeric, banner: "Only consider gem versions published at least N days ago. Use 0 to disable."
|
|
408
411
|
def add(*gems)
|
|
409
412
|
require_relative "cli/add"
|
|
410
413
|
Add.new(options.dup, gems).run
|
|
@@ -435,6 +438,7 @@ module Bundler
|
|
|
435
438
|
method_option "filter-patch", type: :boolean, banner: "Only list patch newer versions"
|
|
436
439
|
method_option "parseable", aliases: "--porcelain", type: :boolean, banner: "Use minimal formatting for more parseable output"
|
|
437
440
|
method_option "only-explicit", type: :boolean, banner: "Only list gems specified in your Gemfile, not their dependencies"
|
|
441
|
+
method_option "cooldown", type: :numeric, banner: "Only consider gem versions published at least N days ago. Use 0 to disable."
|
|
438
442
|
def outdated(*gems)
|
|
439
443
|
require_relative "cli/outdated"
|
|
440
444
|
Outdated.new(options, gems).run
|
|
@@ -71,7 +71,10 @@ module Bundler
|
|
|
71
71
|
# This method gets called at least once for every gem when parsing versions.
|
|
72
72
|
def parse_version_checksum(line, checksums)
|
|
73
73
|
return unless (name_end = line.index(" ")) # Artifactory bug causes blank lines in artifactor index files
|
|
74
|
-
|
|
74
|
+
checksum_start = line.index(" ", name_end + 1)
|
|
75
|
+
return unless checksum_start
|
|
76
|
+
checksum_start += 1
|
|
77
|
+
|
|
75
78
|
checksum_end = line.size - checksum_start
|
|
76
79
|
|
|
77
80
|
line.freeze # allows slicing into the string to not allocate a copy of the line
|
data/lib/bundler/definition.rb
CHANGED
|
@@ -783,7 +783,25 @@ module Bundler
|
|
|
783
783
|
end
|
|
784
784
|
|
|
785
785
|
def precompute_source_requirements_for_indirect_dependencies?
|
|
786
|
-
sources.non_global_rubygems_sources.all?(&:dependency_api_available?)
|
|
786
|
+
if sources.non_global_rubygems_sources.all?(&:dependency_api_available?)
|
|
787
|
+
true
|
|
788
|
+
else
|
|
789
|
+
non_dependency_api_warning
|
|
790
|
+
false
|
|
791
|
+
end
|
|
792
|
+
end
|
|
793
|
+
|
|
794
|
+
def non_dependency_api_warning
|
|
795
|
+
non_api_sources = sources.non_global_rubygems_sources.reject(&:dependency_api_available?)
|
|
796
|
+
non_api_source_names = non_api_sources.map {|d| " * #{d}" }.join("\n")
|
|
797
|
+
|
|
798
|
+
msg = String.new
|
|
799
|
+
msg << "Your Gemfile contains scoped sources that don't implement a dependency API, namely:\n\n"
|
|
800
|
+
msg << non_api_source_names
|
|
801
|
+
msg << "\n\nUsing the above gem servers may result in installing unexpected gems. " \
|
|
802
|
+
"To resolve this warning, make sure you use gem servers that implement dependency APIs, " \
|
|
803
|
+
"such as gemstash or geminabox gem servers."
|
|
804
|
+
Bundler.ui.warn msg
|
|
787
805
|
end
|
|
788
806
|
|
|
789
807
|
def current_platform_locked?
|
|
@@ -988,6 +1006,8 @@ module Bundler
|
|
|
988
1006
|
end
|
|
989
1007
|
end
|
|
990
1008
|
|
|
1009
|
+
sources.metadata_source.checksum_store.merge!(@locked_gems.metadata_source.checksum_store) if @locked_gems
|
|
1010
|
+
|
|
991
1011
|
changes
|
|
992
1012
|
end
|
|
993
1013
|
|
|
@@ -1157,16 +1177,20 @@ module Bundler
|
|
|
1157
1177
|
def find_source_requirements
|
|
1158
1178
|
preload_git_sources
|
|
1159
1179
|
|
|
1180
|
+
# Only safe to exclude when locked_requirements (merged below) backfills the gap.
|
|
1181
|
+
nothing_changed = nothing_changed?
|
|
1182
|
+
excluded = nothing_changed ? excluded_git_sources : []
|
|
1183
|
+
|
|
1160
1184
|
# Record the specs available in each gem's source, so that those
|
|
1161
1185
|
# specs will be available later when the resolver knows where to
|
|
1162
1186
|
# look for that gemspec (or its dependencies)
|
|
1163
1187
|
source_requirements = if precompute_source_requirements_for_indirect_dependencies?
|
|
1164
|
-
all_requirements = source_map.all_requirements(
|
|
1188
|
+
all_requirements = source_map.all_requirements(excluded)
|
|
1165
1189
|
{ default: default_source }.merge(all_requirements)
|
|
1166
1190
|
else
|
|
1167
|
-
{ default: Source::RubygemsAggregate.new(sources, source_map,
|
|
1191
|
+
{ default: Source::RubygemsAggregate.new(sources, source_map, excluded) }.merge(source_map.direct_requirements)
|
|
1168
1192
|
end
|
|
1169
|
-
source_requirements.merge!(source_map.locked_requirements) if nothing_changed
|
|
1193
|
+
source_requirements.merge!(source_map.locked_requirements) if nothing_changed
|
|
1170
1194
|
metadata_dependencies.each do |dep|
|
|
1171
1195
|
source_requirements[dep.name] = sources.metadata_source
|
|
1172
1196
|
end
|
data/lib/bundler/dsl.rb
CHANGED
|
@@ -116,6 +116,10 @@ module Bundler
|
|
|
116
116
|
options = args.last.is_a?(Hash) ? args.pop.dup : {}
|
|
117
117
|
options = normalize_hash(options)
|
|
118
118
|
source = normalize_source(source)
|
|
119
|
+
cooldown = options["cooldown"]
|
|
120
|
+
if cooldown && !(cooldown.is_a?(Integer) && cooldown >= 0)
|
|
121
|
+
raise InvalidOption, "Expected `cooldown` to be a non-negative integer, got #{cooldown.inspect}"
|
|
122
|
+
end
|
|
119
123
|
|
|
120
124
|
if options.key?("type")
|
|
121
125
|
options["type"] = options["type"].to_s
|
|
@@ -130,9 +134,9 @@ module Bundler
|
|
|
130
134
|
source_opts = options.merge("uri" => source)
|
|
131
135
|
with_source(@sources.add_plugin_source(options["type"], source_opts), &blk)
|
|
132
136
|
elsif block_given?
|
|
133
|
-
with_source(@sources.add_rubygems_source("remotes" => source), &blk)
|
|
137
|
+
with_source(@sources.add_rubygems_source("remotes" => source, "cooldown" => cooldown), &blk)
|
|
134
138
|
else
|
|
135
|
-
@sources.add_global_rubygems_remote(source)
|
|
139
|
+
@sources.add_global_rubygems_remote(source, cooldown: cooldown)
|
|
136
140
|
end
|
|
137
141
|
end
|
|
138
142
|
|
|
@@ -5,7 +5,7 @@ module Bundler
|
|
|
5
5
|
class EndpointSpecification < Gem::Specification
|
|
6
6
|
include MatchRemoteMetadata
|
|
7
7
|
|
|
8
|
-
attr_reader :name, :version, :platform, :checksum
|
|
8
|
+
attr_reader :name, :version, :platform, :checksum, :created_at
|
|
9
9
|
attr_writer :dependencies
|
|
10
10
|
attr_accessor :remote, :locked_platform
|
|
11
11
|
|
|
@@ -145,6 +145,7 @@ module Bundler
|
|
|
145
145
|
unless data
|
|
146
146
|
@required_ruby_version = nil
|
|
147
147
|
@required_rubygems_version = nil
|
|
148
|
+
@created_at = nil
|
|
148
149
|
return
|
|
149
150
|
end
|
|
150
151
|
|
|
@@ -161,6 +162,15 @@ module Bundler
|
|
|
161
162
|
@required_rubygems_version = Gem::Requirement.new(v)
|
|
162
163
|
when "ruby"
|
|
163
164
|
@required_ruby_version = Gem::Requirement.new(v)
|
|
165
|
+
when "created_at"
|
|
166
|
+
value = v.is_a?(Array) ? v.last : v
|
|
167
|
+
if value.is_a?(String)
|
|
168
|
+
@created_at = begin
|
|
169
|
+
Time.new(value)
|
|
170
|
+
rescue ArgumentError
|
|
171
|
+
nil
|
|
172
|
+
end
|
|
173
|
+
end
|
|
164
174
|
end
|
|
165
175
|
end
|
|
166
176
|
rescue StandardError => e
|
|
@@ -6,7 +6,7 @@ require_relative "gem_installer"
|
|
|
6
6
|
module Bundler
|
|
7
7
|
class ParallelInstaller
|
|
8
8
|
class SpecInstallation
|
|
9
|
-
attr_accessor :spec, :name, :full_name, :post_install_message, :state, :error
|
|
9
|
+
attr_accessor :spec, :name, :full_name, :post_install_message, :state, :error, :dependencies
|
|
10
10
|
def initialize(spec)
|
|
11
11
|
@spec = spec
|
|
12
12
|
@name = spec.name
|
|
@@ -46,25 +46,11 @@ module Bundler
|
|
|
46
46
|
!post_install_message.empty?
|
|
47
47
|
end
|
|
48
48
|
|
|
49
|
-
|
|
50
|
-
dep.type == :development || dep.name == @name
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
# Checks installed dependencies against spec's dependencies to make
|
|
54
|
-
# sure needed dependencies have been installed.
|
|
49
|
+
# Recursively checks that all dependencies (direct and transitive) have been installed.
|
|
55
50
|
def dependencies_installed?(installed_specs)
|
|
56
|
-
dependencies.all?
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
# Represents only the non-development dependencies, the ones that are
|
|
60
|
-
# itself and are in the total list.
|
|
61
|
-
def dependencies
|
|
62
|
-
@dependencies ||= all_dependencies.reject {|dep| ignorable_dependency? dep }
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
# Represents all dependencies
|
|
66
|
-
def all_dependencies
|
|
67
|
-
@spec.dependencies
|
|
51
|
+
dependencies.all? do |dep|
|
|
52
|
+
installed_specs.include?(dep.name) && dep.dependencies_installed?(installed_specs)
|
|
53
|
+
end
|
|
68
54
|
end
|
|
69
55
|
|
|
70
56
|
def to_s
|
|
@@ -85,6 +71,12 @@ module Bundler
|
|
|
85
71
|
@force = force
|
|
86
72
|
@local = local
|
|
87
73
|
@specs = all_specs.map {|s| SpecInstallation.new(s) }
|
|
74
|
+
specs_by_name = @specs.to_h {|s| [s.name, s] }
|
|
75
|
+
@specs.each do |spec_install|
|
|
76
|
+
spec_install.dependencies = spec_install.spec.dependencies.filter_map do |dep|
|
|
77
|
+
specs_by_name[dep.name] unless dep.type == :development || dep.name == spec_install.name
|
|
78
|
+
end
|
|
79
|
+
end
|
|
88
80
|
@specs.each do |spec_install|
|
|
89
81
|
spec_install.state = :installed if skip.include?(spec_install.name)
|
|
90
82
|
end if skip
|
data/lib/bundler/installer.rb
CHANGED
|
@@ -63,6 +63,11 @@ module Bundler
|
|
|
63
63
|
Bundler.create_bundle_path
|
|
64
64
|
|
|
65
65
|
ProcessLock.lock do
|
|
66
|
+
# Invalidate any stale gem specification cache from before we acquired the lock.
|
|
67
|
+
# Another process may have installed gems while we were waiting.
|
|
68
|
+
Gem::Specification.reset
|
|
69
|
+
@definition.sources.clear_cache
|
|
70
|
+
|
|
66
71
|
@definition.ensure_equivalent_gemfile_and_lockfile(options[:deployment])
|
|
67
72
|
|
|
68
73
|
if @definition.dependencies.empty?
|
|
@@ -71,7 +71,8 @@ module Bundler
|
|
|
71
71
|
checksums = definition.resolve.map do |spec|
|
|
72
72
|
spec.source.checksum_store.to_lock(spec)
|
|
73
73
|
end
|
|
74
|
-
|
|
74
|
+
|
|
75
|
+
add_section("CHECKSUMS", checksums + bundler_checksum)
|
|
75
76
|
end
|
|
76
77
|
|
|
77
78
|
def add_locked_ruby_version
|
|
@@ -100,5 +101,19 @@ module Bundler
|
|
|
100
101
|
raise ArgumentError, "#{value.inspect} can't be serialized in a lockfile"
|
|
101
102
|
end
|
|
102
103
|
end
|
|
104
|
+
|
|
105
|
+
def bundler_checksum
|
|
106
|
+
return [] if Bundler.gem_version.to_s.end_with?(".dev") || ENV["SKIP_BUNDLER_CHECKSUM"]
|
|
107
|
+
|
|
108
|
+
bundler_spec = definition.sources.metadata_source.specs.search(["bundler", Bundler.gem_version]).last
|
|
109
|
+
return [] unless File.exist?(bundler_spec.cache_file)
|
|
110
|
+
|
|
111
|
+
require "rubygems/package"
|
|
112
|
+
|
|
113
|
+
package = Gem::Package.new(bundler_spec.cache_file)
|
|
114
|
+
definition.sources.metadata_source.checksum_store.register(bundler_spec, Checksum.from_gem_package(package))
|
|
115
|
+
|
|
116
|
+
[definition.sources.metadata_source.checksum_store.to_lock(bundler_spec)]
|
|
117
|
+
end
|
|
103
118
|
end
|
|
104
119
|
end
|
|
@@ -28,6 +28,7 @@ module Bundler
|
|
|
28
28
|
|
|
29
29
|
attr_reader(
|
|
30
30
|
:sources,
|
|
31
|
+
:metadata_source,
|
|
31
32
|
:dependencies,
|
|
32
33
|
:specs,
|
|
33
34
|
:platforms,
|
|
@@ -97,6 +98,7 @@ module Bundler
|
|
|
97
98
|
def initialize(lockfile, strict: false)
|
|
98
99
|
@platforms = []
|
|
99
100
|
@sources = []
|
|
101
|
+
@metadata_source = Source::Metadata.new
|
|
100
102
|
@dependencies = {}
|
|
101
103
|
@parse_method = nil
|
|
102
104
|
@specs = {}
|
|
@@ -113,6 +115,17 @@ module Bundler
|
|
|
113
115
|
"Run `git checkout HEAD -- #{@lockfile_path}` first to get a clean lock."
|
|
114
116
|
end
|
|
115
117
|
|
|
118
|
+
@valid = lockfile.strip.empty? ||
|
|
119
|
+
lockfile.split(/(?:\r?\n)+/).any? {|l| KNOWN_SECTIONS.include?(l) }
|
|
120
|
+
|
|
121
|
+
unless @valid
|
|
122
|
+
SharedHelpers.feature_deprecated!(
|
|
123
|
+
"Your #{@lockfile_path} does not appear to be a valid lockfile. " \
|
|
124
|
+
"Run `rm #{@lockfile_path}` and then `bundle install` to generate a new lockfile. " \
|
|
125
|
+
"This will raise a LockfileError in a future version of Bundler."
|
|
126
|
+
)
|
|
127
|
+
end
|
|
128
|
+
|
|
116
129
|
lockfile.split(/((?:\r?\n)+)/) do |line|
|
|
117
130
|
# split alternates between the line and the following whitespace
|
|
118
131
|
next @pos.advance!(line) if line.match?(/^\s*$/)
|
|
@@ -162,6 +175,10 @@ module Bundler
|
|
|
162
175
|
bundler_version.nil? || bundler_version < Gem::Version.new("1.16.2")
|
|
163
176
|
end
|
|
164
177
|
|
|
178
|
+
def valid?
|
|
179
|
+
@valid
|
|
180
|
+
end
|
|
181
|
+
|
|
165
182
|
private
|
|
166
183
|
|
|
167
184
|
TYPES = {
|
|
@@ -252,7 +269,12 @@ module Bundler
|
|
|
252
269
|
version = Gem::Version.new(version)
|
|
253
270
|
platform = platform ? Gem::Platform.new(platform) : Gem::Platform::RUBY
|
|
254
271
|
full_name = Gem::NameTuple.new(name, version, platform).full_name
|
|
255
|
-
|
|
272
|
+
spec = @specs[full_name]
|
|
273
|
+
|
|
274
|
+
if name == "bundler"
|
|
275
|
+
spec ||= LazySpecification.new(name, version, platform, @metadata_source)
|
|
276
|
+
end
|
|
277
|
+
return unless spec
|
|
256
278
|
|
|
257
279
|
if checksums
|
|
258
280
|
checksums.split(",") do |lock_checksum|
|
|
@@ -1,10 +1,10 @@
|
|
|
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" "
|
|
3
|
+
.TH "BUNDLE\-ADD" "1" "April 2026" ""
|
|
4
4
|
.SH "NAME"
|
|
5
5
|
\fBbundle\-add\fR \- Add gem to the Gemfile and run bundle install
|
|
6
6
|
.SH "SYNOPSIS"
|
|
7
|
-
\fBbundle add\fR \fIGEM_NAME\fR [\-\-group=GROUP] [\-\-version=VERSION] [\-\-source=SOURCE] [\-\-path=PATH] [\-\-git=GIT|\-\-github=GITHUB] [\-\-branch=BRANCH] [\-\-ref=REF] [\-\-quiet] [\-\-skip\-install] [\-\-strict|\-\-optimistic]
|
|
7
|
+
\fBbundle add\fR \fIGEM_NAME\fR [\-\-group=GROUP] [\-\-version=VERSION] [\-\-source=SOURCE] [\-\-path=PATH] [\-\-git=GIT|\-\-github=GITHUB] [\-\-branch=BRANCH] [\-\-ref=REF] [\-\-cooldown=NUMBER] [\-\-quiet] [\-\-skip\-install] [\-\-strict|\-\-optimistic]
|
|
8
8
|
.SH "DESCRIPTION"
|
|
9
9
|
Adds the named gem to the [\fBGemfile(5)\fR][Gemfile(5)] and run \fBbundle install\fR\. \fBbundle install\fR can be avoided by using the flag \fB\-\-skip\-install\fR\.
|
|
10
10
|
.SH "OPTIONS"
|
|
@@ -50,6 +50,9 @@ Adds optimistic declaration of version\.
|
|
|
50
50
|
.TP
|
|
51
51
|
\fB\-\-strict\fR
|
|
52
52
|
Adds strict declaration of version\.
|
|
53
|
+
.TP
|
|
54
|
+
\fB\-\-cooldown=<number>\fR
|
|
55
|
+
Only consider gem versions published at least \fInumber\fR days ago when resolving\. Pass \fB0\fR to disable cooldown for this run\. See \fBcooldown\fR in bundle\-config(1) for precedence rules\.
|
|
53
56
|
.SH "EXAMPLES"
|
|
54
57
|
.IP "1." 4
|
|
55
58
|
You can add the \fBrails\fR gem to the Gemfile without any version restriction\. The source of the gem will be the global source\.
|
|
@@ -5,7 +5,7 @@ bundle-add(1) -- Add gem to the Gemfile and run bundle install
|
|
|
5
5
|
|
|
6
6
|
`bundle add` <GEM_NAME> [--group=GROUP] [--version=VERSION] [--source=SOURCE]
|
|
7
7
|
[--path=PATH] [--git=GIT|--github=GITHUB] [--branch=BRANCH] [--ref=REF]
|
|
8
|
-
[--quiet] [--skip-install] [--strict|--optimistic]
|
|
8
|
+
[--cooldown=NUMBER] [--quiet] [--skip-install] [--strict|--optimistic]
|
|
9
9
|
|
|
10
10
|
## DESCRIPTION
|
|
11
11
|
|
|
@@ -56,6 +56,11 @@ Adds the named gem to the [`Gemfile(5)`][Gemfile(5)] and run `bundle install`.
|
|
|
56
56
|
* `--strict`:
|
|
57
57
|
Adds strict declaration of version.
|
|
58
58
|
|
|
59
|
+
* `--cooldown=<number>`:
|
|
60
|
+
Only consider gem versions published at least <number> days ago when
|
|
61
|
+
resolving. Pass `0` to disable cooldown for this run. See `cooldown`
|
|
62
|
+
in bundle-config(1) for precedence rules.
|
|
63
|
+
|
|
59
64
|
## EXAMPLES
|
|
60
65
|
|
|
61
66
|
1. You can add the `rails` gem to the Gemfile without any version restriction.
|
|
@@ -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" "
|
|
3
|
+
.TH "BUNDLE\-BINSTUBS" "1" "April 2026" ""
|
|
4
4
|
.SH "NAME"
|
|
5
5
|
\fBbundle\-binstubs\fR \- Install the binstubs of the listed gems
|
|
6
6
|
.SH "SYNOPSIS"
|