radicle: Move interpretation of output to binary

`radicle::git::run` interprets the `Output` returned by the `git`
process. However, depending on the application we have different
requirements for interpreting the output, e.g., how to handle errors.

Make `radicle::git::run` not interpret `Output`, but move this
logic to the respective binary crates (`radicle-remote-helper` and
`radicle-cli`).
This commit is contained in:
Lorenz Leutgeb 2025-09-11 10:02:51 +02:00 committed by Fintan Halpenny
parent 0a8317c35f
commit a0f6cbf5f1
3 changed files with 38 additions and 26 deletions

View File

@ -132,11 +132,23 @@ pub fn repository() -> Result<Repository, anyhow::Error> {
}
/// Execute a git command by spawning a child process.
/// Returns [`Result::Ok`] if the command *exited successfully*.
pub fn git<S: AsRef<std::ffi::OsStr>>(
repo: &std::path::Path,
args: impl IntoIterator<Item = S>,
) -> Result<String, io::Error> {
radicle::git::run::<_, _, &str, &str>(repo, args, [])
) -> anyhow::Result<std::process::Output> {
let output = radicle::git::run::<_, _, &str, &str>(repo, args, [])?;
if !output.status.success() {
anyhow::bail!(
"`git` exited with status {}, stderr and stdout follow:\n{}\n{}\n",
output.status,
String::from_utf8_lossy(&output.stderr),
String::from_utf8_lossy(&output.stdout),
)
}
Ok(output)
}
/// Configure SSH signing in the given git repo, for the given peer.

View File

@ -6,6 +6,7 @@ mod error;
use std::collections::HashMap;
use std::io::IsTerminal;
use std::path::{Path, PathBuf};
use std::process::ExitStatus;
use std::str::FromStr;
use std::{assert_eq, io};
@ -124,6 +125,15 @@ pub enum Error {
UnknownObjectType { oid: git::Oid },
#[error(transparent)]
FindObjects(#[from] git::canonical::error::FindObjectsError),
/// Errors for "internal" pushes, i.e., pushes that this process
/// initiates between the working copy and storage.
#[error("internal push failed with exit status {status}, stderr and stdout follow:\n{stderr}\n{stdout}")]
InternalPushFailed {
status: ExitStatus,
stderr: String,
stdout: String,
},
}
/// Push command.
@ -888,13 +898,15 @@ fn push_ref(
args.extend([url.to_string(), refspec.to_string()]);
radicle::git::run::<_, _, &str, &str>(repo, args, [])
.map_err(|err| {
Error::Io(std::io::Error::other(format!(
"failed to run `git push {url} {refspec}` in {:?}: {err}",
working.path()
)))
})?;
let output = radicle::git::run::<_, _, &str, &str>(repo, args, [])?;
if !output.status.success() {
return Err(Error::InternalPushFailed {
stderr: String::from_utf8_lossy(&output.stderr).to_string(),
stdout: String::from_utf8_lossy(&output.stdout).to_string(),
status: output.status,
});
}
Ok(())
}

View File

@ -761,29 +761,18 @@ pub fn run<P, S, K, V>(
repo: P,
args: impl IntoIterator<Item = S>,
envs: impl IntoIterator<Item = (K, V)>,
) -> Result<String, io::Error>
) -> io::Result<std::process::Output>
where
P: AsRef<Path>,
S: AsRef<std::ffi::OsStr>,
K: AsRef<std::ffi::OsStr>,
V: AsRef<std::ffi::OsStr>,
{
let output = Command::new("git")
Command::new("git")
.current_dir(repo)
.envs(envs)
.args(args)
.output()?;
if output.status.success() {
let out = if output.stdout.is_empty() {
&output.stderr
} else {
&output.stdout
};
return Ok(String::from_utf8_lossy(out).into());
}
Err(io::Error::other(String::from_utf8_lossy(&output.stderr)))
.output()
}
/// Functions that call to the `git` CLI instead of `git2`.
@ -804,7 +793,7 @@ pub mod process {
storage: &R,
oids: impl IntoIterator<Item = Oid>,
verbosity: Verbosity,
) -> Result<(), io::Error>
) -> io::Result<std::process::Output>
where
R: ReadRepository,
{
@ -817,8 +806,7 @@ pub mod process {
fetch.push(url::File::new(storage.path()).to_string());
fetch.extend(oids.into_iter().map(|oid| oid.to_string()));
// N.b. `.` is used since we're fetching within the working copy
run::<_, _, &str, &str>(working, fetch, [])?;
Ok(())
run::<_, _, &str, &str>(working, fetch, [])
}
}