Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
9901ecf
[DOC] Update bundled gems list at 91cfd2c1efe9b8ae35f4e03cf15124
matzbot Aug 5, 2026
9ac3cce
[ruby/rubygems] Move quality checks from RSpec suite to a lint rake task
hsbt Aug 5, 2026
28dc734
[ruby/rubygems] Remove spec/quality_es_spec.rb
hsbt Aug 5, 2026
0946835
[ruby/rubygems] Move world-writable cache coverage to the RubyGems te…
hsbt Aug 5, 2026
5ede6e3
[ruby/rubygems] Retry without Range when compact index returns 416
hsbt Aug 5, 2026
653716c
[ruby/rubygems] Drop the redundant range-not-satisfiable e2e example
hsbt Aug 5, 2026
58e40b5
[ruby/rubygems] Summarize cooldown-skipped versions at the end of ins…
hsbt Aug 5, 2026
e20b1ca
[ruby/rubygems] Fix weak modifier flagged by quality_spec
hsbt Aug 5, 2026
ecc6eda
win32: a backslash before whitespace escaped the closing quote
mame Jul 16, 2026
23e969c
[DOC] Use __LINE__ in the second Module#module_eval lineno example
hsbt Aug 5, 2026
d4ce236
[DOC] Use IO::Buffer.for in the IO::Buffer#transfer example
hsbt Aug 5, 2026
2d7947d
[DOC] Use trace_object_allocations_start in the dump_all example
hsbt Aug 5, 2026
2e55425
[DOC] Add require 'date' to the SimpleDelegator example
hsbt Aug 5, 2026
d1a30a1
[DOC] Fix SyntaxError in the shareable_proc define_method example
hsbt Aug 5, 2026
d2b75b5
[ruby/rubygems] Document SPDX license handling for license= and licen…
hsbt Aug 5, 2026
f3ec056
[ruby/prism] Split up lparen token
kddnewton Aug 4, 2026
5e36630
[ruby/prism] Split up lbrace token
kddnewton Aug 5, 2026
0017dea
[ruby/prism] Split up backtick token
kddnewton Aug 5, 2026
1e7d46b
Add Ractor tests for the monitor list.
eightbitraptor Aug 4, 2026
1211355
[DOC] Revise "Access Time" section of timestamps doc- #18196
BurdetteLamar Aug 5, 2026
56acaf3
YJIT: Ensure GC ran before executing the test proc in YJIT tests. (GH…
jackorp Aug 5, 2026
70874cc
ZJIT: fix gen_new_hash to use proper shape_id in flags
luke-gruber Aug 3, 2026
f688f1c
[JIT] Only call JIT single ractor mode invalidation once per process
luke-gruber Aug 3, 2026
895084e
Revert recent PRISM changes to fix irb bundled_gems CI
XrXr Aug 5, 2026
87b5ece
[ruby/openssl] rand: fix error check of RAND_load_file()
ndossche Aug 2, 2026
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
15 changes: 12 additions & 3 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,11 +137,11 @@ They are still available on rubygems.org and can be installed with
### The following default gems are updated.

* RubyGems 4.1.0.dev
* 4.0.3 to [v4.0.4][RubyGems-v4.0.4], [v4.0.5][RubyGems-v4.0.5], [v4.0.6][RubyGems-v4.0.6], [v4.0.7][RubyGems-v4.0.7], [v4.0.8][RubyGems-v4.0.8], [v4.0.9][RubyGems-v4.0.9], [v4.0.10][RubyGems-v4.0.10], [v4.0.11][RubyGems-v4.0.11], [v4.0.12][RubyGems-v4.0.12], [v4.0.13][RubyGems-v4.0.13], [v4.0.14][RubyGems-v4.0.14], [v4.0.15][RubyGems-v4.0.15], [v4.0.16][RubyGems-v4.0.16], [v4.0.17][RubyGems-v4.0.17]
* 4.0.3 to [v4.0.4][RubyGems-v4.0.4], [v4.0.5][RubyGems-v4.0.5], [v4.0.6][RubyGems-v4.0.6], [v4.0.7][RubyGems-v4.0.7], [v4.0.8][RubyGems-v4.0.8], [v4.0.9][RubyGems-v4.0.9], [v4.0.10][RubyGems-v4.0.10], [v4.0.11][RubyGems-v4.0.11], [v4.0.12][RubyGems-v4.0.12], [v4.0.13][RubyGems-v4.0.13], [v4.0.14][RubyGems-v4.0.14], [v4.0.15][RubyGems-v4.0.15], [v4.0.16][RubyGems-v4.0.16], [v4.0.17][RubyGems-v4.0.17], [v4.0.18][RubyGems-v4.0.18]
* bundler 4.1.0.dev
* 4.0.3 to [v4.0.4][bundler-v4.0.4], [v4.0.5][bundler-v4.0.5], [v4.0.6][bundler-v4.0.6], [v4.0.7][bundler-v4.0.7], [v4.0.8][bundler-v4.0.8], [v4.0.9][bundler-v4.0.9], [v4.0.10][bundler-v4.0.10], [v4.0.11][bundler-v4.0.11], [v4.0.12][bundler-v4.0.12], [v4.0.13][bundler-v4.0.13], [v4.0.14][bundler-v4.0.14], [v4.0.15][bundler-v4.0.15], [v4.0.16][bundler-v4.0.16], [v4.0.17][bundler-v4.0.17]
* erb 6.0.7
* 6.0.1 to [v6.0.1.1][erb-v6.0.1.1], [v6.0.2][erb-v6.0.2], [v6.0.3][erb-v6.0.3], [v6.0.4][erb-v6.0.4], [v6.0.5][erb-v6.0.5], [v6.0.6][erb-v6.0.6]
* 6.0.1 to [v6.0.1.1][erb-v6.0.1.1], [v6.0.2][erb-v6.0.2], [v6.0.3][erb-v6.0.3], [v6.0.4][erb-v6.0.4], [v6.0.5][erb-v6.0.5], [v6.0.6][erb-v6.0.6], [v6.0.7][erb-v6.0.7]
* error_highlight 0.7.2
* ipaddr 1.2.9
* 1.2.8 to [v1.2.9][ipaddr-v1.2.9]
Expand Down Expand Up @@ -178,7 +178,7 @@ They are still available on rubygems.org and can be installed with
* net-imap 0.6.6
* 0.6.2 to [v0.6.3][net-imap-v0.6.3], [v0.6.4][net-imap-v0.6.4], [v0.6.4.1][net-imap-v0.6.4.1], [v0.6.5][net-imap-v0.6.5], [v0.6.6][net-imap-v0.6.6]
* rbs 4.0.3
* 3.10.0 to [v3.10.1][rbs-v3.10.1], [v3.10.2][rbs-v3.10.2], [v3.10.3][rbs-v3.10.3], [v3.10.4][rbs-v3.10.4], [v4.0.0.dev.5][rbs-v4.0.0.dev.5], [v4.0.0][rbs-v4.0.0], [v4.0.2][rbs-v4.0.2], [v4.0.3][rbs-v4.0.3]
* 3.10.0 to [v3.10.1][rbs-v3.10.1], [v3.10.2][rbs-v3.10.2], [v3.10.3][rbs-v3.10.3], [v3.10.4][rbs-v3.10.4], [v4.0.0.dev.1][rbs-v4.0.0.dev.1], [v4.0.0.dev.2][rbs-v4.0.0.dev.2], [v4.0.0.dev.3][rbs-v4.0.0.dev.3], [v4.0.0.dev.4][rbs-v4.0.0.dev.4], [v4.0.0.dev.5][rbs-v4.0.0.dev.5], [v4.0.0][rbs-v4.0.0], [v4.0.1.dev.1][rbs-v4.0.1.dev.1], [v4.0.1.dev.2][rbs-v4.0.1.dev.2], [v4.0.1][rbs-v4.0.1], [v4.0.2][rbs-v4.0.2], [v4.0.3][rbs-v4.0.3]
* typeprof 0.32.0
* mutex_m 0.3.0
* bigdecimal 4.1.2
Expand Down Expand Up @@ -316,6 +316,7 @@ A lot of work has gone into making Ractors more stable, performant, and usable.
[RubyGems-v4.0.15]: https://github.com/rubygems/rubygems/releases/tag/v4.0.15
[RubyGems-v4.0.16]: https://github.com/rubygems/rubygems/releases/tag/v4.0.16
[RubyGems-v4.0.17]: https://github.com/rubygems/rubygems/releases/tag/v4.0.17
[RubyGems-v4.0.18]: https://github.com/rubygems/rubygems/releases/tag/v4.0.18
[bundler-v4.0.4]: https://github.com/rubygems/rubygems/releases/tag/bundler-v4.0.4
[bundler-v4.0.5]: https://github.com/rubygems/rubygems/releases/tag/bundler-v4.0.5
[bundler-v4.0.6]: https://github.com/rubygems/rubygems/releases/tag/bundler-v4.0.6
Expand All @@ -336,6 +337,7 @@ A lot of work has gone into making Ractors more stable, performant, and usable.
[erb-v6.0.4]: https://github.com/ruby/erb/releases/tag/v6.0.4
[erb-v6.0.5]: https://github.com/ruby/erb/releases/tag/v6.0.5
[erb-v6.0.6]: https://github.com/ruby/erb/releases/tag/v6.0.6
[erb-v6.0.7]: https://github.com/ruby/erb/releases/tag/v6.0.7
[ipaddr-v1.2.9]: https://github.com/ruby/ipaddr/releases/tag/v1.2.9
[json-v2.18.1]: https://github.com/ruby/json/releases/tag/v2.18.1
[json-v2.19.0]: https://github.com/ruby/json/releases/tag/v2.19.0
Expand Down Expand Up @@ -379,8 +381,15 @@ A lot of work has gone into making Ractors more stable, performant, and usable.
[rbs-v3.10.2]: https://github.com/ruby/rbs/releases/tag/v3.10.2
[rbs-v3.10.3]: https://github.com/ruby/rbs/releases/tag/v3.10.3
[rbs-v3.10.4]: https://github.com/ruby/rbs/releases/tag/v3.10.4
[rbs-v4.0.0.dev.1]: https://github.com/ruby/rbs/releases/tag/v4.0.0.dev.1
[rbs-v4.0.0.dev.2]: https://github.com/ruby/rbs/releases/tag/v4.0.0.dev.2
[rbs-v4.0.0.dev.3]: https://github.com/ruby/rbs/releases/tag/v4.0.0.dev.3
[rbs-v4.0.0.dev.4]: https://github.com/ruby/rbs/releases/tag/v4.0.0.dev.4
[rbs-v4.0.0.dev.5]: https://github.com/ruby/rbs/releases/tag/v4.0.0.dev.5
[rbs-v4.0.0]: https://github.com/ruby/rbs/releases/tag/v4.0.0
[rbs-v4.0.1.dev.1]: https://github.com/ruby/rbs/releases/tag/v4.0.1.dev.1
[rbs-v4.0.1.dev.2]: https://github.com/ruby/rbs/releases/tag/v4.0.1.dev.2
[rbs-v4.0.1]: https://github.com/ruby/rbs/releases/tag/v4.0.1
[rbs-v4.0.2]: https://github.com/ruby/rbs/releases/tag/v4.0.2
[rbs-v4.0.3]: https://github.com/ruby/rbs/releases/tag/v4.0.3
[bigdecimal-v4.1.0]: https://github.com/ruby/bigdecimal/releases/tag/v4.1.0
Expand Down
18 changes: 18 additions & 0 deletions bootstraptest/test_ractor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2420,6 +2420,24 @@ def initialize(a)
port.receive == :aborted
}

assert_equal 'ok', %q{
long = Ractor.new { Ractor.receive }
Ractor.new(long) { |t| t.monitor(Ractor::Port.new) }.value
GC.start
'ok'
}

assert_equal 'ok', %q{
long = Ractor.new { Ractor.receive }
20.times do
Ractor.new(long) { |t| t.monitor(Ractor::Port.new) }.value
GC.start
end
long.send(:bye)
long.join
'ok'
}

## Ractor#join

# Ractor#join returns self when the Ractor is terminated.
Expand Down
54 changes: 43 additions & 11 deletions doc/file/timestamps.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ the returned times may vary among filesystems, even on the same machine.

These timestamps methods are:

| Name | Meaning | Changes |
|:--------------------------------:|----------------------------------------|-----------------------|
| [`birthtime`](#birth-time) | Create time. | Never. |
| [`mtime`](#modification-time) | Modification time. | When written. |
| [`atime`](#access-time) | Access time. | When read or written. |
| [`ctime`](#metadata-change-time) | Metadata-change time (or create time). | See below. |
| Name | Meaning | Changes |
|:--------------------------------:|----------------------------------------|---------------|
| [`birthtime`](#birth-time) | Create time. | Never. |
| [`mtime`](#modification-time) | Modification time. | When written. |
| [`atime`](#access-time) | Access time. | When read. |
| [`ctime`](#metadata-change-time) | Metadata-change time (or create time). | See below. |

A method raises an exception if the filesystem does not support
the corresponding timestamp.
Expand Down Expand Up @@ -61,11 +61,8 @@ The modification time (along with the access time) may also be updated explicitl

## Access \Time

The access time for an entry is the time of the most recent read of or write to
the content of the entry, as reported by the underlying filesystem.

Depending on a filesystem's settings, reading an entry may cause the access time
to be updated immediately, later, or never.
The access time for an entry is the time of the most recent read for the entry,
as reported by the underlying filesystem.

Each of these methods returns the access time for an entry as a Time object:

Expand All @@ -81,6 +78,41 @@ The access time (along with the modification time) may also be updated explicitl
- Pathname#lutime.
- Pathname#utime.

Depending on a filesystem's settings, reading an entry may cause the access time
to be updated immediately, later, or never;
thus in the tables below, some entries say "Filesystem-dependent."

### File

The access time for a file is commonly the most recent time the file was read,
or if never read, the time it was created:

| Operation | Updates Access \Time |
|:------------------:|:--------------------:|
| Create | Yes |
| Read | Filesystem-dependent |
| Write | No |
| Rename | No |
| Move | No |
| Change permissions | No |
| Change ownership | No |


### Directory

The access time for a directory is commonly the most recent time its entries were read,
or if never read, the time it was created:

| Operation | Updates Access \Time |
|:------------------:|:--------------------:|
| Create | Yes |
| Read entries | Filesystem-dependent |
| Write entries | No |
| Rename | No |
| Move | No |
| Change permissions | No |
| Change ownership | No |

## Metadata-Change \Time

The metadata-change time for an entry is the time the entry last read.
Expand Down
4 changes: 1 addition & 3 deletions doc/language/ractor.md
Original file line number Diff line number Diff line change
Expand Up @@ -533,9 +533,7 @@ In order to dynamically define a method with `Module#define_method` that can be

```ruby
class A
define_method :testing, &Ractor.shareable_proc do
p self
end
define_method(:testing, Ractor.shareable_proc { p self })
end
Ractor.new do
a = A.new
Expand Down
2 changes: 1 addition & 1 deletion ext/objspace/lib/objspace.rb
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ def dump(obj, output: :string)
# If _shapes_ is +false+, no shapes are dumped.
#
# To only dump objects allocated past a certain point you can combine _since_ and _shapes_:
# ObjectSpace.trace_object_allocations
# ObjectSpace.trace_object_allocations_start
# GC.start
# gc_generation = GC.count
# shape_generation = RubyVM.stat(:next_shape_id)
Expand Down
2 changes: 1 addition & 1 deletion ext/openssl/ossl_rand.c
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ ossl_rand_add(VALUE self, VALUE str, VALUE entropy)
static VALUE
ossl_rand_load_file(VALUE self, VALUE filename)
{
if(!RAND_load_file(StringValueCStr(filename), -1)) {
if (RAND_load_file(StringValueCStr(filename), -1) < 0) {
ossl_raise(eRandomError, NULL);
}
return Qtrue;
Expand Down
13 changes: 10 additions & 3 deletions hash.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#include "internal/class.h"
#include "internal/cont.h"
#include "internal/error.h"
#include "internal/gc.h"
#include "internal/hash.h"
#include "internal/object.h"
#include "internal/proc.h"
Expand All @@ -44,6 +45,7 @@
#include "ruby/st.h"
#include "ruby/util.h"
#include "ruby_assert.h"
#include "shape.h"
#include "symbol.h"
#include "ruby/thread_native.h"
#include "ruby/ractor.h"
Expand Down Expand Up @@ -1463,9 +1465,14 @@ hash_alloc(VALUE klass)

#if USE_ZJIT
size_t
rb_zjit_hash_new_size(void)
{
return hash_slot_size(sizeof(st_table) > sizeof(ar_table));
rb_zjit_hash_new_size(VALUE *flags_out)
{
size_t size = hash_slot_size(sizeof(st_table) > sizeof(ar_table));
// mimic rb_newobj()
shape_id_t shape_id = rb_shape_transition_slot_size(ROOT_SHAPE_ID | SHAPE_ID_LAYOUT_OTHER,
rb_gc_size_slot_size(size));
*flags_out = T_HASH | ((VALUE)shape_id << SHAPE_FLAG_SHIFT);
return size;
}
#endif

Expand Down
6 changes: 3 additions & 3 deletions io_buffer.c
Original file line number Diff line number Diff line change
Expand Up @@ -1819,15 +1819,15 @@ io_buffer_slice(int argc, VALUE *argv, VALUE self)
* Transfers ownership of the underlying memory to a new buffer, causing the
* current buffer to become uninitialized.
*
* buffer = IO::Buffer.new('test')
* buffer = IO::Buffer.for('test')
* other = buffer.transfer
* other
* # =>
* # #<IO::Buffer 0x00007f136a15f7b0+4 SLICE>
* # #<IO::Buffer 0x00007f136a15f7b0+4 EXTERNAL READONLY SLICE>
* # 0x00000000 74 65 73 74 test
* buffer
* # =>
* # #<IO::Buffer 0x0000000000000000+0 NULL>
* # #<IO::Buffer 0x0000000000000000+0 NULL EXTERNAL READONLY>
* buffer.null?
* # => true
*/
Expand Down
11 changes: 11 additions & 0 deletions lib/bundler/cli/common.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,17 @@ def self.print_post_install_message(name, msg)
Bundler.ui.info msg
end

def self.output_cooldown_skipped_summary(definition = Bundler.definition)
skipped = definition.cooldown_skipped
return if skipped.empty?

Bundler.ui.info "The following gem versions were skipped by the cooldown setting:"
skipped.each do |entry|
days = entry[:available_in_days]
Bundler.ui.info " * #{entry[:name]} #{entry[:version]} (available in #{days} #{days == 1 ? "day" : "days"}), resolved #{entry[:resolved]} instead"
end
end

def self.output_fund_metadata_summary
return if Bundler.settings["ignore_funding_requests"]
definition = Bundler.definition
Expand Down
1 change: 1 addition & 0 deletions lib/bundler/cli/install.rb
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ def run
end

Bundler::CLI::Common.output_post_install_messages installer.post_install_messages
Bundler::CLI::Common.output_cooldown_skipped_summary(definition)

if CLI::Common.clean_after_install?
require_relative "clean"
Expand Down
2 changes: 2 additions & 0 deletions lib/bundler/cli/lock.rb
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ def run
puts "Writing lockfile to #{file}"
definition.write_lock(file, false)
end

Bundler::CLI::Common.output_cooldown_skipped_summary(definition)
end

Bundler.ui.output_stream = previous_output_stream
Expand Down
1 change: 1 addition & 0 deletions lib/bundler/cli/update.rb
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ def run
Bundler.ui.confirm "Bundle updated!"
Bundler::CLI::Common.output_without_groups_message(:update)
Bundler::CLI::Common.output_post_install_messages installer.post_install_messages
Bundler::CLI::Common.output_cooldown_skipped_summary

Bundler::CLI::Common.output_fund_metadata_summary
end
Expand Down
8 changes: 8 additions & 0 deletions lib/bundler/definition.rb
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,12 @@ def spec_git_paths
sources.git_sources.filter_map {|s| File.realpath(s.path) if File.exist?(s.path) }
end

# Versions excluded by cooldown during the last resolution, one entry per
# gem with the newest skipped version. Empty when no resolution ran.
def cooldown_skipped
@cooldown_skipped || []
end

def groups
dependencies.flat_map(&:groups).uniq
end
Expand Down Expand Up @@ -783,6 +789,8 @@ def start_resolution

result = SpecSet.new(resolver.start)

@cooldown_skipped = resolver.cooldown_skipped

@resolved_bundler_version = result.find {|spec| spec.name == "bundler" }&.version

@new_platforms.each do |platform|
Expand Down
55 changes: 54 additions & 1 deletion lib/bundler/resolver.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,15 @@ class Resolver
require_relative "resolver/root"
require_relative "resolver/strategy"

attr_reader :cooldown_skipped

def initialize(base, gem_version_promoter, most_specific_locked_platform = nil)
@source_requirements = base.source_requirements
@base = base
@gem_version_promoter = gem_version_promoter
@most_specific_locked_platform = most_specific_locked_platform
@cooldown_skipped = []
@cooldown_skipped_specs = {}
end

def start
Expand All @@ -36,6 +40,8 @@ def setup_solver
root = Resolver::Root.new(name_for_explicit_dependency_source)
root_version = Resolver::Candidate.new(0)

@cooldown_skipped_specs = {}

@all_specs = Hash.new do |specs, name|
source = source_for(name)
matches = source.specs.search(name)
Expand Down Expand Up @@ -83,7 +89,9 @@ def solve_versions(root:, logger:)
result = solver.solve
resolved_specs = result.flat_map {|package, version| version.to_specs(package, @most_specific_locked_platform) }
Override.attach(resolved_specs, @base.overrides)
SpecSet.new(resolved_specs).specs_with_additional_variants_from(@base.locked_specs)
spec_set = SpecSet.new(resolved_specs).specs_with_additional_variants_from(@base.locked_specs)
@cooldown_skipped = cooldown_skipped_summary(spec_set)
spec_set
rescue Gem::PubGrub::SolveFailure => e
incompatibility = e.incompatibility

Expand Down Expand Up @@ -447,6 +455,7 @@ def cooldown_excluded_versions(specs)
specs.each do |spec|
next unless cooldown_excluded?(spec)
excluded[[spec.name, spec.version]] = true
(@cooldown_skipped_specs ||= {})[[spec.name, spec.version]] ||= spec
end
excluded
end
Expand Down Expand Up @@ -489,6 +498,50 @@ def cooldown_now
@cooldown_now ||= Time.now
end

# Reports, per gem, the newest version that cooldown kept out of a
# successful resolution. A skipped version is only worth reporting when it
# is newer than the resolved version and satisfies every requirement the
# final resolution places on that gem, so we don't claim a version the
# resolver could never have picked anyway.
def cooldown_skipped_summary(spec_set)
return [] if @cooldown_skipped_specs.empty?

requirements = Hash.new {|h, name| h[name] = [] }
@requirements.each {|dep| requirements[dep.name] << dep.requirement }
spec_set.each do |spec|
spec.dependencies.each {|dep| requirements[dep.name] << dep.requirement }
end

resolved_versions = {}
spec_set.each do |spec|
version = resolved_versions[spec.name]
resolved_versions[spec.name] = spec.version if version.nil? || spec.version > version
end

newest_skipped = {}
@cooldown_skipped_specs.each do |(name, version), spec|
resolved = resolved_versions[name]
next unless resolved && version > resolved
next unless requirements[name].all? {|req| req.satisfied_by?(version) }
newest = newest_skipped[name]
newest_skipped[name] = spec if newest.nil? || version > newest.version
end

newest_skipped.values.sort_by(&:name).map do |spec|
{
name: spec.name,
version: spec.version,
resolved: resolved_versions[spec.name],
available_in_days: remaining_cooldown_days(spec),
}
end
end

def remaining_cooldown_days(spec)
remaining = (spec.remote.effective_cooldown * 86_400) - (cooldown_now - spec.created_at)
[(remaining / 86_400.0).ceil, 1].max
end

def filter_remote_specs(specs, package)
if package.prefer_local?
local_specs = specs.select {|s| s.is_a?(StubSpecification) }
Expand Down
Loading