Skip to content

Commit fc9647f

Browse files
committed
Fix a related issue too
1 parent a230043 commit fc9647f

3 files changed

Lines changed: 52 additions & 1 deletion

File tree

lib/zip_kit/streamer.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,11 @@ def add_empty_directory(dirname:, modification_time: Time.now.utc, unix_permissi
273273
# output (using `IO.copy_stream` is a good approach).
274274
# @return [ZipKit::Streamer::Writable] without a block - the Writable sink which has to be closed manually
275275
def write_file(filename, modification_time: Time.now.utc, unix_permissions: nil, &blk)
276+
# Reset rollback state when starting a new entry attempt, so that if this entry
277+
# fails before writing a header, rollback! won't use stale values from a previous entry
278+
@offset_before_last_local_file_header = nil
279+
@remove_last_file_at_rollback = false
280+
276281
writable = ZipKit::Streamer::Heuristic.new(self, filename, modification_time: modification_time, unix_permissions: unix_permissions)
277282
yield_or_return_writable(writable, &blk)
278283
end

rbi/zip_kit.rbi

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# typed: strong
22
module ZipKit
3-
VERSION = T.let("6.3.3", T.untyped)
3+
VERSION = T.let("6.3.4", T.untyped)
44

55
class Railtie < Rails::Railtie
66
end

spec/zip_kit/streamer_spec.rb

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -705,4 +705,50 @@ def stream_with_just_write.write(bytes)
705705
zip.close
706706
}.not_to raise_error
707707
end
708+
709+
it "handles rollback when second entry fails before header is written, preserving first entry" do
710+
# This test reproduces the bug where rollback! uses stale values from a previous
711+
# successful entry when a new entry fails before writing a header.
712+
# When write_file fails before the Heuristic calls write_deflated_file/write_stored_file,
713+
# rollback! should not affect the previous successful entry.
714+
zip_file = ManagedTempfile.new("zipp")
715+
zip = described_class.new(zip_file)
716+
717+
# First entry succeeds
718+
zip.write_file("success.txt") do |sink|
719+
sink << "successful content"
720+
end
721+
722+
# Second entry fails before header is written (exception in Heuristic before decide)
723+
expect {
724+
zip.write_file("fail.txt") do |sink|
725+
# Raise an error before any data is written, so the Heuristic hasn't decided
726+
# which compression method to use yet, and no header has been written
727+
raise "Invalid URL or other error"
728+
end
729+
}.to raise_error("Invalid URL or other error")
730+
731+
# rollback! should not affect the first entry
732+
expect {
733+
zip.rollback!
734+
}.not_to raise_error
735+
736+
# Should be able to close successfully
737+
expect {
738+
zip.close
739+
}.not_to raise_error
740+
741+
zip_file.flush
742+
743+
# Verify the final ZIP file contains only the first entry with correct content
744+
per_filename = {}
745+
Zip::File.open(zip_file.path) do |zipfile|
746+
zipfile.each do |entry|
747+
per_filename[entry.name] = entry.get_input_stream.read
748+
end
749+
end
750+
751+
expect(per_filename.size).to eq(1)
752+
expect(per_filename["success.txt"]).to eq("successful content")
753+
end
708754
end

0 commit comments

Comments
 (0)