//! public-ref snapshots taken once per generation build. use anyhow::Context as _; use knot_git::Layout; use knot_index::{Index, Resolved}; use knot_types::{ObjectFormat, OwnerDid, RepoDid, RepoName, RepoRkey}; /// everything later generation steps need about one repo, captured at snapshot time /// so a concurrent ref update yields the *next* generation, not a torn read. #[derive(Debug, Clone, PartialEq, Eq)] pub struct RepoSnapshot { pub repo_did: RepoDid, pub owner: OwnerDid, pub rkey: RepoRkey, pub name: Option, /// resolved HEAD commit, if the repo advertises one. pub head: Option, /// `advertised_refs_for(AdvertScope::Upload)` at snapshot time. pub public_refs: Vec<(String, String)>, pub object_format: ObjectFormat, } pub fn snapshot_repo( layout: &Layout, index: &Index, repo_did: &RepoDid, ) -> anyhow::Result { let ownership = match index.ownership_of(repo_did) { Resolved::Ready(Some(r)) => r, Resolved::Ready(None) => anyhow::bail!("repo {repo_did} not found in index"), Resolved::Warming => anyhow::bail!("index registry is still warming"), }; let repo = layout .open(repo_did) .with_context(|| format!("open repo {repo_did} for snapshot"))?; let head = repo.head().map(|h| h.target.to_hex()); let public_refs = repo .advertised_refs_for(knot_git::AdvertScope::Upload) .with_context(|| format!("advertised refs for {repo_did}"))? .into_iter() .map(|r| (r.name.to_string(), r.target.to_hex())) .collect(); Ok(RepoSnapshot { repo_did: repo_did.clone(), owner: ownership.owner, name: RepoName::new(ownership.rkey.as_str()).ok(), rkey: ownership.rkey, head, public_refs, object_format: repo.object_format(), }) } pub fn snapshot_all(layout: &Layout, index: &Index) -> anyhow::Result> { index .hosted_repos() .iter() .map(|did| snapshot_repo(layout, index, did)) .collect() }