Use Tempfile for auto-attestation bundles to avoid file conflicts and cleanup issues#9327
Use Tempfile for auto-attestation bundles to avoid file conflicts and cleanup issues#9327hsbt merged 3 commits intopush-with-attestationfrom
Conversation
Co-authored-by: hsbt <12301+hsbt@users.noreply.github.com>
Co-authored-by: hsbt <12301+hsbt@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR iterates on the push command’s auto-attestation flow by switching bundle generation to use a temporary file and ensuring cleanup occurs after the multipart request payload is constructed.
Changes:
- Update
Gem::Commands::PushCommand#attest!to create the sigstore bundle usingTempfileinstead of a deterministic path. - Ensure the auto-generated bundle file is removed after it’s read into the multipart request payload.
- Adjust the auto-attestation test to avoid reading the bundle after cleanup.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
lib/rubygems/commands/push_command.rb |
Uses Tempfile for bundle path creation and unlinks the auto-attestation bundle after reading it. |
test/rubygems/test_gem_commands_push_command.rb |
Updates the auto-attestation test to assert multipart payload contents without rereading a cleaned-up file. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| begin | ||
| [Gem.read_binary(bundle_path)] | ||
| ensure | ||
| File.unlink(bundle_path) if bundle_path && File.exist?(bundle_path) | ||
| end |
There was a problem hiding this comment.
The ensure cleanup uses File.unlink(...) without rescuing errors. If unlink fails (Windows file locking, permissions, race between exist? and unlink), the push could error out even though the request was constructed successfully. Prefer a non-raising cleanup (FileUtils.rm_f) or rescue SystemCallError around the unlink so cleanup failure doesn’t break the command.
| @@ -133,7 +134,7 @@ def test_execute_attestation_auto | |||
| assert_equal Gem::Net::HTTP::Post, @fetcher.last_request.class | |||
| content_length = @fetcher.last_request["Content-Length"].to_i | |||
| assert_equal content_length, @fetcher.last_request.body.length | |||
| assert_attestation_multipart Gem.read_binary(attestation_path) | |||
| assert_attestation_multipart attestation_content | |||
There was a problem hiding this comment.
This test exercises the auto-attestation path but no longer asserts the key new behavior: that the generated bundle file is cleaned up after request construction. Add an assertion after @cmd.execute that the file at attestation_path no longer exists to prevent regressions in the cleanup logic.
| # Create a temporary file for the bundle | ||
| basename = File.basename(name, ".*") | ||
| tempfile = Tempfile.new([basename, ".sigstore.json"]) | ||
| bundle = tempfile.path | ||
| tempfile.close(false) # Close but don't unlink - we need the file for sigstore-cli |
There was a problem hiding this comment.
attest! creates a Tempfile but only returns its path. Since Tempfile registers a finalizer to unlink the file on GC, the bundle can be deleted between returning from attest! and Gem.read_binary(bundle_path), causing intermittent ENOENT and attestation fallback. Consider managing the tempfile lifetime across signing+reading (e.g., create/hold the Tempfile in send_push_request_with_attestation and only unlink after reading, or use an approach that disables Tempfile auto-unlink and ensures manual cleanup on both success and failure).
What was the end-user or developer problem that led to this PR?
The
attest!method wrote attestation bundles to a deterministic path (#{name}.sigstore.json) next to the gem file, which could overwrite existing files and left artifacts behind after successful pushes.What is your fix for the problem, implemented in this PR?
Changed
attest!to useTempfilefor bundle creation instead of a fixed path:begin...ensureblock insend_push_request_with_attestationto unlink bundle after readingtempfile.close(false)to prevent premature deletion before sigstore-cli writes to itUser-provided attestations via
--attestationflag remain unchanged and are not cleaned up.Make sure the following tasks are checked
✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.