diff --git a/src/list_signatures.rs b/src/list_signatures.rs new file mode 100644 index 0000000..096170d --- /dev/null +++ b/src/list_signatures.rs @@ -0,0 +1,75 @@ +//! List signatures stored in this repository. + +use std::collections::BTreeMap; + +use anyhow::{Context, Result}; +use git2::{Oid, Repository}; + +use super::utils; + +/// List signatures stored in this repository. +pub fn command() -> Result<()> { + let repo = utils::open_repository()?; + + for (oid, signers) in find_signers(&repo)? { + let object = repo + .find_object(oid, None) + .context("Failed to find signed object")?; + + let signed_rev = { + let opts = { + let mut opts = git2::DescribeOptions::new(); + opts.describe_all(); + opts.show_commit_oid_as_fallback(true); + opts + }; + object + .describe(&opts) + .with_context(|| format!("Failed to describe oid={oid}"))? + .format(None) + .with_context(|| format!("Failed to format description of oid={oid}"))? + }; + + println!("Signers of {signed_rev}:"); + + for (signer, revname) in &signers { + println!(" - {signer}"); + println!(" {revname}"); + } + } + + Ok(()) +} + +fn find_signers(repo: &Repository) -> Result>> { + let mut signers: BTreeMap<_, Vec<_>> = BTreeMap::new(); + + for maybe_rev in repo + .references_glob(utils::ALL_SIGNIFY_SIGNATURE_REFS) + .context("Failed to look-up all git-signify signature refs")? + { + let rev = maybe_rev.context("Failed to parse git revision")?; + let revname = rev.name().context("Invalid revision name")?; + + let Some(("", signer_and_oid)) = + revname.split_once(utils::ALL_SIGNIFY_SIGNATURE_REFS_PREFIX) + else { + continue; + }; + let Some((signer, oid)) = signer_and_oid.split_once('/') else { + continue; + }; + + let oid = Oid::from_str(oid) + .with_context(|| format!("Failed to parse git oid={oid} of signed obj"))?; + let signer = Oid::from_str(signer) + .with_context(|| format!("Failed to parse git signer with oid={oid}"))?; + + signers + .entry(oid) + .or_default() + .push((signer, revname.to_string())); + } + + Ok(signers) +} diff --git a/src/main.rs b/src/main.rs index 8ba245c..a72c908 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,5 @@ mod fingerprint; +mod list_signatures; mod pull; mod push; mod raw; @@ -60,6 +61,8 @@ enum Action { /// The name of the remote repository remote: Option>, }, + /// List signatures stored in this repository + ListSignatures, } #[derive(Subcommand)] @@ -112,5 +115,6 @@ fn main() -> Result<()> { } => verify::command(public_key, rev), Action::Push { remote } => push::command(&remote.unwrap_or(Cow::Borrowed("origin"))), Action::Pull { remote } => pull::command(&remote.unwrap_or(Cow::Borrowed("origin"))), + Action::ListSignatures => list_signatures::command(), } } diff --git a/src/utils.rs b/src/utils.rs index 7b2c1b4..10e43b4 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -511,3 +511,9 @@ pub fn craft_signature_reference(key_fingerprint: Oid, signed_object: Oid) -> St /// Git refspec describing all signify references. pub const ALL_SIGNIFY_REFS: &str = "refs/signify/*"; + +/// Git refspec describing all signify signature references. +pub const ALL_SIGNIFY_SIGNATURE_REFS: &str = "refs/signify/signatures/*"; + +/// Git refspec prefix describing all signify signature references. +pub const ALL_SIGNIFY_SIGNATURE_REFS_PREFIX: &str = "refs/signify/signatures/";