crates/cli/git-ents/src/commands/redact.rs
redact.rshistorycomment on this file
| 1 | //! `git ents redact`: record that an object was redacted |
| 2 | //! (`model.redaction`), refusing any future push that would refill it |
| 3 | //! (`receive.redaction-ingest`). |
| 4 | |
| 5 | use ents_model::{Redaction, namespace}; |
| 6 | use ents_receive::{Identity, propose_entity}; |
| 7 | use gix_ref_store::RefStoreRead; |
| 8 | |
| 9 | use super::{actor, signer}; |
| 10 | use crate::error::{Error, Result}; |
| 11 | use crate::mutate::outcome_to_result; |
| 12 | use crate::root::LocalRoot; |
| 13 | |
| 14 | /// `git ents redact list`: every redaction recorded in this repository. |
| 15 | /// |
| 16 | /// # Errors |
| 17 | /// |
| 18 | /// Propagates a ref-store or object read failure. |
| 19 | pub fn list(root: &LocalRoot) -> Result<Vec<(String, Redaction)>> { |
| 20 | let mut out = Vec::new(); |
| 21 | for entry in root.refs.iter_prefix("refs/meta/redactions/")? { |
| 22 | let (name, tip) = entry?; |
| 23 | let path = name.as_bstr().to_string(); |
| 24 | let Some(id) = path.strip_prefix("refs/meta/redactions/") else { |
| 25 | continue; |
| 26 | }; |
| 27 | let tree = super::commit_tree(&root.objects, tip)?; |
| 28 | if let Ok(redaction) = facet_git_tree::deserialize::<Redaction>(&tree, &root.objects) { |
| 29 | out.push((id.to_owned(), redaction)); |
| 30 | } |
| 31 | } |
| 32 | Ok(out) |
| 33 | } |
| 34 | |
| 35 | /// Run `git ents redact add <oid> --reason ...`. |
| 36 | /// |
| 37 | /// The record lands at `refs/meta/redactions/<id>`; the gate's default |
| 38 | /// namespace-authorization arm requires admin-registered provenance for |
| 39 | /// this namespace, so a non-admin signer is refused here exactly as any |
| 40 | /// other call site would refuse it (`gate.call-sites`, |
| 41 | /// `receive.redaction-admin-only`). |
| 42 | /// |
| 43 | /// # Errors |
| 44 | /// |
| 45 | /// [`Error::InvalidArgument`] if `oid` does not parse as an object id; |
| 46 | /// otherwise see [`crate::mutate::outcome_to_result`]. |
| 47 | pub fn add( |
| 48 | root: &LocalRoot, |
| 49 | oid: &str, |
| 50 | reason: String, |
| 51 | key: Option<std::path::PathBuf>, |
| 52 | ) -> Result<()> { |
| 53 | let target: gix_hash::ObjectId = oid |
| 54 | .parse() |
| 55 | .map_err(|_source| Error::InvalidArgument(format!("not an object id: {oid}")))?; |
| 56 | let redaction = Redaction::new(target, reason); |
| 57 | let id = target.to_string(); |
| 58 | let ref_name = namespace::redaction_ref(&id)?; |
| 59 | |
| 60 | let signer = signer(root, key)?; |
| 61 | let identity = Identity { |
| 62 | actor: actor(&signer), |
| 63 | author: None, |
| 64 | sign: &|payload| signer.sign(payload), |
| 65 | }; |
| 66 | let outcome = propose_entity( |
| 67 | &root.refs, |
| 68 | &root.objects, |
| 69 | &root.events, |
| 70 | ref_name, |
| 71 | &redaction, |
| 72 | &identity, |
| 73 | &format!("Redact {id}"), |
| 74 | root.mode(), |
| 75 | )?; |
| 76 | outcome_to_result(outcome, None)?; |
| 77 | Ok(()) |
| 78 | } |