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. /// 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>>( pub fn git<S: AsRef<std::ffi::OsStr>>(
repo: &std::path::Path, repo: &std::path::Path,
args: impl IntoIterator<Item = S>, args: impl IntoIterator<Item = S>,
) -> Result<String, io::Error> { ) -> anyhow::Result<std::process::Output> {
radicle::git::run::<_, _, &str, &str>(repo, args, []) 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. /// 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::collections::HashMap;
use std::io::IsTerminal; use std::io::IsTerminal;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::process::ExitStatus;
use std::str::FromStr; use std::str::FromStr;
use std::{assert_eq, io}; use std::{assert_eq, io};
@ -124,6 +125,15 @@ pub enum Error {
UnknownObjectType { oid: git::Oid }, UnknownObjectType { oid: git::Oid },
#[error(transparent)] #[error(transparent)]
FindObjects(#[from] git::canonical::error::FindObjectsError), 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. /// Push command.
@ -888,13 +898,15 @@ fn push_ref(
args.extend([url.to_string(), refspec.to_string()]); args.extend([url.to_string(), refspec.to_string()]);
radicle::git::run::<_, _, &str, &str>(repo, args, []) let output = radicle::git::run::<_, _, &str, &str>(repo, args, [])?;
.map_err(|err| {
Error::Io(std::io::Error::other(format!( if !output.status.success() {
"failed to run `git push {url} {refspec}` in {:?}: {err}", return Err(Error::InternalPushFailed {
working.path() stderr: String::from_utf8_lossy(&output.stderr).to_string(),
))) stdout: String::from_utf8_lossy(&output.stdout).to_string(),
})?; status: output.status,
});
}
Ok(()) Ok(())
} }

View File

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