Skip to content

Commit 921f321

Browse files
Merge branch 'master' into zjit-locals-in-stackmap
2 parents c2a68ea + 76ce036 commit 921f321

51 files changed

Lines changed: 2196 additions & 283 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/check_sast.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ jobs:
4545
persist-credentials: false
4646

4747
- name: Run zizmor
48-
uses: zizmorcore/zizmor-action@3dc1ecc9bcb9e94e9b2c709687979e1298497054 # v0.6.2
48+
uses: zizmorcore/zizmor-action@70fb788f84895a7701f5643d103d587e460b5c99 # v0.6.3
4949
continue-on-error: true
5050

5151
analyze:

.github/workflows/zjit-macos.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ jobs:
9898
rustup install ${{ matrix.rust_version }} --profile minimal
9999
rustup default ${{ matrix.rust_version }}
100100
101-
- uses: taiki-e/install-action@742a3317eac7bd62f91cd888b4eead5e784ba833 # v2.87.1
101+
- uses: taiki-e/install-action@1ed6d7be6168f6c9046541087ff549b6bc581fdf # v2.87.2
102102
with:
103103
tool: nextest@0.9
104104
if: ${{ matrix.test_task == 'zjit-check' }}

.github/workflows/zjit-ubuntu.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ jobs:
152152
ruby-version: '3.1'
153153
bundler: none
154154

155-
- uses: taiki-e/install-action@742a3317eac7bd62f91cd888b4eead5e784ba833 # v2.87.1
155+
- uses: taiki-e/install-action@1ed6d7be6168f6c9046541087ff549b6bc581fdf # v2.87.2
156156
with:
157157
tool: nextest@0.9
158158
if: ${{ matrix.test_task == 'zjit-check' }}

file.c

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2482,21 +2482,29 @@ rb_file_sticky_p(VALUE obj, VALUE fname)
24822482

24832483
/*
24842484
* call-seq:
2485-
* File.identical?(file_1, file_2) -> true or false
2485+
* File.identical?(object_0, object_1) -> true or false
24862486
*
2487-
* Returns <code>true</code> if the named files are identical.
2487+
* Returns whether the given objects represent filesystem entries that are identical;
2488+
* each object may be a string path or an IO object:
24882489
*
2489-
* _file_1_ and _file_2_ can be an IO object.
2490+
* # Paths.
2491+
* File.identical?('README.md', 'README.md') # => true # Same path.
2492+
* File.identical?('README.md', './README.md') # => true # Same entry.
2493+
* File.identical?('.', '.') # => true # Directory.
2494+
* File.identical?('README.md', 'LEGAL') # => false
2495+
* File.identical?('README.md', 'nosuch') # => false # Non-existent entry.
2496+
* # Links and File object.
2497+
* File.link('README.md', 'link') # Symbolic link.
2498+
* File.symlink('README.md', 'symlink') # Hard link.
2499+
* file = File.open('README.md', 'r') # File object.
2500+
* File.identical?('README.md', 'link') # => true
2501+
* File.identical?('README.md', 'symlink') # => true
2502+
* File.identical?('README.md', file) # => true
2503+
* # Clean up.
2504+
* File.unlink('link')
2505+
* File.unlink('symlink')
2506+
* file.close
24902507
*
2491-
* open("a", "w") {}
2492-
* p File.identical?("a", "a") #=> true
2493-
* p File.identical?("a", "./a") #=> true
2494-
* File.link("a", "b")
2495-
* p File.identical?("a", "b") #=> true
2496-
* File.symlink("a", "c")
2497-
* p File.identical?("a", "c") #=> true
2498-
* open("d", "w") {}
2499-
* p File.identical?("a", "d") #=> false
25002508
*/
25012509

25022510
static VALUE

lib/bundler/cli/exec.rb

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ def run
2323
bin_path.delete_suffix!(".bat") if Gem.win_platform?
2424
kernel_load(bin_path, *args)
2525
else
26-
bin_path = "./" + bin_path unless File.absolute_path?(bin_path)
26+
bin_path = explicit_path(bin_path)
2727
kernel_exec(bin_path, *args)
2828
end
2929
else
@@ -71,6 +71,20 @@ def process_title(file, args)
7171
"#{file} #{args.join(" ")}".strip
7272
end
7373

74+
def explicit_path(path)
75+
if File.absolute_path?(path) || explicit_relative_path?(path)
76+
path
77+
else
78+
".#{File::SEPARATOR}#{path}"
79+
end
80+
end
81+
82+
def explicit_relative_path?(path)
83+
[File::SEPARATOR, File::ALT_SEPARATOR].compact.any? do |separator|
84+
path.start_with?(".#{separator}", "..#{separator}")
85+
end
86+
end
87+
7488
def directly_loadable?(file)
7589
if Gem.win_platform?
7690
script_wrapper?(file)

lib/bundler/source/git/git_proxy.rb

Lines changed: 64 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,13 @@ def git_remote_fetch(args)
193193
return out if status.success?
194194

195195
if err.include?("couldn't find remote ref") || err.include?("not our ref")
196-
raise MissingGitRevisionError.new(command_with_no_credentials, path, commit || explicit_ref, credential_filtered_uri)
196+
default_branch = renamed_remote_default_branch if tracking_remote_default_branch?
197+
if default_branch
198+
out = follow_remote_default_branch(args, default_branch)
199+
return out if out
200+
end
201+
202+
raise MissingGitRevisionError.new(command_with_no_credentials, path, commit || explicit_ref || current_branch, credential_filtered_uri)
197203
else
198204
if shallow?
199205
args -= depth_args
@@ -298,6 +304,61 @@ def not_pinned?
298304
branch_option || ref.nil?
299305
end
300306

307+
def tracking_remote_default_branch?
308+
explicit_ref.nil? && commit.nil?
309+
end
310+
311+
# Returns nil on any failure, leaving HEAD untouched so the caller reports
312+
# the original fetch failure. Runs inside the retry block, so it must not
313+
# touch the caller's command locals.
314+
def follow_remote_default_branch(args, default_branch)
315+
reference = "refs/heads/#{default_branch}"
316+
command = fetch_command(args, "#{reference}:#{reference}")
317+
check_allowed(command)
318+
319+
out, err, status = capture(command, path)
320+
unless status.success?
321+
Bundler.ui.debug "Could not fetch #{reference} from #{credential_filtered_uri}: #{err}"
322+
return
323+
end
324+
325+
previous_branch = current_branch
326+
begin
327+
git "symbolic-ref", "HEAD", reference, dir: path
328+
rescue GitError => e
329+
Bundler.ui.debug "Could not repoint the cached clone at #{reference}: #{e.message}"
330+
return
331+
end
332+
@current_branch = nil
333+
Bundler.ui.warn "#{credential_filtered_uri} no longer has #{previous_branch}, " \
334+
"now following its default branch #{default_branch}"
335+
out
336+
end
337+
338+
# The cached clone's HEAD branch is gone from the remote, so the remote's
339+
# own idea of its default branch is the only thing left to follow.
340+
def renamed_remote_default_branch
341+
default_branch = remote_default_branch
342+
return if default_branch.nil? || default_branch == current_branch
343+
344+
default_branch
345+
end
346+
347+
def remote_default_branch
348+
command = ["ls-remote", "--symref", "--", configured_uri, "HEAD"]
349+
check_allowed(command)
350+
351+
out, err, status = capture(command, path)
352+
unless status.success?
353+
Bundler.ui.debug "Could not ask #{credential_filtered_uri} for its default branch: #{err}"
354+
return
355+
end
356+
357+
# A remote is free to advertise a ref name that is not valid UTF-8, and
358+
# matching that as text raises out of the GitError family.
359+
out.b[%r{^ref:\s+refs/heads/(.+?)\s+HEAD}, 1]
360+
end
361+
301362
def pinned_to_full_sha?
302363
full_sha_revision?(ref)
303364
end
@@ -465,8 +526,8 @@ def extra_clone_args
465526
args
466527
end
467528

468-
def fetch_command(args)
469-
["fetch", "--force", "--quiet", "--no-tags", *args, "--", configured_uri, refspec].compact
529+
def fetch_command(args, spec = refspec)
530+
["fetch", "--force", "--quiet", "--no-tags", *args, "--", configured_uri, spec].compact
470531
end
471532

472533
def clone_command(args)

lib/rubygems/basic_specification.rb

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -182,17 +182,6 @@ def full_require_paths
182182
end
183183
end
184184

185-
##
186-
# The path to the data directory for this gem.
187-
188-
def datadir
189-
# TODO: drop the extra ", gem_name" which is uselessly redundant
190-
File.expand_path(File.join(gems_dir, full_name, "data", name))
191-
end
192-
193-
extend Gem::Deprecate
194-
rubygems_deprecate :datadir, :none, "4.1"
195-
196185
##
197186
# Full path of the target library file.
198187
# If the file is not in this gem, return nil.

lib/rubygems/commands/cert_command.rb

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,10 @@ def initialize
4545
end
4646

4747
add_option("-A", "--key-algorithm ALGORITHM",
48-
"Select which key algorithm to use for --build") do |algorithm, options|
48+
"Select key algorithm for --build from",
49+
"RSA, DSA, EC, ML-DSA-44, ML-DSA-65,",
50+
"or ML-DSA-87. Defaults to "\
51+
"#{Gem::Security::DEFAULT_KEY_ALGORITHM}.") do |algorithm, options|
4952
options[:key_algorithm] = algorithm
5053
end
5154

@@ -100,7 +103,8 @@ def open_private_key(key_file)
100103
rescue Errno::ENOENT
101104
raise Gem::OptionParser::InvalidArgument, "#{key_file}: does not exist"
102105
rescue OpenSSL::PKey::PKeyError, ArgumentError
103-
raise Gem::OptionParser::InvalidArgument, "#{key_file}: invalid RSA, DSA, or EC key"
106+
raise Gem::OptionParser::InvalidArgument, "#{key_file}: invalid "\
107+
"RSA, DSA, EC, ML-DSA-44, ML-DSA-65, or ML-DSA-87 key"
104108
end
105109

106110
def execute

lib/rubygems/commands/push_command.rb

Lines changed: 54 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@ def initialize
4848
end
4949

5050
add_option("--attestation FILE",
51-
"Push with sigstore attestations") do |value, options|
51+
"Push with sigstore attestations",
52+
" (FILE must be a JSON sigstore bundle)") do |value, options|
5253
options[:attestations] << value
5354
end
5455

@@ -96,7 +97,7 @@ def send_gem(name)
9697
def send_push_request(name, args)
9798
# Always honor explicit --attestation option
9899
# Auto-attestation is only supported on rubygems.org with GitHub Actions (not JRuby)
99-
if options[:attestations].any? || (RUBY_ENGINE != "jruby" && attestation_supported_host? && ENV["GITHUB_ACTIONS"])
100+
if options[:attestations].any? || (RUBY_ENGINE != "jruby" && attestation_supported_host? && ENV["GITHUB_ACTIONS"] == "true")
100101
send_push_request_with_attestation(name, args)
101102
else
102103
send_push_request_without_attestation(name, args)
@@ -117,14 +118,23 @@ def send_push_request_without_attestation(name, args)
117118
def send_push_request_with_attestation(name, args)
118119
attestations = if options[:attestations].any?
119120
options[:attestations].map do |attestation|
120-
Gem.read_binary(attestation)
121+
load_attestation(attestation)
121122
end
122123
else
123-
bundle_path = attest!(name)
124+
# Only the opportunistic signing step falls back. The request below stays
125+
# outside this rescue because once the server may have seen the attested
126+
# push, a network error must not trigger an unattested retry.
124127
begin
125-
[Gem.read_binary(bundle_path)]
126-
ensure
127-
File.unlink(bundle_path) if bundle_path && File.exist?(bundle_path)
128+
[attest!(name)]
129+
rescue StandardError => e
130+
message = "Failed to create an attestation, pushing without one.\n"
131+
message += if Gem.configuration.really_verbose
132+
e.full_message
133+
else
134+
e.message
135+
end
136+
alert_warning message
137+
return send_push_request_without_attestation(name, args)
128138
end
129139
end
130140
bundles = "[" + attestations.join(",") + "]"
@@ -136,38 +146,52 @@ def send_push_request_with_attestation(name, args)
136146
], "multipart/form-data")
137147
request.add_field "Authorization", api_key
138148
end
139-
rescue StandardError => e
140-
message = "Failed to push with attestation, retrying without attestation.\n"
141-
message += if Gem.configuration.really_verbose
142-
e.full_message
143-
else
144-
e.message
149+
end
150+
151+
def load_attestation(file)
152+
data = begin
153+
Gem.read_binary(file)
154+
rescue SystemCallError, IOError, ArgumentError => e
155+
raise Gem::Exception, "Failed to read attestation #{file}: #{e.message}"
156+
end
157+
validate_attestation_json(data, file)
158+
end
159+
160+
def validate_attestation_json(data, source)
161+
require "json"
162+
163+
parsed = begin
164+
JSON.parse(data)
165+
rescue JSON::ParserError => e
166+
raise Gem::Exception, "Attestation #{source} is not valid JSON: #{e.message}"
145167
end
146-
alert_warning message
147-
send_push_request_without_attestation(name, args)
168+
raise Gem::Exception, "Attestation #{source} is not a JSON object" unless parsed.is_a?(Hash)
169+
data
148170
end
149171

150172
def attest!(name)
151173
require "open3"
152174
require "shellwords"
153175
require "tempfile"
154176

155-
tempfile = Tempfile.new([File.basename(name, ".*"), ".sigstore.json"])
156-
bundle = tempfile.path
157-
tempfile.close(false)
158-
159177
env = defined?(Bundler.unbundled_env) ? Bundler.unbundled_env : ENV.to_h
160-
# Gem.ruby is quoted if it contains whitespace, so split it into argv
161-
# elements to keep the quotes out of the spawned command.
162-
out, st = Open3.capture2e(
163-
env,
164-
*Shellwords.split(Gem.ruby), "-S", "gem", "exec", "--conservative",
165-
"sigstore-cli", "sign", name, "--bundle", bundle,
166-
unsetenv_others: true
167-
)
168-
raise Gem::Exception, "Failed to sign gem:\n\n#{out}" unless st.success?
169-
170-
bundle
178+
179+
Tempfile.create([File.basename(name, ".*"), ".sigstore.json"]) do |tempfile|
180+
tempfile.close
181+
bundle = tempfile.path
182+
183+
# Gem.ruby is quoted if it contains whitespace, so split it into argv
184+
# elements to keep the quotes out of the spawned command.
185+
out, st = Open3.capture2e(
186+
env,
187+
*Shellwords.split(Gem.ruby), "-S", "gem", "exec", "--conservative",
188+
"sigstore-cli", "sign", name, "--bundle", bundle,
189+
unsetenv_others: true
190+
)
191+
raise Gem::Exception, "Failed to sign gem:\n\n#{out}" unless st.success?
192+
193+
validate_attestation_json(Gem.read_binary(bundle), "generated by sigstore-cli")
194+
end
171195
end
172196

173197
def get_hosts_for(name)

lib/rubygems/config_file.rb

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,12 @@ class Gem::ConfigFile
191191

192192
##
193193
# Use a global cache for .gem files shared across all Ruby installations.
194-
# When enabled, gems are cached to ~/.cache/gem/gems (or XDG_CACHE_HOME/gem/gems).
194+
# When enabled, gems fetched from a remote source are cached to
195+
# ~/.cache/gem/gems (or XDG_CACHE_HOME/gem/gems). Gems installed from a
196+
# local path are not, since a cached copy is later reused without being
197+
# verified again. <tt>gem fetch</tt> still writes to the working directory,
198+
# and an unwritable cache directory falls back to the cache of the
199+
# installation.
195200

196201
attr_accessor :global_gem_cache
197202

0 commit comments

Comments
 (0)