Conversation
- Test `bundle_output`
- Fix an issue where temp files where being dropped earlier
- Revamp error cases
There was a problem hiding this comment.
Pull request overview
Implements the pluto-app utils module to provide helper functions for short hex formatting, bundling an output directory into a .tar.gz, extracting an archive, and recursively comparing directories (with accompanying unit tests), as requested in #245.
Changes:
- Added
crates/app/src/utils.rswithhex_7,bundle_output,extract_archive, andcompare_directoriesplus tests. - Exposed the new module via
crates/app/src/lib.rs. - Added
tar,flate2,tempfile, andtest-casedependencies (workspace + crate).
Reviewed changes
Copilot reviewed 4 out of 5 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| crates/app/src/utils.rs | New utilities for tar/gzip bundling/extraction and directory comparison; includes tests. |
| crates/app/src/lib.rs | Exposes the new utils module from the crate root. |
| crates/app/Cargo.toml | Adds dependencies required by utils and its tests. |
| Cargo.toml | Adds workspace dependency versions for tar and flate2. |
| Cargo.lock | Locks new dependency graph for tar/flate2 and transitive updates. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // Delete all files from the `target_dir` | ||
| fs::remove_dir_all(&target_path)?; | ||
| fs::create_dir_all(&target_path)?; | ||
|
|
||
| // Move the created tarball to the target location | ||
| let output_path = path::Path::new(target_path.as_ref()).join(filename.as_ref()); | ||
| fs::rename(tar_file_path, output_path)?; |
There was a problem hiding this comment.
bundle_output deletes target_path (removing the original output) before it knows the archive can be moved into place. If rename fails (e.g., due to an open handle or permissions), this will lose the original directory contents and leave only the temp archive behind. Safer flow is: fully finalize/close the archive, move it into target_path, then delete everything except the archive (or otherwise ensure the move cannot fail before deleting originals).
There was a problem hiding this comment.
The move operation should not fail since we have permissions to manipulate the target_path, and we assume that we can manipulate any temporary file. Nevertheless, this is indeed an unsafe operation.
The original implementation uses the suggested approach, but it can also end up corrupted due to partially deleted data. I think my proposed implementation is better due to its brevity but I'm not against changing it if it's considered appropriate.
There was a problem hiding this comment.
There is also the chance that fs::create_dir_all fails and we potentially lose data. I would suggest saving everything into target_path and then deleting old files manually, and not by remove_dir_all
There was a problem hiding this comment.
The challenge here is with the implementation: if we put the .tar.gz file in the target directory then we cannot use tar.append_dir_all which does a lot of heavy lifting. I don't see how operations could fail here though (if we can remove the dir then we can create it again, right?)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 4 out of 5 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| dir1: impl AsRef<path::Path>, | ||
| dir2: impl AsRef<path::Path>, | ||
| ) -> Result<()> { | ||
| let mut entries1 = fs::read_dir(dir1)?.collect::<std::result::Result<Vec<_>, _>>()?; |
There was a problem hiding this comment.
not sure if it's worth to use tokio::fs here
There was a problem hiding this comment.
It gets complicated quickly due to recursive async. Given that the extract_archive function is already sync I would leave this one also sync, and if needed we can wrap it in a tokio::spawn_blocking.
| /// Returns the first 7 (or less) hex chars of the provided bytes. | ||
| pub fn hex_7(input: &[u8]) -> String { | ||
| let as_string = hex::encode(input); | ||
| as_string.chars().take(7).collect() | ||
| } |
There was a problem hiding this comment.
The same function is used in the peerinfo it would be great to port it there.
There was a problem hiding this comment.
Agree, but I don't immediately see a way to extract the common code (does not make sense for the example to depend on the entire pluto_app crate for a single function).
Any suggestions?
| // Delete all files from the `target_dir` | ||
| fs::remove_dir_all(&target_path)?; | ||
| fs::create_dir_all(&target_path)?; | ||
|
|
||
| // Move the created tarball to the target location | ||
| let output_path = path::Path::new(target_path.as_ref()).join(filename.as_ref()); | ||
| fs::rename(tar_file_path, output_path)?; |
There was a problem hiding this comment.
There is also the chance that fs::create_dir_all fails and we potentially lose data. I would suggest saving everything into target_path and then deleting old files manually, and not by remove_dir_all
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 4 out of 5 changed files in this pull request and generated 5 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
|
@claude review this PR |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 4 out of 5 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
* feat: add quic upgrade * feat: add comment about peer addresses * fix: linter * fix: clean up tests * fix: linter * fix: review comments * fix: review comments
* feat: add quic upgrade * feat: add comment about peer addresses * fix: linter * fix: clean up tests * fix: linter * feat: add force direct connections * fix: linter * fix: review comments * fix: review comments * fix: review comments
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 13 out of 14 changed files in this pull request and generated 1 comment.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 4 out of 5 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
| if path1.is_dir() && path2.is_dir() { | ||
| compare_directories(&path1, &path2)?; | ||
| } else if path1.is_file() && path2.is_file() { | ||
| compare_file_contents(&path1, &path2)?; | ||
| } else { | ||
| return Err(UtilsError::TypeMismatch { path1, path2 }); | ||
| } |
There was a problem hiding this comment.
compare_directories uses PathBuf::is_dir() / is_file(), which follow symlinks. A symlink loop (or a symlink pointing outside the compared trees) can cause infinite recursion or unintended reads outside the directory being compared. Consider using fs::symlink_metadata and explicitly handling symlinks (e.g., treat as a distinct type mismatch, or compare link targets without following).
Closes #245
The implementation is a lot shorter since the Rust libraries handle several edge cases that are hand-rolled in the reference Go implementation.
Only two dependencies are added to handle
tarandgzfiles:flate2: https://crates.io/crates/flate2tar: https://crates.io/crates/tar