Show effective merged mounts in config command
Replace the per-layer mount display with a single 'Config volume mounts'
section showing the de-duplicated effective mounts, each tagged with the
scope it came from (default/user/project). Add a separate 'Config sources'
section listing which config files are present.
merged_mounts() now returns Vec<(Scope, &ResolvedMount)> so callers
can see which layer each mount originated from.
diff --git a/src/config.rs b/src/config.rs
index f60956d..738f7ce 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -64,7 +64,8 @@ impl ScopedConfig {
///
/// Mounts accumulate across layers. When multiple layers define mounts with
/// the same container target path, the higher-precedence layer wins.
- pub fn merged_mounts(&self) -> Vec<&ResolvedMount> {
+ /// Each mount is tagged with the scope it came from.
+ pub fn merged_mounts(&self) -> Vec<(Scope, &ResolvedMount)> {
let mut seen = std::collections::HashSet::new();
let mut result = Vec::new();
// Iterate in reverse (highest precedence first) so higher layers win,
@@ -72,7 +73,7 @@ impl ScopedConfig {
for layer in self.layers.iter().rev() {
for mount in layer.mounts.iter().rev() {
if seen.insert(&mount.target) {
- result.push(mount);
+ result.push((layer.scope, mount));
}
}
}
@@ -449,8 +450,8 @@ mod tests {
};
let merged = config.merged_mounts();
assert_eq!(merged.len(), 2);
- assert_eq!(merged[0].target, "/a");
- assert_eq!(merged[1].target, "/b");
+ assert_eq!(merged[0].1.target, "/a");
+ assert_eq!(merged[1].1.target, "/b");
}
#[test]
@@ -468,7 +469,7 @@ mod tests {
};
let merged = config.merged_mounts();
assert_eq!(merged.len(), 1);
- assert_eq!(merged[0].target, "/a");
+ assert_eq!(merged[0].1.target, "/a");
}
#[test]
@@ -506,9 +507,9 @@ mod tests {
};
let merged = config.merged_mounts();
assert_eq!(merged.len(), 3);
- assert_eq!(merged[0].target, "/a");
- assert_eq!(merged[1].target, "/b");
- assert_eq!(merged[2].target, "/c");
+ assert_eq!(merged[0].1.target, "/a");
+ assert_eq!(merged[1].1.target, "/b");
+ assert_eq!(merged[2].1.target, "/c");
}
#[test]
@@ -547,11 +548,13 @@ mod tests {
// /root/.config/git appears in both layers; user layer wins
assert_eq!(merged.len(), 2);
// jj from default (not overridden)
- assert_eq!(merged[0].target, "/root/.config/jj");
+ assert_eq!(merged[0].0, Scope::Default);
+ assert_eq!(merged[0].1.target, "/root/.config/jj");
// git from user (overrides default)
- assert_eq!(merged[1].target, "/root/.config/git");
- assert_eq!(merged[1].source, PathBuf::from("/user/git"));
- assert!(merged[1].writable);
+ assert_eq!(merged[1].0, Scope::User);
+ assert_eq!(merged[1].1.target, "/root/.config/git");
+ assert_eq!(merged[1].1.source, PathBuf::from("/user/git"));
+ assert!(merged[1].1.writable);
}
#[test]
diff --git a/src/main.rs b/src/main.rs
index 836d997..7888726 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -195,25 +195,30 @@ impl Ramekin {
);
}
+ println!();
+ println!("Config volume mounts");
+ let merged = self.config.merged_mounts();
+ if merged.is_empty() {
+ println!(" (none)");
+ } else {
+ for (scope, m) in &merged {
+ println!(
+ " {} {} → {} ({})",
+ check(&m.source),
+ m.source.display(),
+ m.display_target(),
+ scope,
+ );
+ }
+ }
+
+ println!();
+ println!("Config sources");
for layer in &self.config.layers {
- println!();
- let label = match layer.path {
- Some(ref path) => format!("Volume mounts — {} ({})", layer.scope, path.display()),
- None => format!("Volume mounts — {}", layer.scope),
+ match layer.path {
+ Some(ref path) => println!(" {} {} {}", check(path), layer.scope, path.display()),
+ None => println!(" ✓ {}", layer.scope),
};
- println!("{label}");
- if layer.mounts.is_empty() {
- println!(" (none)");
- } else {
- for m in &layer.mounts {
- println!(
- " {} {} → {}",
- check(&m.source),
- m.source.display(),
- m.display_target()
- );
- }
- }
}
println!();
@@ -275,11 +280,14 @@ impl Ramekin {
.create_cache_directory(format!("sessions/{session_id}"))
.wrap_err("failed to create session directory")?;
- let all_mounts: Vec<&config::ResolvedMount> = self
- .builtin_mounts
- .iter()
- .chain(self.config.merged_mounts())
+ let config_mounts: Vec<_> = self
+ .config
+ .merged_mounts()
+ .into_iter()
+ .map(|(_, m)| m)
.collect();
+ let all_mounts: Vec<&config::ResolvedMount> =
+ self.builtin_mounts.iter().chain(config_mounts).collect();
let compose = generate_compose(&dockerfile, &build_context, &all_mounts);
let compose_file = session_dir.join("compose.yml");
fs_err::write(&compose_file, &compose)?;