From 24565bd28860a944678086de59087385b53fa8f1 Mon Sep 17 00:00:00 2001 From: FrozenPandaz Date: Wed, 27 Mar 2024 16:51:34 -0400 Subject: [PATCH] fix(core): fix caching outputs which have symlinks --- packages/nx/src/native/cache/file_ops.rs | 90 +++++++++++++++++++++--- 1 file changed, 80 insertions(+), 10 deletions(-) diff --git a/packages/nx/src/native/cache/file_ops.rs b/packages/nx/src/native/cache/file_ops.rs index 4fe9b31f4b303..1744e22e7c6fc 100644 --- a/packages/nx/src/native/cache/file_ops.rs +++ b/packages/nx/src/native/cache/file_ops.rs @@ -1,5 +1,5 @@ -use std::fs; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; +use std::{fs, io}; use fs_extra::error::ErrorKind; @@ -13,25 +13,55 @@ pub fn remove(src: String) -> anyhow::Result<()> { #[napi] pub fn copy(src: String, dest: String) -> anyhow::Result<()> { - let copy_options = fs_extra::dir::CopyOptions::new() - .overwrite(true) - .skip_exist(false); - let dest: PathBuf = dest.into(); let dest_parent = dest.parent().unwrap_or(&dest); + let src: PathBuf = src.into(); + if !dest_parent.exists() { fs::create_dir_all(dest_parent)?; } - fs_extra::copy_items(&[src], dest_parent, ©_options).map_err(|err| match err.kind { - ErrorKind::Io(err_kind) => anyhow::Error::new(err_kind), - _ => anyhow::Error::new(err), - })?; + if src.is_dir() { + copy_dir_all(&src, dest).map_err(anyhow::Error::new)?; + } else if src.is_symlink() { + symlink(fs::read_link(src)?, dest)?; + } else { + fs::copy(src, dest)?; + } Ok(()) } +#[cfg(windows)] +fn symlink, Q: AsRef>(original: P, link: Q) -> io::Result<()> { + std::os::windows::fs::symlink_file(original, link) +} + +#[cfg(unix)] +fn symlink, Q: AsRef>(original: P, link: Q) -> io::Result<()> { + std::os::unix::fs::symlink(original, link) +} + +fn copy_dir_all(src: impl AsRef, dst: impl AsRef) -> io::Result<()> { + fs::create_dir_all(&dst)?; + for entry in fs::read_dir(src)? { + let entry = entry?; + let ty = entry.file_type()?; + if ty.is_dir() { + copy_dir_all(entry.path(), dst.as_ref().join(entry.file_name()))?; + } else if ty.is_symlink() { + symlink( + fs::read_link(entry.path())?, + dst.as_ref().join(entry.file_name()), + )?; + } else { + fs::copy(entry.path(), dst.as_ref().join(entry.file_name()))?; + } + } + Ok(()) +} + #[cfg(test)] mod test { use super::*; @@ -71,4 +101,44 @@ mod test { assert!(temp.child("new-parent/file.txt").exists()); } + + #[test] + fn should_copy_symlinks() { + let temp = TempDir::new().unwrap(); + let target = temp.child("parent").child("target.txt"); + target.touch().unwrap(); + let link = temp.child("parent").child("file.txt"); + + link.symlink_to_file(&target).unwrap(); + + let src = temp.join("parent/file.txt"); + let dest = temp.join("new-parent/file.txt"); + copy(src.to_string_lossy().into(), dest.to_string_lossy().into()).unwrap(); + + assert!(temp.child("new-parent/file.txt").exists()); + assert_eq!( + temp.child("new-parent/file.txt").read_link().unwrap(), + target.path() + ); + } + + #[test] + fn should_copy_directories_with_symlinks() { + let temp = TempDir::new().unwrap(); + let target = temp.child("parent").child("target.txt"); + target.touch().unwrap(); + let link = temp.child("parent").child("file.txt"); + + link.symlink_to_file(&target).unwrap(); + + let src = temp.join("parent"); + let dest = temp.join("new-parent"); + copy(src.to_string_lossy().into(), dest.to_string_lossy().into()).unwrap(); + + assert!(temp.child("new-parent/file.txt").exists()); + assert_eq!( + temp.child("new-parent/file.txt").read_link().unwrap(), + target.path() + ); + } }