Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "firstaide"
version = "0.1.6"
version = "0.1.7"
authors = ["Gavin Panella <gavinpanella@gmail.com>"]
edition = "2018"
description = "Bootstrap and cache Nix environments; works with direnv."
Expand Down
2 changes: 1 addition & 1 deletion src/cmds/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ fn build(config: config::Config) -> Result<u8> {

// 5. Calculate checksums.
log::info!("Calculate file checksums.");
let checksums = spin(|| sums::Checksums::from(&config.watch_files()?))
let checksums = spin(|| sums::Checksums::from(&config.build_dir, &config.watch_files()?))
.context("could not calculate checksums")?;
let cache_file = config.cache_file(&checksums);

Expand Down
6 changes: 4 additions & 2 deletions src/cmds/hook.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ impl Command {
.write_all(&chunk("Helpers.", include_bytes!("hook/helpers.sh")))
.context("could not write helpers")?;

let sums_now = sums::Checksums::from(&config.watch_files()?)?;
let sums_now = sums::Checksums::from(&config.build_dir, &config.watch_files()?)?;
let cache_file = config.cache_file(&sums_now);
let cache_file_fallback = config.cache_file_most_recent();

Expand Down Expand Up @@ -116,8 +116,10 @@ impl Command {
{
let mut watches = Vec::with_capacity(8192); // 8kB enough?
watches.extend(b"watch_file \\\n ");
// Checksum paths are relative to the build directory (or
// absolute, in caches written by older versions).
for watch in cache.sums.into_iter() {
bash::escape_into(watch.path(), &mut watches);
bash::escape_into(&config.abspath(watch.path()), &mut watches);
watches.extend(b" \\\n ");
}
// Also watch the cache file, the build log, the build
Expand Down
8 changes: 5 additions & 3 deletions src/cmds/status.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,11 @@ impl Command {
let stdout = io::stdout();
let mut handle = stdout.lock();

let sums_now =
sums::Checksums::from(&config.watch_files().context("could not get watch files")?)
.context("could not calculate checksums")?;
let sums_now = sums::Checksums::from(
&config.build_dir,
&config.watch_files().context("could not get watch files")?,
)
.context("could not calculate checksums")?;
let cache_file = config.cache_file(&sums_now);
let cache_file_fallback = config.cache_file_most_recent();

Expand Down
2 changes: 1 addition & 1 deletion src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ impl Config {
}

/// Return an absolute path, resolved relative to `self.build_dir`.
fn abspath<T: AsRef<Path>>(&self, path: T) -> PathBuf {
pub fn abspath<T: AsRef<Path>>(&self, path: T) -> PathBuf {
Comment thread
omnibs marked this conversation as resolved.
let p = path.as_ref();
if p.is_relative() {
self.build_dir.join(p)
Expand Down
69 changes: 64 additions & 5 deletions src/sums.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

use crypto_hash::{hex_digest, Algorithm};
use serde::{Deserialize, Serialize};
use std::fs;
Expand All @@ -9,13 +8,14 @@ use std::path::{Path, PathBuf};
pub struct Checksums(Vec<Checksum>);

impl Checksums {
pub fn from<T>(filenames: &[T]) -> io::Result<Self>
pub fn from<R, T>(root: R, filenames: &[T]) -> io::Result<Self>
where
R: AsRef<Path>,
T: AsRef<Path>,
{
let mut sums = Vec::new();
for filename in filenames {
let sum = Checksum::from(filename)?;
let sum = Checksum::from(root.as_ref(), filename)?;
sums.push(sum);
}
Comment thread
omnibs marked this conversation as resolved.
Ok(Self(sums))
Expand Down Expand Up @@ -44,11 +44,15 @@ pub enum Checksum {
}

impl Checksum {
pub fn from<T>(filename: T) -> io::Result<Self>
pub fn from<T>(root: &Path, filename: T) -> io::Result<Self>
where
T: AsRef<Path>,
{
let path = filename.as_ref().to_path_buf();
// Store the path relative to `root` so that checksums – and the cache
// signature derived from them – do not depend on where the working
// tree lives. Paths outside `root` are kept as they are.
let path = filename.as_ref();
let path = path.strip_prefix(root).unwrap_or(path).to_path_buf();
match Sha1::from(&filename) {
Ok(sha1) => Ok(Checksum::Found(path, sha1)),
Err(ref err) if err.kind() == io::ErrorKind::NotFound => Ok(Checksum::NotFound(path)),
Expand Down Expand Up @@ -79,3 +83,58 @@ impl Sha1 {
pub fn equal(a: &Checksums, b: &Checksums) -> bool {
a.0.iter().eq(b.0.iter())
}

#[cfg(test)]
mod tests {
use super::*;
use std::fs;

#[test]
fn checksums_of_identical_trees_at_different_roots_are_equal() {
let dir_a = tempfile::tempdir().unwrap();
let dir_b = tempfile::tempdir().unwrap();
for dir in [dir_a.path(), dir_b.path()] {
fs::create_dir(dir.join("sub")).unwrap();
fs::write(dir.join("shell.nix"), b"{ }: 12345").unwrap();
fs::write(dir.join("sub").join("deps.nix"), b"{ }: 67890").unwrap();
}
let files_a = [
dir_a.path().join("shell.nix"),
dir_a.path().join("sub/deps.nix"),
];
let files_b = [
dir_b.path().join("shell.nix"),
dir_b.path().join("sub/deps.nix"),
];

let sums_a = Checksums::from(dir_a.path(), &files_a).unwrap();
let sums_b = Checksums::from(dir_b.path(), &files_b).unwrap();

assert!(equal(&sums_a, &sums_b));
assert_eq!(sums_a.sig(), sums_b.sig());
}

#[test]
fn checksums_of_missing_files_are_also_root_relative() {
let dir_a = tempfile::tempdir().unwrap();
let dir_b = tempfile::tempdir().unwrap();

let sums_a = Checksums::from(dir_a.path(), &[dir_a.path().join("nope.nix")]).unwrap();
let sums_b = Checksums::from(dir_b.path(), &[dir_b.path().join("nope.nix")]).unwrap();

assert!(equal(&sums_a, &sums_b));
assert_eq!(sums_a.sig(), sums_b.sig());
}

#[test]
fn checksums_keep_paths_outside_the_root_absolute() {
let root = tempfile::tempdir().unwrap();
let elsewhere = tempfile::tempdir().unwrap();
fs::write(elsewhere.path().join("other.nix"), b"{ }: 1").unwrap();
let outside = elsewhere.path().join("other.nix");

let sums = Checksums::from(root.path(), &[outside.clone()]).unwrap();

assert_eq!(sums.0[0].path(), outside.as_path());
}
}
Loading