a generation links the files it shares with the active one
Signed-off-by: dawn <90008@klbr.net>
1311 unmodified linesExpand all1312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711311 unmodified linesExpand all drop(queued); after(); assert!(!abandoned.exists());}
#[test]fn a_generation_links_the_files_it_shares_with_the_active_one() { use std::os::unix::fs::MetadataExt as _; let env = setup_test_env(); assert!(knot_static(&env.config, ["generate"]).status.success()); let first = get_only_generation_dir(&env.output); let first_id = first.file_name().unwrap().to_str().unwrap().to_string(); assert!( knot_static(&env.config, ["activate", &first_id]) .status .success() ); std::fs::write( env.output.join("owner-handles.json"), serde_json::to_vec(&serde_json::json!({ "version": 1, "entries": { OWNER: { "handle": "ptr.pet", "verified_at_unix": 1_700_000_000u64, "profile": { "description": "shared" }, } } })) .unwrap(), ) .unwrap(); let _ = knot_static(&env.config, ["post-activate"]); let second = env .output .join("generations") .join(active_generation(&env.output)); assert_ne!(second, first);
let (mut shared, mut changed) = (0, 0); for file in walkdir(&second) { let meta = std::fs::symlink_metadata(&file).unwrap(); let theirs = first.join(file.strip_prefix(&second).unwrap()); if !meta.is_file() || !theirs.is_file() { continue; } let same = std::fs::read(&file).unwrap() == std::fs::read(&theirs).unwrap(); let linked = meta.ino() == std::fs::metadata(&theirs).unwrap().ino(); assert_eq!(same, linked, "{}", file.display()); if linked { shared += 1; } else { changed += 1; } } assert!( shared > 0 && changed > 0, "{shared} shared, {changed} changed" );}103 unmodified linesExpand all10410510610710810911011111211311411511611725 unmodified lines143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185227 unmodified linesExpand all103 unmodified linesExpand all self, destination: &Path, expected: &GenerationManifest, ) -> anyhow::Result<Publication> { if let Some(output_root) = destination.parent().and_then(Path::parent) && let Some(active) = crate::publish::current_generation(output_root) { let active = output_root.join("generations").join(active); share_unchanged(self.path(), &active).context("share unchanged files")?; } prepare_completion(self.path(), expected)?; crate::cas::seal_read_only(self.path()).context("seal generation files")?;
if destination.exists() {25 unmodified lines Ok(Publication::Published) }}
/// a file byte-identical to the active generation's copy becomes a link to it, so a write's/// generation only takes room for what it changed. both are read-only once published.fn share_unchanged(root: &Path, active: &Path) -> anyhow::Result<()> { let mut dirs = vec![root.to_path_buf()]; while let Some(dir) = dirs.pop() { for entry in std::fs::read_dir(&dir).with_context(|| format!("list {}", dir.display()))? { let entry = entry?; let kind = entry.file_type()?; if kind.is_dir() { dirs.push(entry.path()); continue; } let path = entry.path(); let relative = path.strip_prefix(root).expect("below the root"); let theirs = active.join(relative); let len = entry.metadata()?.len(); let same = kind.is_file() && std::fs::symlink_metadata(&theirs) .is_ok_and(|meta| meta.is_file() && meta.len() == len) && std::fs::read(&theirs)? == std::fs::read(&path)?; if same { let link = dir.join(format!( ".share-{}", relative.file_name().unwrap_or_default().display() )); std::fs::hard_link(&theirs, &link) .with_context(|| format!("link {}", theirs.display()))?; std::fs::rename(&link, &path) .with_context(|| format!("share {}", path.display()))?; } } } Ok(())}
pub(crate) fn prepare_completion(root: &Path, expected: &GenerationManifest) -> anyhow::Result<()> { validate_payload(root, expected)?; let completion = Completion { version: 1,227 unmodified linesExpand all