diff --git a/docs/src/tutorial/run.md b/docs/src/tutorial/run.md index 6fd96f9..a564a02 100644 --- a/docs/src/tutorial/run.md +++ b/docs/src/tutorial/run.md @@ -114,6 +114,9 @@ In this way, you can detect from inside the pipeline if you are in a profile or This is useful if you want to keep the outputs of different profiles separate, for instance. +> Profiles work on directories too! If you specify a directory as a target +> of a profile, Kerblam! will move the whole directory to the new location. + ### File modification times when using profiles `make` tracks file creation times to determine if it has to re-run pipelines again. This means that if you move files around, like Kerblam! does when it applies diff --git a/src/commands/run.rs b/src/commands/run.rs index 80e6ffc..6c36c44 100644 --- a/src/commands/run.rs +++ b/src/commands/run.rs @@ -5,9 +5,9 @@ use crate::cache::{check_last_profile, delete_last_profile, get_cache}; use crate::execution::{setup_ctrlc_hook, Executor, FileMover}; use crate::options::KerblamTomlOptions; use crate::options::Pipe; +use crate::utils::update_timestamps; use anyhow::{anyhow, bail, Result}; -use filetime::{set_file_mtime, FileTime}; /// Push a bit of a string to the end of this path /// @@ -75,10 +75,16 @@ fn extract_profile_paths( .map(|file| { let f = &root_dir.join(file); log::debug!("Checking if {f:?} exists..."); - if !f.exists() { - bail!("\t- {:?} does not exists!", file) - }; - Ok(()) + match f.try_exists() { + Ok(i) => { + if i { + Ok(()) + } else { + bail!("\t - {file:?} does not exist!") + } + } + Err(e) => bail!("\t- {file:?} - {e:?}"), + } }) .filter_map(|x| x.err()) .collect(); @@ -228,7 +234,7 @@ pub fn kerblam_run_project( for mover in profile_paths { log::debug!("Touching {:?}", &mover.clone().get_from()); - set_file_mtime(&mover.get_from(), FileTime::now())?; + update_timestamps(&mover.clone().get_from())? } // We are done. We can delete the last profile. diff --git a/src/execution.rs b/src/execution.rs index d7d18a9..aa01659 100644 --- a/src/execution.rs +++ b/src/execution.rs @@ -7,12 +7,11 @@ use std::path::{Path, PathBuf}; use std::process::{Child, Command, ExitStatus, Stdio}; use crate::options::KerblamTomlOptions; +use crate::utils::update_timestamps; use anyhow::{anyhow, bail, Context, Result}; use crossbeam_channel::{bounded, Receiver}; -use filetime::{set_file_mtime, FileTime}; - // TODO: I think we can add all cleanup code to `Drop`, so that a lot of these // functions can be simplified a lot. // E.g. make a "cleanup: Option>" to the Executor and remove @@ -405,7 +404,7 @@ impl FileMover { log::debug!("Moving {:?} to {:?}", self.from, self.to); fs::rename(&self.from, &self.to)?; if update_time { - set_file_mtime(&self.to, FileTime::now())?; + update_timestamps(&self.to)?; } Ok(self.invert()) diff --git a/src/utils.rs b/src/utils.rs index 20dc36a..f00c0a9 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,4 +1,5 @@ use anyhow::{anyhow, bail, Result}; +use filetime::{set_file_mtime, FileTime}; use flate2::Compression; use std::env::current_dir; use std::ffi::{OsStr, OsString}; @@ -475,3 +476,24 @@ pub fn gunzip_file(input: impl AsRef, output: impl AsRef) -> Result< Ok(unzipped) } + +pub fn update_timestamps(path: &PathBuf) -> anyhow::Result<()> { + let mut files_touched = 0; + if path.is_file() { + files_touched += 1; + set_file_mtime(path, FileTime::now())? + } else if path.is_dir() { + for entry in walkdir::WalkDir::new(path) + .follow_links(false) + .into_iter() + .filter_map(|x| x.ok()) + { + files_touched += 1; + if entry.path().is_file() { + set_file_mtime(entry.path(), FileTime::now())?; + } + } + } + log::debug!("Re-touched {files_touched} files."); + Ok(()) +} diff --git a/tests/utils.rs b/tests/utils.rs index 875b1f3..e34d2cd 100644 --- a/tests/utils.rs +++ b/tests/utils.rs @@ -1,3 +1,4 @@ +use filetime::{set_file_mtime, FileTime}; use similar::{ChangeTag, TextDiff}; use std::env::set_var; use std::fmt::{Debug, Write};