From 8e6fd8fed4bc45a0557243df7f800c95ac3b46ff Mon Sep 17 00:00:00 2001 From: Koichi Sasada Date: Sat, 30 Nov 2024 04:13:04 +0900 Subject: [PATCH 1/5] fix control flow `do { ...; continue; } while(0)` doesn't retry the loop body. pointed out by Coverity Scan and mame. --- hash.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/hash.c b/hash.c index 182f41a288adab..41d5a1dfe61bdd 100644 --- a/hash.c +++ b/hash.c @@ -712,7 +712,7 @@ ar_force_convert_table(VALUE hash, const char *file, int line) RUBY_ASSERT(rb_gc_obj_slot_size(hash) >= sizeof(struct RHash) + sizeof(ar_table)); // prepare hash values - do { + while (1) { st_data_t keys[RHASH_AR_TABLE_MAX_SIZE]; bound = RHASH_AR_TABLE_BOUND(hash); size = RHASH_AR_TABLE_SIZE(hash); @@ -727,7 +727,9 @@ ar_force_convert_table(VALUE hash, const char *file, int line) if (UNLIKELY(!RHASH_AR_TABLE_P(hash))) return RHASH_ST_TABLE(hash); if (UNLIKELY(RHASH_AR_TABLE_BOUND(hash) != bound)) continue; if (UNLIKELY(ar_each_key(ar, bound, ar_each_key_cmp, keys, NULL, NULL))) continue; - } while (0); + + break; + } // make st st_table tab; From cd54d0f9260bebb32e2cfd19609e555aae2c2b05 Mon Sep 17 00:00:00 2001 From: Koichi Sasada Date: Mon, 29 Jun 2026 12:35:37 +0000 Subject: [PATCH 2/5] Fix flaky TestGc#test_stat_single by disabling GC test_stat_single fails intermittently: 1) Failure: TestGc#test_stat_single [test/ruby/test_gc.rb:198]: <12> expected but was <13>. The test reads the full GC.stat hash and then GC.stat(:count) separately and asserts they are equal: stat = GC.stat assert_equal stat[:count], GC.stat(:count) These are two separate reads of :count. If a GC runs between them, :count increases and the reads disagree (stat[:count] < GC.stat(:count)). A GC in that window can be triggered by an allocation on another thread; running the two reads in a loop with a background allocating thread reproduces it deterministically (54/2,000,000 mismatches; 0 after the fix). The exact source of the GC in the failing CI run is unclear (test-all runs each test process independently), so rather than chase the trigger, disable GC around the two reads (matching test_stat_heap etc.) so :count cannot change between them. CI: https://ci.rvm.jp/results/trunk@ruby-sp3/6395611 log: https://ci.rvm.jp/logfiles/brlog.trunk.20260629-050802 Co-Authored-By: Claude Opus 4.8 (1M context) --- test/ruby/test_gc.rb | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/test/ruby/test_gc.rb b/test/ruby/test_gc.rb index feffe36aae028d..91aad11da97d6d 100644 --- a/test/ruby/test_gc.rb +++ b/test/ruby/test_gc.rb @@ -194,8 +194,14 @@ def test_stat_argument def test_stat_single omit 'stress' if GC.stress - stat = GC.stat - assert_equal stat[:count], GC.stat(:count) + # GC.stat and GC.stat(:count) are two separate reads of :count. If a GC + # runs between them (e.g. triggered by an allocation on another thread), + # :count changes and the two reads disagree. Disable GC so both reads + # observe the same :count. + EnvUtil.without_gc do + stat = GC.stat + assert_equal stat[:count], GC.stat(:count) + end assert_raise(ArgumentError){ GC.stat(:invalid) } end From 9eda32ccaed33be3067d53b77f16b507e67e91a6 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Thu, 2 Jul 2026 05:40:05 +0900 Subject: [PATCH 3/5] Remove stale Windows skips in bundler specs These examples pass on Windows as is now. Materialization detects a yanked platform-specific locked spec and falls back to the generic variant the same as other platforms, `bundle exec bundle` resolves the bundle executable, and git on Windows accepts branch and tag names starting with `#`. Co-Authored-By: Claude Opus 4.8 --- spec/bundler/install/deploy_spec.rb | 2 -- spec/bundler/install/gemfile/git_spec.rb | 4 ---- spec/bundler/install/yanked_spec.rb | 2 -- 3 files changed, 8 deletions(-) diff --git a/spec/bundler/install/deploy_spec.rb b/spec/bundler/install/deploy_spec.rb index a3b4a87ecfaa8d..5bdb8f6194f518 100644 --- a/spec/bundler/install/deploy_spec.rb +++ b/spec/bundler/install/deploy_spec.rb @@ -44,8 +44,6 @@ end it "works when you bundle exec bundle" do - skip "doesn't find bundle" if Gem.win_platform? - bundle :install bundle_config "deployment true" bundle :install diff --git a/spec/bundler/install/gemfile/git_spec.rb b/spec/bundler/install/gemfile/git_spec.rb index f2138b5fda2088..ee4c91a4f2b4a6 100644 --- a/spec/bundler/install/gemfile/git_spec.rb +++ b/spec/bundler/install/gemfile/git_spec.rb @@ -427,8 +427,6 @@ context "when the branch starts with a `#`" do let(:branch) { "#149/redirect-url-fragment" } it "works" do - skip "git does not accept this" if Gem.win_platform? - update_git("foo", path: repo, branch: branch) install_gemfile <<-G @@ -481,8 +479,6 @@ context "when the tag starts with a `#`" do let(:tag) { "#149/redirect-url-fragment" } it "works" do - skip "git does not accept this" if Gem.win_platform? - update_git("foo", path: repo, tag: tag) install_gemfile <<-G diff --git a/spec/bundler/install/yanked_spec.rb b/spec/bundler/install/yanked_spec.rb index fb9316cced8a4c..02c1c360be7714 100644 --- a/spec/bundler/install/yanked_spec.rb +++ b/spec/bundler/install/yanked_spec.rb @@ -54,8 +54,6 @@ end before do - skip "Materialization on Windows is not yet strict, so the example does not detect the gem has been yanked" if Gem.win_platform? - build_repo4 do build_gem "foo", "1.0.0" build_gem "foo", "1.0.1" From ece7e736cb294cb1c6bad81810978552d58be7fd Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Wed, 1 Jul 2026 19:42:21 +0900 Subject: [PATCH 4/5] Preserve CRLF lockfile line endings on Windows write_lock decided whether to emit `\r\n` from @lockfile_contents, which is read in text mode and drops carriage returns on Windows. A `\r\n` lockfile was therefore rewritten with `\n`. Detect the existing line ending from the raw bytes on disk instead, and apply the conversion after the equality check so the comparison keeps operating on `\n` content. Co-Authored-By: Claude Opus 4.8 --- lib/bundler/definition.rb | 10 ++++++---- spec/bundler/lock/lockfile_spec.rb | 4 +--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/bundler/definition.rb b/lib/bundler/definition.rb index 0d37f89772fb3c..ba14628bb18d58 100644 --- a/lib/bundler/definition.rb +++ b/lib/bundler/definition.rb @@ -403,10 +403,6 @@ def write_lock(file, preserve_unknown_sections) contents = to_lock - # Convert to \r\n if the existing lock has them - # i.e., Windows with `git config core.autocrlf=true` - contents.gsub!(/\n/, "\r\n") if @lockfile_contents.match?("\r\n") - if @locked_bundler_version locked_major = @locked_bundler_version.segments.first current_major = bundler_version_to_lock.segments.first @@ -427,6 +423,12 @@ def write_lock(file, preserve_unknown_sections) return end + # Convert to \r\n if the existing lock has them, i.e., Windows with + # `git config core.autocrlf=true`. Detect from the bytes on disk because + # reading in text mode strips carriage returns on Windows, which would + # otherwise defeat this check and rewrite a `\r\n` lockfile with `\n`. + contents.gsub!(/\n/, "\r\n") if File.exist?(file) && File.binread(file).include?("\r\n") + begin SharedHelpers.filesystem_access(file) do |p| File.open(p, "wb") {|f| f.puts(contents) } diff --git a/spec/bundler/lock/lockfile_spec.rb b/spec/bundler/lock/lockfile_spec.rb index 84b74823d47759..419ce84b94f3a1 100644 --- a/spec/bundler/lock/lockfile_spec.rb +++ b/spec/bundler/lock/lockfile_spec.rb @@ -2336,8 +2336,6 @@ def set_lockfile_mtime_to_known_value end it "preserves Gemfile.lock \\n\\r line endings" do - skip "needs to be adapted" if Gem.win_platform? - update_repo2 do build_gem "myrack", "1.2" do |s| s.executables = "myrackup" @@ -2349,7 +2347,7 @@ def set_lockfile_mtime_to_known_value set_lockfile_mtime_to_known_value expect { bundle "update", all: true }.to change { File.mtime(bundled_app_lock) } - expect(File.read(bundled_app_lock)).to match("\r\n") + expect(File.binread(bundled_app_lock)).to match("\r\n") expect(the_bundle).to include_gems "myrack 1.2" end From e91f477e215c3baf81d2ea73e335fe198644d88d Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Thu, 2 Jul 2026 05:41:11 +0900 Subject: [PATCH 5/5] Wrap the line ending probe in filesystem_access Keeps IO errors on the raw read mapped to Bundler friendly errors like the other file operations in write_lock. Also rename the CRLF spec descriptions that spelled the line ending backwards. Co-Authored-By: Claude Opus 4.8 --- lib/bundler/definition.rb | 4 +++- spec/bundler/lock/lockfile_spec.rb | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/bundler/definition.rb b/lib/bundler/definition.rb index ba14628bb18d58..ac37929386592c 100644 --- a/lib/bundler/definition.rb +++ b/lib/bundler/definition.rb @@ -427,7 +427,9 @@ def write_lock(file, preserve_unknown_sections) # `git config core.autocrlf=true`. Detect from the bytes on disk because # reading in text mode strips carriage returns on Windows, which would # otherwise defeat this check and rewrite a `\r\n` lockfile with `\n`. - contents.gsub!(/\n/, "\r\n") if File.exist?(file) && File.binread(file).include?("\r\n") + if File.exist?(file) && SharedHelpers.filesystem_access(file, :read) {|p| File.binread(p).include?("\r\n") } + contents.gsub!(/\n/, "\r\n") + end begin SharedHelpers.filesystem_access(file) do |p| diff --git a/spec/bundler/lock/lockfile_spec.rb b/spec/bundler/lock/lockfile_spec.rb index 419ce84b94f3a1..3ee0705c652e22 100644 --- a/spec/bundler/lock/lockfile_spec.rb +++ b/spec/bundler/lock/lockfile_spec.rb @@ -2335,7 +2335,7 @@ def set_lockfile_mtime_to_known_value expect(the_bundle).to include_gems "myrack 1.2" end - it "preserves Gemfile.lock \\n\\r line endings" do + it "preserves Gemfile.lock \\r\\n line endings" do update_repo2 do build_gem "myrack", "1.2" do |s| s.executables = "myrackup" @@ -2363,7 +2363,7 @@ def set_lockfile_mtime_to_known_value end.not_to change { File.mtime(bundled_app_lock) } end - it "preserves Gemfile.lock \\n\\r line endings" do + it "preserves Gemfile.lock \\r\\n line endings" do win_lock = File.read(bundled_app_lock).gsub(/\n/, "\r\n") File.open(bundled_app_lock, "wb") {|f| f.puts(win_lock) } set_lockfile_mtime_to_known_value