summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHermes Agent <hermes@localhost>2026-08-12 04:01:16 +0000
committerHermes Agent <hermes@localhost>2026-08-12 04:01:16 +0000
commit196f315321643a0ce2327f750c779a7d40e4b427 (patch)
tree700f3f67afea6f0a5086ce36782747ee63c26983
parent815ec6e06e05f03022c2076f6e8573f1d2c454c9 (diff)
Avoid manifest collisions beside artifacts
-rw-r--r--src/manifest.rs18
-rw-r--r--tests/compare.rs20
2 files changed, 34 insertions, 4 deletions
diff --git a/src/manifest.rs b/src/manifest.rs
index 8e9d13c..3a34af7 100644
--- a/src/manifest.rs
+++ b/src/manifest.rs
@@ -46,14 +46,26 @@ impl ArtifactManifest {
}
pub fn write_beside(&self, artifact: impl AsRef<Path>) -> Result<PathBuf> {
- let artifact = artifact.as_ref();
- let directory = artifact.parent().unwrap_or_else(|| Path::new("."));
- let path = directory.join("artifact.manifest.toml");
+ let path = Self::path_beside(artifact)?;
let text = toml::to_string_pretty(self).context("serialize artifact manifest")?;
fs::write(&path, text).with_context(|| format!("write artifact manifest {}", path.display()))?;
Ok(path)
}
+ /// Return the unambiguous companion-manifest path for one artifact.
+ /// Keeping the artifact filename prevents legacy and native manifests from
+ /// overwriting one another when both are written to the comparison output
+ /// directory.
+ pub fn path_beside(artifact: impl AsRef<Path>) -> Result<PathBuf> {
+ let artifact = artifact.as_ref();
+ let name = artifact
+ .file_name()
+ .ok_or_else(|| anyhow::anyhow!("artifact path has no filename: {}", artifact.display()))?;
+ let mut manifest_name = name.to_os_string();
+ manifest_name.push(".manifest.toml");
+ Ok(artifact.with_file_name(manifest_name))
+ }
+
/// Collect native filesystem and archive facts from a completed build.
/// Package records come from the typed package-installation boundary because
/// the RPM database is not a portable filesystem format.
diff --git a/tests/compare.rs b/tests/compare.rs
index 2e06a39..43c91e0 100644
--- a/tests/compare.rs
+++ b/tests/compare.rs
@@ -136,11 +136,29 @@ fn reads_and_writes_a_toml_manifest_beside_an_artifact() {
let manifest = baseline();
let manifest_path = manifest.write_beside(&artifact).expect("write manifest");
- assert_eq!(manifest_path, fixture.path().join("artifact.manifest.toml"));
+ assert_eq!(manifest_path, fixture.path().join("controller.tar.manifest.toml"));
assert_eq!(ArtifactManifest::load(&manifest_path).expect("load manifest"), manifest);
}
#[test]
+fn writes_distinct_manifests_for_artifacts_in_the_same_directory() {
+ let fixture = tempdir().expect("temporary directory");
+ let legacy = fixture.path().join("legacy.tar");
+ let native = fixture.path().join("native.tar");
+ let legacy_manifest = baseline();
+ let native_manifest = ArtifactManifest::new(vec![], vec![], None, vec![], vec![])
+ .expect("valid native manifest");
+
+ let legacy_path = legacy_manifest.write_beside(&legacy).expect("write legacy manifest");
+ let native_path = native_manifest.write_beside(&native).expect("write native manifest");
+
+ assert_eq!(legacy_path, fixture.path().join("legacy.tar.manifest.toml"));
+ assert_eq!(native_path, fixture.path().join("native.tar.manifest.toml"));
+ assert_eq!(ArtifactManifest::load(&legacy_path).expect("load legacy manifest"), legacy_manifest);
+ assert_eq!(ArtifactManifest::load(&native_path).expect("load native manifest"), native_manifest);
+}
+
+#[test]
fn rejects_duplicate_semantic_keys() {
let error = ArtifactManifest::new(
vec![PackageRecord::new("controller", "1"), PackageRecord::new("controller", "2")],