Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 24 additions & 2 deletions bundler/lib/bundler/cli/outdated.rb
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,11 @@ def print_gem(current_spec, active_spec, dependency, groups)
end

spec_outdated_info = "#{active_spec.name} (newest #{spec_version}, " \
"installed #{current_version}#{dependency_version})"
"installed #{current_version}#{dependency_version}"

release_date = release_date_for(active_spec)
spec_outdated_info += ", released #{release_date}" unless release_date.empty?
spec_outdated_info += ")"

output_message = if options[:parseable]
spec_outdated_info.to_s
Expand All @@ -218,6 +222,7 @@ def gem_column_for(current_spec, active_spec, dependency, groups)
dependency = dependency.requirement if dependency

ret_val = [active_spec.name, current_version, spec_version, dependency.to_s, groups.to_s]
ret_val << release_date_for(active_spec)
ret_val << loaded_from_for(active_spec).to_s if Bundler.ui.debug?
ret_val
end
Expand Down Expand Up @@ -283,11 +288,28 @@ def print_indented(matrix)
end

def table_header
header = ["Gem", "Current", "Latest", "Requested", "Groups"]
header = ["Gem", "Current", "Latest", "Requested", "Groups", "Release Date"]
header << "Path" if Bundler.ui.debug?
header
end

def release_date_for(spec)
return "" unless spec.respond_to?(:date)

date = spec.date
return "" unless date

return "" unless Gem.const_defined?(:DEFAULT_SOURCE_DATE_EPOCH)
default_date = Time.at(Gem::DEFAULT_SOURCE_DATE_EPOCH).utc
Comment on lines 299 to 303
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The default-date suppression compares against Gem::DEFAULT_SOURCE_DATE_EPOCH, but Gem::Specification#date defaults to Gem.source_date_epoch (which may differ when SOURCE_DATE_EPOCH is set). In those environments, gems with no real packaged date can end up showing an arbitrary build-epoch date as the “Release Date”. Consider avoiding calling spec.date when the date wasn’t explicitly set (e.g., check the underlying ivar), or compare against Gem.source_date_epoch using the same normalization RubyGems uses.

Suggested change
date = spec.date
return "" unless date
date = date.utc if date.respond_to?(:utc)
default_date = Time.at(Gem::DEFAULT_SOURCE_DATE_EPOCH).utc
# Avoid triggering RubyGems' implicit default date based on SOURCE_DATE_EPOCH.
unless spec.instance_variable_defined?(:@date)
return ""
end
date = spec.instance_variable_get(:@date)
return "" unless date
date = date.utc if date.respond_to?(:utc)
# Suppress the default build epoch date used by RubyGems when no date is set.
default_date = Gem.source_date_epoch
default_date = default_date.utc if default_date.respond_to?(:utc)

Copilot uses AI. Check for mistakes.
default_date = Time.utc(default_date.year, default_date.month, default_date.day)

date = date.utc if date.respond_to?(:utc)

return "" if date == default_date

date.strftime("%Y-%m-%d")
end
Comment on lines 296 to 311
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

release_date_for won't actually surface release dates for the common Rubygems resolution path. When specs come from the dependency API / compact index they are typically Bundler::EndpointSpecification instances; #date on those will be the default SOURCE_DATE_EPOCH-derived value (or nil) and this method returns an empty string, so the new column will usually be blank. To make this feature work, fetch the real remote gemspec date (e.g., via EndpointSpecification's remote spec fetcher) or otherwise obtain the published date from the source, instead of relying on the local #date accessor.

Copilot uses AI. Check for mistakes.

def justify(row, sizes)
row.each_with_index.map do |element, index|
element.ljust(sizes[index])
Expand Down
24 changes: 12 additions & 12 deletions bundler/lib/bundler/man/bundle-outdated.1
Original file line number Diff line number Diff line change
Expand Up @@ -61,42 +61,42 @@ The 3 filtering options do not affect the resolution of versions, merely what ve
If the regular output shows the following:
.IP "" 4
.nf
* Gem Current Latest Requested Groups
* faker 1\.6\.5 1\.6\.6 ~> 1\.4 development, test
* hashie 1\.2\.0 3\.4\.6 = 1\.2\.0 default
* headless 2\.2\.3 2\.3\.1 = 2\.2\.3 test
* Gem Current Latest Requested Groups Release Date
* faker 1\.6\.5 1\.6\.6 ~> 1\.4 development, test 2024\-02\-05
* hashie 1\.2\.0 3\.4\.6 = 1\.2\.0 default 2023\-11\-10
* headless 2\.2\.3 2\.3\.1 = 2\.2\.3 test 2022\-08\-19
.fi
.IP "" 0
.P
\fB\-\-filter\-major\fR would only show:
.IP "" 4
.nf
* Gem Current Latest Requested Groups
* hashie 1\.2\.0 3\.4\.6 = 1\.2\.0 default
* Gem Current Latest Requested Groups Release Date
* hashie 1\.2\.0 3\.4\.6 = 1\.2\.0 default 2023\-11\-10
.fi
.IP "" 0
.P
\fB\-\-filter\-minor\fR would only show:
.IP "" 4
.nf
* Gem Current Latest Requested Groups
* headless 2\.2\.3 2\.3\.1 = 2\.2\.3 test
* Gem Current Latest Requested Groups Release Date
* headless 2\.2\.3 2\.3\.1 = 2\.2\.3 test 2022\-08\-19
.fi
.IP "" 0
.P
\fB\-\-filter\-patch\fR would only show:
.IP "" 4
.nf
* Gem Current Latest Requested Groups
* faker 1\.6\.5 1\.6\.6 ~> 1\.4 development, test
* Gem Current Latest Requested Groups Release Date
* faker 1\.6\.5 1\.6\.6 ~> 1\.4 development, test 2024\-02\-05
.fi
.IP "" 0
.P
Filter options can be combined\. \fB\-\-filter\-minor\fR and \fB\-\-filter\-patch\fR would show:
.IP "" 4
.nf
* Gem Current Latest Requested Groups
* faker 1\.6\.5 1\.6\.6 ~> 1\.4 development, test
* Gem Current Latest Requested Groups Release Date
* faker 1\.6\.5 1\.6\.6 ~> 1\.4 development, test 2024\-02\-05
.fi
.IP "" 0
.P
Expand Down
24 changes: 12 additions & 12 deletions bundler/lib/bundler/man/bundle-outdated.1.ronn
Original file line number Diff line number Diff line change
Expand Up @@ -82,29 +82,29 @@ in the output.

If the regular output shows the following:

* Gem Current Latest Requested Groups
* faker 1.6.5 1.6.6 ~> 1.4 development, test
* hashie 1.2.0 3.4.6 = 1.2.0 default
* headless 2.2.3 2.3.1 = 2.2.3 test
* Gem Current Latest Requested Groups Release Date
* faker 1.6.5 1.6.6 ~> 1.4 development, test 2024-02-05
* hashie 1.2.0 3.4.6 = 1.2.0 default 2023-11-10
* headless 2.2.3 2.3.1 = 2.2.3 test 2022-08-19
Comment on lines +85 to +88
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The manpage examples now show concrete release dates in the table output. With the current implementation, release dates are frequently unavailable (and the column will be blank) unless the code explicitly fetches/derives the remote gemspec’s packaged date. Either update the implementation to reliably populate these values or adjust the documentation/examples to reflect when the column can be populated.

Copilot uses AI. Check for mistakes.

`--filter-major` would only show:

* Gem Current Latest Requested Groups
* hashie 1.2.0 3.4.6 = 1.2.0 default
* Gem Current Latest Requested Groups Release Date
* hashie 1.2.0 3.4.6 = 1.2.0 default 2023-11-10

`--filter-minor` would only show:

* Gem Current Latest Requested Groups
* headless 2.2.3 2.3.1 = 2.2.3 test
* Gem Current Latest Requested Groups Release Date
* headless 2.2.3 2.3.1 = 2.2.3 test 2022-08-19

`--filter-patch` would only show:

* Gem Current Latest Requested Groups
* faker 1.6.5 1.6.6 ~> 1.4 development, test
* Gem Current Latest Requested Groups Release Date
* faker 1.6.5 1.6.6 ~> 1.4 development, test 2024-02-05

Filter options can be combined. `--filter-minor` and `--filter-patch` would show:

* Gem Current Latest Requested Groups
* faker 1.6.5 1.6.6 ~> 1.4 development, test
* Gem Current Latest Requested Groups Release Date
* faker 1.6.5 1.6.6 ~> 1.4 development, test 2024-02-05

Combining all three `filter` options would be the same result as providing none of them.
2 changes: 1 addition & 1 deletion bundler/spec/commands/newgem_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1764,7 +1764,7 @@ def create_temporary_dir(dir)
it "configures the crate such that `cargo test` works", :ruby_repo, :mri_only do
env = setup_rust_env
gem_path = bundled_app(gem_name)
result = sys_exec("cargo test", env: env, dir: gem_path)
result = sys_exec("cargo test", env: env, dir: gem_path, timeout: 300)

expect(result).to include("1 passed")
end
Expand Down
Loading