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
|
@@ -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\-OUTDATED" "1" "
|
|
3
|
+
.TH "BUNDLE\-OUTDATED" "1" "April 2026" ""
|
|
4
4
|
.SH "NAME"
|
|
5
5
|
\fBbundle\-outdated\fR \- List installed gems with newer versions available
|
|
6
6
|
.SH "SYNOPSIS"
|
|
7
|
-
\fBbundle outdated\fR [GEM] [\-\-local] [\-\-pre] [\-\-source] [\-\-filter\-strict | \-\-strict] [\-\-update\-strict] [\-\-parseable | \-\-porcelain] [\-\-group=GROUP] [\-\-groups] [\-\-patch|\-\-minor|\-\-major] [\-\-filter\-major] [\-\-filter\-minor] [\-\-filter\-patch] [\-\-only\-explicit]
|
|
7
|
+
\fBbundle outdated\fR [GEM] [\-\-local] [\-\-pre] [\-\-source] [\-\-filter\-strict | \-\-strict] [\-\-update\-strict] [\-\-parseable | \-\-porcelain] [\-\-group=GROUP] [\-\-groups] [\-\-patch|\-\-minor|\-\-major] [\-\-filter\-major] [\-\-filter\-minor] [\-\-filter\-patch] [\-\-only\-explicit] [\-\-cooldown=NUMBER]
|
|
8
8
|
.SH "DESCRIPTION"
|
|
9
9
|
Outdated lists the names and versions of gems that have a newer version available in the given source\. Calling outdated with [GEM [GEM]] will only check for newer versions of the given gems\. Prerelease gems are ignored by default\. If your gems are up to date, Bundler will exit with a status of 0\. Otherwise, it will exit 1\.
|
|
10
10
|
.SH "OPTIONS"
|
|
@@ -53,6 +53,9 @@ Only list patch newer versions\.
|
|
|
53
53
|
.TP
|
|
54
54
|
\fB\-\-only\-explicit\fR
|
|
55
55
|
Only list gems specified in your Gemfile, not their dependencies\.
|
|
56
|
+
.TP
|
|
57
|
+
\fB\-\-cooldown=<number>\fR
|
|
58
|
+
Annotate (rather than hide) versions that are still inside the cooldown window of \fInumber\fR days\. The prose output appends "in cooldown for Nd more days" and the table form adds "(cooldown Nd)" to the Latest column\. See \fBcooldown\fR in bundle\-config(1)\.
|
|
56
59
|
.SH "PATCH LEVEL OPTIONS"
|
|
57
60
|
See bundle update(1) \fIbundle\-update\.1\.html\fR for details\.
|
|
58
61
|
.SH "FILTERING OUTPUT"
|
|
@@ -61,42 +64,42 @@ The 3 filtering options do not affect the resolution of versions, merely what ve
|
|
|
61
64
|
If the regular output shows the following:
|
|
62
65
|
.IP "" 4
|
|
63
66
|
.nf
|
|
64
|
-
* Gem Current Latest Requested Groups
|
|
65
|
-
* faker 1\.6\.5 1\.6\.6 ~> 1\.4 development, test
|
|
66
|
-
* hashie 1\.2\.0 3\.4\.6 = 1\.2\.0 default
|
|
67
|
-
* headless 2\.2\.3 2\.3\.1 = 2\.2\.3 test
|
|
67
|
+
* Gem Current Latest Requested Groups Release Date
|
|
68
|
+
* faker 1\.6\.5 1\.6\.6 ~> 1\.4 development, test 2024\-02\-05
|
|
69
|
+
* hashie 1\.2\.0 3\.4\.6 = 1\.2\.0 default 2023\-11\-10
|
|
70
|
+
* headless 2\.2\.3 2\.3\.1 = 2\.2\.3 test 2022\-08\-19
|
|
68
71
|
.fi
|
|
69
72
|
.IP "" 0
|
|
70
73
|
.P
|
|
71
74
|
\fB\-\-filter\-major\fR would only show:
|
|
72
75
|
.IP "" 4
|
|
73
76
|
.nf
|
|
74
|
-
* Gem Current Latest Requested Groups
|
|
75
|
-
* hashie 1\.2\.0 3\.4\.6 = 1\.2\.0 default
|
|
77
|
+
* Gem Current Latest Requested Groups Release Date
|
|
78
|
+
* hashie 1\.2\.0 3\.4\.6 = 1\.2\.0 default 2023\-11\-10
|
|
76
79
|
.fi
|
|
77
80
|
.IP "" 0
|
|
78
81
|
.P
|
|
79
82
|
\fB\-\-filter\-minor\fR would only show:
|
|
80
83
|
.IP "" 4
|
|
81
84
|
.nf
|
|
82
|
-
* Gem Current Latest Requested Groups
|
|
83
|
-
* headless 2\.2\.3 2\.3\.1 = 2\.2\.3 test
|
|
85
|
+
* Gem Current Latest Requested Groups Release Date
|
|
86
|
+
* headless 2\.2\.3 2\.3\.1 = 2\.2\.3 test 2022\-08\-19
|
|
84
87
|
.fi
|
|
85
88
|
.IP "" 0
|
|
86
89
|
.P
|
|
87
90
|
\fB\-\-filter\-patch\fR would only show:
|
|
88
91
|
.IP "" 4
|
|
89
92
|
.nf
|
|
90
|
-
* Gem Current Latest Requested Groups
|
|
91
|
-
* faker 1\.6\.5 1\.6\.6 ~> 1\.4 development, test
|
|
93
|
+
* Gem Current Latest Requested Groups Release Date
|
|
94
|
+
* faker 1\.6\.5 1\.6\.6 ~> 1\.4 development, test 2024\-02\-05
|
|
92
95
|
.fi
|
|
93
96
|
.IP "" 0
|
|
94
97
|
.P
|
|
95
98
|
Filter options can be combined\. \fB\-\-filter\-minor\fR and \fB\-\-filter\-patch\fR would show:
|
|
96
99
|
.IP "" 4
|
|
97
100
|
.nf
|
|
98
|
-
* Gem Current Latest Requested Groups
|
|
99
|
-
* faker 1\.6\.5 1\.6\.6 ~> 1\.4 development, test
|
|
101
|
+
* Gem Current Latest Requested Groups Release Date
|
|
102
|
+
* faker 1\.6\.5 1\.6\.6 ~> 1\.4 development, test 2024\-02\-05
|
|
100
103
|
.fi
|
|
101
104
|
.IP "" 0
|
|
102
105
|
.P
|
|
@@ -16,6 +16,7 @@ bundle-outdated(1) -- List installed gems with newer versions available
|
|
|
16
16
|
[--filter-minor]
|
|
17
17
|
[--filter-patch]
|
|
18
18
|
[--only-explicit]
|
|
19
|
+
[--cooldown=NUMBER]
|
|
19
20
|
|
|
20
21
|
## DESCRIPTION
|
|
21
22
|
|
|
@@ -71,6 +72,12 @@ are up to date, Bundler will exit with a status of 0. Otherwise, it will exit 1.
|
|
|
71
72
|
* `--only-explicit`:
|
|
72
73
|
Only list gems specified in your Gemfile, not their dependencies.
|
|
73
74
|
|
|
75
|
+
* `--cooldown=<number>`:
|
|
76
|
+
Annotate (rather than hide) versions that are still inside the
|
|
77
|
+
cooldown window of <number> days. The prose output appends "in
|
|
78
|
+
cooldown for Nd more days" and the table form adds "(cooldown Nd)" to
|
|
79
|
+
the Latest column. See `cooldown` in bundle-config(1).
|
|
80
|
+
|
|
74
81
|
## PATCH LEVEL OPTIONS
|
|
75
82
|
|
|
76
83
|
See [bundle update(1)](bundle-update.1.html) for details.
|
|
@@ -82,29 +89,29 @@ in the output.
|
|
|
82
89
|
|
|
83
90
|
If the regular output shows the following:
|
|
84
91
|
|
|
85
|
-
* Gem Current Latest Requested Groups
|
|
86
|
-
* faker 1.6.5 1.6.6 ~> 1.4 development, test
|
|
87
|
-
* hashie 1.2.0 3.4.6 = 1.2.0 default
|
|
88
|
-
* headless 2.2.3 2.3.1 = 2.2.3 test
|
|
92
|
+
* Gem Current Latest Requested Groups Release Date
|
|
93
|
+
* faker 1.6.5 1.6.6 ~> 1.4 development, test 2024-02-05
|
|
94
|
+
* hashie 1.2.0 3.4.6 = 1.2.0 default 2023-11-10
|
|
95
|
+
* headless 2.2.3 2.3.1 = 2.2.3 test 2022-08-19
|
|
89
96
|
|
|
90
97
|
`--filter-major` would only show:
|
|
91
98
|
|
|
92
|
-
* Gem Current Latest Requested Groups
|
|
93
|
-
* hashie 1.2.0 3.4.6 = 1.2.0 default
|
|
99
|
+
* Gem Current Latest Requested Groups Release Date
|
|
100
|
+
* hashie 1.2.0 3.4.6 = 1.2.0 default 2023-11-10
|
|
94
101
|
|
|
95
102
|
`--filter-minor` would only show:
|
|
96
103
|
|
|
97
|
-
* Gem Current Latest Requested Groups
|
|
98
|
-
* headless 2.2.3 2.3.1 = 2.2.3 test
|
|
104
|
+
* Gem Current Latest Requested Groups Release Date
|
|
105
|
+
* headless 2.2.3 2.3.1 = 2.2.3 test 2022-08-19
|
|
99
106
|
|
|
100
107
|
`--filter-patch` would only show:
|
|
101
108
|
|
|
102
|
-
* Gem Current Latest Requested Groups
|
|
103
|
-
* faker 1.6.5 1.6.6 ~> 1.4 development, test
|
|
109
|
+
* Gem Current Latest Requested Groups Release Date
|
|
110
|
+
* faker 1.6.5 1.6.6 ~> 1.4 development, test 2024-02-05
|
|
104
111
|
|
|
105
112
|
Filter options can be combined. `--filter-minor` and `--filter-patch` would show:
|
|
106
113
|
|
|
107
|
-
* Gem Current Latest Requested Groups
|
|
108
|
-
* faker 1.6.5 1.6.6 ~> 1.4 development, test
|
|
114
|
+
* Gem Current Latest Requested Groups Release Date
|
|
115
|
+
* faker 1.6.5 1.6.6 ~> 1.4 development, test 2024-02-05
|
|
109
116
|
|
|
110
117
|
Combining all three `filter` options would be the same result as providing none of them.
|
|
@@ -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" "
|
|
3
|
+
.TH "BUNDLE\-PLATFORM" "1" "April 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\-PRISTINE" "1" "
|
|
3
|
+
.TH "BUNDLE\-PRISTINE" "1" "April 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\-SHOW" "1" "
|
|
3
|
+
.TH "BUNDLE\-SHOW" "1" "April 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,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\-UPDATE" "1" "
|
|
3
|
+
.TH "BUNDLE\-UPDATE" "1" "April 2026" ""
|
|
4
4
|
.SH "NAME"
|
|
5
5
|
\fBbundle\-update\fR \- Update your gems to the latest available versions
|
|
6
6
|
.SH "SYNOPSIS"
|
|
7
|
-
\fBbundle update\fR \fI*gems\fR [\-\-all] [\-\-group=NAME] [\-\-source=NAME] [\-\-local] [\-\-ruby] [\-\-bundler[=VERSION]] [\-\-force] [\-\-full\-index] [\-\-gemfile=GEMFILE] [\-\-jobs=NUMBER] [\-\-quiet] [\-\-patch|\-\-minor|\-\-major] [\-\-pre] [\-\-strict] [\-\-conservative]
|
|
7
|
+
\fBbundle update\fR \fI*gems\fR [\-\-all] [\-\-group=NAME] [\-\-source=NAME] [\-\-local] [\-\-ruby] [\-\-bundler[=VERSION]] [\-\-cooldown=NUMBER] [\-\-force] [\-\-full\-index] [\-\-gemfile=GEMFILE] [\-\-jobs=NUMBER] [\-\-quiet] [\-\-patch|\-\-minor|\-\-major] [\-\-pre] [\-\-strict] [\-\-conservative]
|
|
8
8
|
.SH "DESCRIPTION"
|
|
9
9
|
Update the gems specified (all gems, if \fB\-\-all\fR flag is used), ignoring the previously installed gems specified in the \fBGemfile\.lock\fR\. In general, you should use bundle install(1) \fIbundle\-install\.1\.html\fR to install the same exact gems and versions across machines\.
|
|
10
10
|
.P
|
|
@@ -64,6 +64,9 @@ Do not allow any gem to be updated past latest \fB\-\-patch\fR | \fB\-\-minor\fR
|
|
|
64
64
|
.TP
|
|
65
65
|
\fB\-\-conservative\fR
|
|
66
66
|
Use bundle install conservative update behavior and do not allow indirect dependencies to be updated\.
|
|
67
|
+
.TP
|
|
68
|
+
\fB\-\-cooldown=<number>\fR
|
|
69
|
+
Only consider gem versions published at least \fInumber\fR days ago when resolving\. Pass \fB0\fR to disable cooldown for this run, overriding any per\-source or global configuration\. Combine with \fB\-\-conservative\fR to minimize transitive churn when bypassing cooldown for an urgent update\. See \fBcooldown\fR in bundle\-config(1)\.
|
|
67
70
|
.SH "UPDATING ALL GEMS"
|
|
68
71
|
If you run \fBbundle update \-\-all\fR, bundler will ignore any previously installed gems and resolve all dependencies again based on the latest versions of all gems available in the sources\.
|
|
69
72
|
.P
|
|
@@ -9,6 +9,7 @@ bundle-update(1) -- Update your gems to the latest available versions
|
|
|
9
9
|
[--local]
|
|
10
10
|
[--ruby]
|
|
11
11
|
[--bundler[=VERSION]]
|
|
12
|
+
[--cooldown=NUMBER]
|
|
12
13
|
[--force]
|
|
13
14
|
[--full-index]
|
|
14
15
|
[--gemfile=GEMFILE]
|
|
@@ -91,6 +92,13 @@ gem.
|
|
|
91
92
|
* `--conservative`:
|
|
92
93
|
Use bundle install conservative update behavior and do not allow indirect dependencies to be updated.
|
|
93
94
|
|
|
95
|
+
* `--cooldown=<number>`:
|
|
96
|
+
Only consider gem versions published at least <number> days ago when
|
|
97
|
+
resolving. Pass `0` to disable cooldown for this run, overriding any
|
|
98
|
+
per-source or global configuration. Combine with `--conservative` to
|
|
99
|
+
minimize transitive churn when bypassing cooldown for an urgent
|
|
100
|
+
update. See `cooldown` in bundle-config(1).
|
|
101
|
+
|
|
94
102
|
## UPDATING ALL GEMS
|
|
95
103
|
|
|
96
104
|
If you run `bundle update --all`, bundler will ignore
|
data/lib/bundler/man/bundle.1
CHANGED
data/lib/bundler/man/gemfile.5
CHANGED
|
@@ -12,7 +12,7 @@ module Bundler
|
|
|
12
12
|
|
|
13
13
|
attr_reader :name, :version, :platform
|
|
14
14
|
attr_writer :dependencies
|
|
15
|
-
attr_accessor :source, :remote, :locked_platform
|
|
15
|
+
attr_accessor :source, :remote, :locked_platform, :created_at
|
|
16
16
|
|
|
17
17
|
def initialize(name, version, platform, spec_fetcher)
|
|
18
18
|
@name = name
|
data/lib/bundler/resolver.rb
CHANGED
|
@@ -184,6 +184,9 @@ module Bundler
|
|
|
184
184
|
|
|
185
185
|
platforms_explanation = specs_matching_other_platforms.any? ? " for any resolution platforms (#{package.platforms.join(", ")})" : ""
|
|
186
186
|
custom_explanation = "#{constraint} could not be found in #{repository_for(package)}#{platforms_explanation}"
|
|
187
|
+
if hint = cooldown_hint(specs_matching_other_platforms)
|
|
188
|
+
custom_explanation += " (#{hint})"
|
|
189
|
+
end
|
|
187
190
|
|
|
188
191
|
label = "#{name} (#{constraint_string})"
|
|
189
192
|
extended_explanation = other_specs_matching_message(specs_matching_other_platforms, label) if specs_matching_other_platforms.any?
|
|
@@ -353,6 +356,10 @@ module Bundler
|
|
|
353
356
|
message << "\n#{other_specs_matching_message(specs, matching_part)}"
|
|
354
357
|
end
|
|
355
358
|
|
|
359
|
+
if hint = cooldown_hint(specs_matching_requirement)
|
|
360
|
+
message << "\n\n#{hint}."
|
|
361
|
+
end
|
|
362
|
+
|
|
356
363
|
if specs_matching_requirement.any? && (hint = platform_mismatch_hint)
|
|
357
364
|
message << "\n\n#{hint}"
|
|
358
365
|
end
|
|
@@ -396,7 +403,7 @@ module Bundler
|
|
|
396
403
|
end
|
|
397
404
|
|
|
398
405
|
def filter_specs(specs, package)
|
|
399
|
-
filter_remote_specs(filter_prereleases(specs, package), package)
|
|
406
|
+
filter_remote_specs(filter_cooldown(filter_prereleases(specs, package)), package)
|
|
400
407
|
end
|
|
401
408
|
|
|
402
409
|
def filter_prereleases(specs, package)
|
|
@@ -405,6 +412,56 @@ module Bundler
|
|
|
405
412
|
specs.reject {|s| s.version.prerelease? }
|
|
406
413
|
end
|
|
407
414
|
|
|
415
|
+
def filter_cooldown(specs)
|
|
416
|
+
return specs if specs.empty?
|
|
417
|
+
excluded_versions = cooldown_excluded_versions(specs)
|
|
418
|
+
return specs if excluded_versions.empty?
|
|
419
|
+
specs.reject {|s| excluded_versions.include?([s.name, s.version]) }
|
|
420
|
+
end
|
|
421
|
+
|
|
422
|
+
def cooldown_excluded_versions(specs)
|
|
423
|
+
excluded = {}
|
|
424
|
+
specs.each do |spec|
|
|
425
|
+
next unless cooldown_excluded?(spec)
|
|
426
|
+
excluded[[spec.name, spec.version]] = true
|
|
427
|
+
end
|
|
428
|
+
excluded
|
|
429
|
+
end
|
|
430
|
+
|
|
431
|
+
def cooldown_hint(specs)
|
|
432
|
+
excluded_versions = cooldown_excluded_versions(specs)
|
|
433
|
+
return nil if excluded_versions.empty?
|
|
434
|
+
"#{excluded_versions.size} version#{"s" if excluded_versions.size > 1} excluded by the cooldown setting; pass `--cooldown 0` to bypass"
|
|
435
|
+
end
|
|
436
|
+
|
|
437
|
+
def cooldown_excluded?(spec)
|
|
438
|
+
return false unless spec.respond_to?(:created_at) && spec.created_at
|
|
439
|
+
return false unless spec.respond_to?(:remote) && spec.remote
|
|
440
|
+
return false if pinned_by_lockfile_floor?(spec)
|
|
441
|
+
days = spec.remote.effective_cooldown
|
|
442
|
+
return false if days.nil? || days <= 0
|
|
443
|
+
(cooldown_now - spec.created_at) < (days * 86_400)
|
|
444
|
+
end
|
|
445
|
+
|
|
446
|
+
# A spec sitting exactly at a `>= locked_version` prevent-downgrade floor is
|
|
447
|
+
# the version the lockfile currently pins. `bundle update` and `bundle
|
|
448
|
+
# outdated` install that floor so resolution never moves a gem backwards.
|
|
449
|
+
# Filtering it out for cooldown would then make resolution impossible
|
|
450
|
+
# whenever the locked version is itself inside the cooldown window, which is
|
|
451
|
+
# exactly what happens to a lockfile written before cooldown was enabled.
|
|
452
|
+
# Keep it eligible; gems being explicitly updated carry an exact `=`
|
|
453
|
+
# requirement instead and stay subject to the cooldown filter.
|
|
454
|
+
def pinned_by_lockfile_floor?(spec)
|
|
455
|
+
return false unless defined?(@base) && @base
|
|
456
|
+
requirement = base_requirements[spec.name]
|
|
457
|
+
return false unless requirement && !requirement.exact?
|
|
458
|
+
requirement.requirements.any? {|op, version| op == ">=" && version == spec.version }
|
|
459
|
+
end
|
|
460
|
+
|
|
461
|
+
def cooldown_now
|
|
462
|
+
@cooldown_now ||= Time.now
|
|
463
|
+
end
|
|
464
|
+
|
|
408
465
|
def filter_remote_specs(specs, package)
|
|
409
466
|
if package.prefer_local?
|
|
410
467
|
local_specs = specs.select {|s| s.is_a?(StubSpecification) }
|
data/lib/bundler/rubygems_ext.rb
CHANGED
|
@@ -465,6 +465,28 @@ module Gem
|
|
|
465
465
|
Resolver::APISet::GemParser.prepend(UnfreezeCompactIndexParsedResponse)
|
|
466
466
|
end
|
|
467
467
|
|
|
468
|
+
# RubyGems before 4.0.13 split compact index dependency/requirement entries
|
|
469
|
+
# on every colon, which mangles metadata values that contain colons such as
|
|
470
|
+
# the `created_at` timestamps the cooldown feature relies on. Split only on
|
|
471
|
+
# the first colon so those values survive on older RubyGems.
|
|
472
|
+
#
|
|
473
|
+
# The module is defined unconditionally so it stays testable on any RubyGems,
|
|
474
|
+
# but only prepended when the host RubyGems still has the buggy behavior.
|
|
475
|
+
module SplitCompactIndexEntryOnFirstColon
|
|
476
|
+
private
|
|
477
|
+
|
|
478
|
+
def parse_dependency(string)
|
|
479
|
+
dependency = string.split(":", 2)
|
|
480
|
+
dependency[-1] = dependency[-1].split("&") if dependency.size > 1
|
|
481
|
+
dependency[0] = -dependency[0]
|
|
482
|
+
dependency
|
|
483
|
+
end
|
|
484
|
+
end
|
|
485
|
+
|
|
486
|
+
unless Gem.rubygems_version >= Gem::Version.new("4.0.13")
|
|
487
|
+
Resolver::APISet::GemParser.prepend(SplitCompactIndexEntryOnFirstColon)
|
|
488
|
+
end
|
|
489
|
+
|
|
468
490
|
if Gem.rubygems_version < Gem::Version.new("3.6.0")
|
|
469
491
|
class Package; end
|
|
470
492
|
require "rubygems/package/tar_reader"
|
data/lib/bundler/settings.rb
CHANGED
|
@@ -432,9 +432,14 @@ module Bundler
|
|
|
432
432
|
end
|
|
433
433
|
|
|
434
434
|
def capture3_args_for(cmd, dir)
|
|
435
|
-
|
|
435
|
+
# Disable automatic maintenance so a background commit-graph write in
|
|
436
|
+
# the source repo can't race the hardlinking local clone and fail with
|
|
437
|
+
# "hardlink different from source".
|
|
438
|
+
opts = ["-c", "gc.auto=0", "-c", "maintenance.auto=false"]
|
|
436
439
|
|
|
437
|
-
["git",
|
|
440
|
+
return ["git", *opts, *cmd] unless dir
|
|
441
|
+
|
|
442
|
+
["git", "-C", dir.to_s, *opts, *cmd]
|
|
438
443
|
end
|
|
439
444
|
|
|
440
445
|
def extra_clone_args
|
data/lib/bundler/source/path.rb
CHANGED
|
@@ -220,10 +220,11 @@ module Bundler
|
|
|
220
220
|
# Some gem authors put absolute paths in their gemspec
|
|
221
221
|
# and we have to save them from themselves
|
|
222
222
|
spec.files = spec.files.filter_map do |path|
|
|
223
|
-
|
|
223
|
+
pathname = Pathname.new(path)
|
|
224
|
+
next path unless pathname.absolute?
|
|
224
225
|
next if File.directory?(path)
|
|
225
226
|
begin
|
|
226
|
-
|
|
227
|
+
pathname.relative_path_from(gem_dir).to_s
|
|
227
228
|
rescue ArgumentError
|
|
228
229
|
path
|
|
229
230
|
end
|
|
@@ -4,9 +4,9 @@ module Bundler
|
|
|
4
4
|
class Source
|
|
5
5
|
class Rubygems
|
|
6
6
|
class Remote
|
|
7
|
-
attr_reader :uri, :anonymized_uri, :original_uri
|
|
7
|
+
attr_reader :uri, :anonymized_uri, :original_uri, :cooldown
|
|
8
8
|
|
|
9
|
-
def initialize(uri)
|
|
9
|
+
def initialize(uri, cooldown: nil)
|
|
10
10
|
orig_uri = uri
|
|
11
11
|
uri = Bundler.settings.mirror_for(uri)
|
|
12
12
|
@original_uri = orig_uri if orig_uri != uri
|
|
@@ -14,6 +14,16 @@ module Bundler
|
|
|
14
14
|
|
|
15
15
|
@uri = apply_auth(uri, fallback_auth).freeze
|
|
16
16
|
@anonymized_uri = remove_auth(@uri).freeze
|
|
17
|
+
@cooldown = cooldown
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Returns the cooldown days that apply to this remote, resolving the
|
|
21
|
+
# precedence CLI > config > Gemfile per-source. Returns nil if no
|
|
22
|
+
# cooldown applies.
|
|
23
|
+
def effective_cooldown
|
|
24
|
+
override = Bundler.settings[:cooldown]
|
|
25
|
+
return override if override
|
|
26
|
+
@cooldown
|
|
17
27
|
end
|
|
18
28
|
|
|
19
29
|
MAX_CACHE_SLUG_HOST_SIZE = 255 - 1 - 32 # 255 minus dot minus MD5 length
|
|
@@ -11,11 +11,12 @@ module Bundler
|
|
|
11
11
|
API_REQUEST_SIZE = 100
|
|
12
12
|
REQUIRE_MUTEX = Mutex.new
|
|
13
13
|
|
|
14
|
-
attr_accessor :remotes
|
|
14
|
+
attr_accessor :remotes, :remote_cooldowns
|
|
15
15
|
|
|
16
16
|
def initialize(options = {})
|
|
17
17
|
@options = options
|
|
18
18
|
@remotes = []
|
|
19
|
+
@remote_cooldowns = {}
|
|
19
20
|
@dependency_names = []
|
|
20
21
|
@allow_remote = false
|
|
21
22
|
@allow_cached = false
|
|
@@ -25,7 +26,8 @@ module Bundler
|
|
|
25
26
|
@gem_installers = {}
|
|
26
27
|
@gem_installers_mutex = Mutex.new
|
|
27
28
|
|
|
28
|
-
|
|
29
|
+
cooldown = options["cooldown"]
|
|
30
|
+
Array(options["remotes"]).reverse_each {|r| add_remote(r, cooldown: cooldown) }
|
|
29
31
|
|
|
30
32
|
@lockfile_remotes = @remotes if options["from_lockfile"]
|
|
31
33
|
end
|
|
@@ -148,6 +150,13 @@ module Bundler
|
|
|
148
150
|
# sources, and large_idx.merge! small_idx is way faster than
|
|
149
151
|
# small_idx.merge! large_idx.
|
|
150
152
|
index = @allow_remote ? remote_specs.dup : Index.new
|
|
153
|
+
|
|
154
|
+
# Snapshot per-version `created_at` from the remote info before installed
|
|
155
|
+
# / cached specs overwrite the EndpointSpecification objects that carry
|
|
156
|
+
# it. The cooldown filter consults `created_at` on every candidate, so
|
|
157
|
+
# local stubs need the published date back-filled to participate.
|
|
158
|
+
remote_created_at = collect_remote_created_at(index)
|
|
159
|
+
|
|
151
160
|
index.merge!(cached_specs) if @allow_cached
|
|
152
161
|
index.merge!(installed_specs) if @allow_local
|
|
153
162
|
|
|
@@ -161,6 +170,8 @@ module Bundler
|
|
|
161
170
|
end
|
|
162
171
|
end
|
|
163
172
|
|
|
173
|
+
backfill_created_at(index, remote_created_at) unless remote_created_at.empty?
|
|
174
|
+
|
|
164
175
|
index
|
|
165
176
|
end
|
|
166
177
|
end
|
|
@@ -243,9 +254,14 @@ module Bundler
|
|
|
243
254
|
cached_path
|
|
244
255
|
end
|
|
245
256
|
|
|
246
|
-
def add_remote(source)
|
|
257
|
+
def add_remote(source, cooldown: nil)
|
|
247
258
|
uri = normalize_uri(source)
|
|
248
259
|
@remotes.unshift(uri) unless @remotes.include?(uri)
|
|
260
|
+
@remote_cooldowns[uri] = cooldown if cooldown
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
def cooldown_for(uri)
|
|
264
|
+
@remote_cooldowns[uri]
|
|
249
265
|
end
|
|
250
266
|
|
|
251
267
|
def spec_names
|
|
@@ -266,7 +282,7 @@ module Bundler
|
|
|
266
282
|
|
|
267
283
|
def remote_fetchers
|
|
268
284
|
@remote_fetchers ||= remotes.to_h do |uri|
|
|
269
|
-
remote = Source::Rubygems::Remote.new(uri)
|
|
285
|
+
remote = Source::Rubygems::Remote.new(uri, cooldown: cooldown_for(uri))
|
|
270
286
|
[remote, Bundler::Fetcher.new(remote)]
|
|
271
287
|
end.freeze
|
|
272
288
|
end
|
|
@@ -314,6 +330,13 @@ module Bundler
|
|
|
314
330
|
@allow_remote && api_fetchers.any?
|
|
315
331
|
end
|
|
316
332
|
|
|
333
|
+
def clear_cache
|
|
334
|
+
@specs = nil
|
|
335
|
+
@installed_specs = nil
|
|
336
|
+
@default_specs = nil
|
|
337
|
+
@cached_specs = nil
|
|
338
|
+
end
|
|
339
|
+
|
|
317
340
|
protected
|
|
318
341
|
|
|
319
342
|
def remote_names
|
|
@@ -456,6 +479,31 @@ module Bundler
|
|
|
456
479
|
|
|
457
480
|
private
|
|
458
481
|
|
|
482
|
+
def collect_remote_created_at(index)
|
|
483
|
+
return {} unless @allow_remote
|
|
484
|
+
|
|
485
|
+
snapshot = {}
|
|
486
|
+
index.each do |spec|
|
|
487
|
+
next unless spec.respond_to?(:created_at) && spec.created_at
|
|
488
|
+
# Remember the remote that supplied the date too: when a source has
|
|
489
|
+
# several remotes with different per-URI cooldown settings we must
|
|
490
|
+
# restore the same one during backfill so `effective_cooldown` agrees.
|
|
491
|
+
snapshot[[spec.name, spec.version]] = [spec.created_at, spec.remote]
|
|
492
|
+
end
|
|
493
|
+
snapshot
|
|
494
|
+
end
|
|
495
|
+
|
|
496
|
+
def backfill_created_at(index, snapshot)
|
|
497
|
+
index.each do |spec|
|
|
498
|
+
next unless spec.respond_to?(:created_at=)
|
|
499
|
+
next if spec.created_at
|
|
500
|
+
remote_created_at, remote = snapshot[[spec.name, spec.version]]
|
|
501
|
+
next unless remote_created_at
|
|
502
|
+
spec.created_at = remote_created_at
|
|
503
|
+
spec.remote ||= remote if remote && spec.respond_to?(:remote=)
|
|
504
|
+
end
|
|
505
|
+
end
|
|
506
|
+
|
|
459
507
|
def lockfile_remotes
|
|
460
508
|
@lockfile_remotes || credless_remotes
|
|
461
509
|
end
|
|
@@ -508,7 +556,7 @@ module Bundler
|
|
|
508
556
|
|
|
509
557
|
# We are using a mutex to reaed and write from/to the hash.
|
|
510
558
|
# The reason this double synchronization was added is for performance
|
|
511
|
-
# and lock the mutex for the shortest possible amount of time. Otherwise,
|
|
559
|
+
# and to lock the mutex for the shortest possible amount of time. Otherwise,
|
|
512
560
|
# all threads are fighting over this mutex and when it gets acquired it gets locked
|
|
513
561
|
# until a threads finishes downloading a gem, leaving the other threads waiting
|
|
514
562
|
# doing nothing.
|
data/lib/bundler/source_list.rb
CHANGED
|
@@ -59,8 +59,8 @@ module Bundler
|
|
|
59
59
|
add_source_to_list Plugin.source(source).new(options), @plugin_sources
|
|
60
60
|
end
|
|
61
61
|
|
|
62
|
-
def add_global_rubygems_remote(uri)
|
|
63
|
-
global_rubygems_source.add_remote(uri)
|
|
62
|
+
def add_global_rubygems_remote(uri, cooldown: nil)
|
|
63
|
+
global_rubygems_source.add_remote(uri, cooldown: cooldown)
|
|
64
64
|
global_rubygems_source
|
|
65
65
|
end
|
|
66
66
|
|
|
@@ -136,6 +136,10 @@ module Bundler
|
|
|
136
136
|
all_sources.each(&:remote!)
|
|
137
137
|
end
|
|
138
138
|
|
|
139
|
+
def clear_cache
|
|
140
|
+
rubygems_sources.each(&:clear_cache)
|
|
141
|
+
end
|
|
142
|
+
|
|
139
143
|
private
|
|
140
144
|
|
|
141
145
|
def map_sources(replacement_sources)
|
|
@@ -165,6 +169,10 @@ module Bundler
|
|
|
165
169
|
# locked sources never include credentials so always prefer remotes from the gemfile
|
|
166
170
|
replacement_source.remotes = gemfile_source.remotes
|
|
167
171
|
|
|
172
|
+
# cooldowns are only ever declared in the Gemfile, so carry them over
|
|
173
|
+
# along with the remotes they apply to
|
|
174
|
+
replacement_source.remote_cooldowns = gemfile_source.remote_cooldowns
|
|
175
|
+
|
|
168
176
|
yield replacement_source if block_given?
|
|
169
177
|
|
|
170
178
|
replacement_source
|
|
@@ -22,6 +22,12 @@ Gem::Specification.new do |spec|
|
|
|
22
22
|
spec.metadata["changelog_uri"] = "<%= config[:changelog_uri] %>"
|
|
23
23
|
<%- end -%>
|
|
24
24
|
|
|
25
|
+
# Uncomment the line below to require MFA for gem pushes.
|
|
26
|
+
# This helps protect your gem from supply chain attacks by ensuring
|
|
27
|
+
# no one can publish a new version without multi-factor authentication.
|
|
28
|
+
# See: https://guides.rubygems.org/mfa-requirement-opt-in/
|
|
29
|
+
# spec.metadata["rubygems_mfa_required"] = "true"
|
|
30
|
+
|
|
25
31
|
# Specify which files should be added to the gem when it is released.
|
|
26
32
|
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
|
27
33
|
gemspec = File.basename(__FILE__)
|
|
@@ -48,5 +54,5 @@ Gem::Specification.new do |spec|
|
|
|
48
54
|
<%- end -%>
|
|
49
55
|
|
|
50
56
|
# For more information and examples about making a new gem, check out our
|
|
51
|
-
# guide at: https://
|
|
57
|
+
# guide at: https://guides.rubygems.org/make-your-own-gem/
|
|
52
58
|
end
|