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
2 changes: 1 addition & 1 deletion lib/bundler/compact_index_client/cache_file.rb
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ def open(write_mode = "wb", perm = @perm, &block)
# Returns false without appending when no digests since appending is too error prone to do without digests.
def append(data)
return false unless digests?
open("a") {|f| f.write data }
open("ab") {|f| f.write data }
verify && commit
end

Expand Down
2 changes: 1 addition & 1 deletion lib/rubygems/compact_index_client/cache_file.rb
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ def open(write_mode = "wb", perm = @perm, &block)
# Returns false without appending when no digests since appending is too error prone to do without digests.
def append(data)
return false unless digests?
open("a") {|f| f.write data }
open("ab") {|f| f.write data }
verify && commit
end

Expand Down
29 changes: 29 additions & 0 deletions spec/bundler/compact_index_client/cache_file_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# frozen_string_literal: true

require "bundler/compact_index_client"
require "bundler/compact_index_client/cache_file"

Comment on lines +3 to +5
RSpec.describe Bundler::CompactIndexClient::CacheFile do
let(:path) { Pathname.new(Dir.mktmpdir("localpath")).join("versions") }

def sha256(data)
{ "sha-256" => Digest::SHA256.base64digest(data) }
end

it "appends in binary mode so line endings are preserved" do
path.binwrite "created_at: 2026-06-10\n---\n"

appended = nil
described_class.copy(path) do |file|
file.digests = sha256("created_at: 2026-06-10\n---\nrake 13.0.0\n")
appended = file.append("rake 13.0.0\n")
end

expect(appended).to be_truthy
# On Windows a text-mode append rewrites the appended LF as CRLF while the
# digest is computed over the pre-write bytes, so verify passes but the file
# on disk is corrupted. Read raw bytes to catch any stray carriage return.
expect(path.binread).not_to include("\r")
expect(path.binread).to eq("created_at: 2026-06-10\n---\nrake 13.0.0\n")
end
end
17 changes: 17 additions & 0 deletions test/rubygems/test_gem_compact_index_client_cache_file.rb
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,23 @@ def test_append_with_matching_digests
assert_equal "abcdef", @path.read
end

def test_append_writes_in_binary_mode
@path.binwrite "created_at: 2026-06-10\n---\n"

appended = nil
CacheFile.copy(@path) do |file|
file.digests = sha256("created_at: 2026-06-10\n---\nrake 13.0.0\n")
appended = file.append("rake 13.0.0\n")
end

assert appended
# On Windows a text-mode append rewrites the appended LF as CRLF while the
# digest is computed over the pre-write bytes, so verify passes but the file
# on disk is corrupted. Read raw bytes to catch any stray carriage return.
refute_includes @path.binread, "\r"
assert_equal "created_at: 2026-06-10\n---\nrake 13.0.0\n", @path.binread
end

def test_append_with_mismatched_digests_keeps_original
@path.binwrite "abc"

Expand Down