From ebdb4a00271ff5e1ec8fb1b4e252a00995adf62c Mon Sep 17 00:00:00 2001 From: Patrick Nordahl Date: Tue, 26 Nov 2024 06:16:26 -0600 Subject: [PATCH] fix symlinked directories in checkpoint update (#38) * fix symlinked directories in checkpoint update * version bump and changelog * change target reason naming clarity --- CHANGELOG.md | 6 ++++++ Cargo.lock | 2 +- Cargo.toml | 2 +- src/app/analyze.rs | 8 ++++---- src/core/file.rs | 33 ++++++++++++++++++++++++++++++--- 5 files changed, 42 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d9290d4..36bd9cc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Change Log +## [3.5.7] - 2024-11-26 + +### Fixed +- Issue preventing symlinked directories from working with `checkpoint update --pending` +- (internal) Naming of change target reason `uses_target` to `uses ` for clarity + ## [3.5.6] - 2024-11-23 ### Changed diff --git a/Cargo.lock b/Cargo.lock index b4089c8..ff1dace 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -396,7 +396,7 @@ dependencies = [ [[package]] name = "monorail" -version = "3.5.6" +version = "3.5.7" dependencies = [ "chrono", "clap", diff --git a/Cargo.toml b/Cargo.toml index 3f52052..91135d4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ description = "A tool for effective polyglot, multi-project monorepo development license = "MIT" homepage = "https://github.com/pnordahl/monorail" repository = "https://github.com/pnordahl/monorail" -version = "3.5.6" +version = "3.5.7" authors = ["Patrick Nordahl "] edition = "2021" keywords = ["monorail", "monorepo", "build", "cli", "build-tool"] diff --git a/src/app/analyze.rs b/src/app/analyze.rs index 9d31a02..812fbe0 100644 --- a/src/app/analyze.rs +++ b/src/app/analyze.rs @@ -74,8 +74,8 @@ impl PartialOrd for AnalyzedChangeTarget { pub(crate) enum AnalyzedChangeTargetReason { #[serde(rename = "target")] Target, - #[serde(rename = "uses_target")] - UsesTarget, + #[serde(rename = "uses")] + Uses, #[serde(rename = "ignores")] Ignores, } @@ -290,7 +290,7 @@ fn analyze_change<'a>( update_change_targets( &mut change_targets, &target2, - AnalyzedChangeTargetReason::UsesTarget, + AnalyzedChangeTargetReason::Uses, ); trace!(target = &target2, "Added uses target"); } else { @@ -499,7 +499,7 @@ mod tests { }, AnalyzedChangeTarget { path: target3.to_string(), - reason: AnalyzedChangeTargetReason::UsesTarget, + reason: AnalyzedChangeTargetReason::Uses, }, ]), }]; diff --git a/src/core/file.rs b/src/core/file.rs index 8df8a95..f3bfdc2 100644 --- a/src/core/file.rs +++ b/src/core/file.rs @@ -1,7 +1,7 @@ use std::collections::{HashMap, VecDeque}; use std::os::unix::fs::PermissionsExt; +use std::path; use std::result::Result; -use std::{io, path}; use sha2::Digest; use tokio::io::AsyncReadExt; @@ -56,13 +56,33 @@ pub(crate) fn contains_file(p: &path::Path) -> Result<(), MonorailError> { // TODO: configure buffer size based on file size? // TODO: pass in open file instead of path? pub(crate) async fn get_file_checksum(p: &path::Path) -> Result { + let md = match tokio::fs::metadata(p).await { + Ok(md) => md, + Err(_) => { + // non-existent/failed to stat files have an empty checksum + return Ok(String::new()); + } + }; let mut file = match tokio::fs::File::open(p).await { Ok(file) => file, - Err(e) if e.kind() == io::ErrorKind::NotFound => return Ok("".to_string()), Err(e) => { + // TODO: once io::ErrorKind::IsADirectory, use that + // empty directories have no checksum; this is required here + // because opening a normal directory would return an error + if md.is_dir() { + return Ok(String::new()); + } return Err(MonorailError::from(e)); } }; + + // check for symlink directory; this is because File::open won't fail to + // open the symlink, but attempting to read it will fail + if md.is_dir() { + return Ok(String::new()); + } + + // hash the file let mut hasher = sha2::Sha256::new(); let mut buffer = [0u8; 64 * 1024]; @@ -234,11 +254,18 @@ mod tests { // files that don't exist have an empty checksum let p = &repo_path.join("test.txt"); - assert_eq!(get_file_checksum(p).await.unwrap(), "".to_string()); + assert_eq!(get_file_checksum(p).await.unwrap(), String::new()); // write file and compare checksums let checksum = write_with_checksum(p, &[1, 2, 3]).await.unwrap(); assert_eq!(get_file_checksum(p).await.unwrap(), checksum); + + // empty directories have an empty checksum + tokio::fs::create_dir_all(repo_path.join("link")) + .await + .unwrap(); + let p = &repo_path.join("link"); + assert_eq!(get_file_checksum(p).await.unwrap(), String::new()); } #[test]