Use repository Git remotes and SSH config
Some checks failed
Dependency security audit / rustsec (push) Has been cancelled

This commit is contained in:
Georg Bauer
2026-08-26 09:32:21 +02:00
parent a3da9fda69
commit 300ccf5f1f
7 changed files with 316 additions and 23 deletions

View File

@@ -13,7 +13,11 @@ use std::{
use cap_std::{ambient_authority, fs::Dir};
use cap_tempfile::TempFile;
#[cfg(not(any(target_os = "ios", target_os = "watchos")))]
use gix::bstr::ByteSlice as _;
use serde::Deserialize;
#[cfg(not(any(target_os = "ios", target_os = "watchos")))]
use sha2::{Digest as _, Sha256};
use url::Url;
use crate::{
@@ -1694,7 +1698,14 @@ fn validate_config(
});
}
};
let git_remotes = validate_remotes(raw.git.remotes, Some(base))?;
let configured_git_remotes = raw.git.remotes;
#[cfg(not(any(target_os = "ios", target_os = "watchos")))]
let git_remotes = match repository_git_remotes(&vault)? {
Some(remotes) => remotes,
None => validate_remotes(configured_git_remotes, Some(base))?,
};
#[cfg(any(target_os = "ios", target_os = "watchos"))]
let git_remotes = validate_remotes(configured_git_remotes, Some(base))?;
Ok(Config {
source,
@@ -1715,6 +1726,75 @@ fn validate_config(
})
}
#[cfg(not(any(target_os = "ios", target_os = "watchos")))]
fn repository_git_remotes(vault: &Path) -> Result<Option<Vec<GitRemote>>, ConfigError> {
let path = vault.join(".git/config");
if !path.is_file() {
return Ok(None);
}
let config =
gix_config::File::from_path_no_includes(path, gix_config::Source::Local).map_err(|_| {
ConfigError::InvalidField {
field: "git.repository_remotes",
}
})?;
let mut remotes = Vec::new();
if let Some(sections) = config.sections_by_name("remote") {
for section in sections {
let name = section
.header()
.subsection_name()
.and_then(|name| name.to_str().ok())
.ok_or(ConfigError::InvalidField {
field: "git.repository_remotes",
})?;
let url = config
.raw_value_by("remote", Some(name.into()), "url")
.ok()
.and_then(|url| url.to_str().ok().map(str::to_owned))
.ok_or(ConfigError::InvalidRemoteUrl {
name: name.to_owned(),
})?;
let endpoint =
RemoteEndpoint::parse(&url).map_err(|_| ConfigError::InvalidRemoteUrl {
name: name.to_owned(),
})?;
let remote = match endpoint {
RemoteEndpoint::Https(ref endpoint) => GitRemote::https(
name,
&url,
stable_identifier(
"server",
format!(
"{}://{}:{}",
endpoint.scheme(),
endpoint.host_str().unwrap_or_default(),
endpoint.port_or_known_default().unwrap_or(443)
)
.as_bytes(),
),
stable_identifier("repository", url.as_bytes()),
)?,
RemoteEndpoint::Ssh(_) => GitRemote::ssh(name, &url)?,
};
remotes.push(remote);
}
}
Ok((!remotes.is_empty()).then_some(remotes))
}
#[cfg(not(any(target_os = "ios", target_os = "watchos")))]
fn stable_identifier(prefix: &str, value: &[u8]) -> String {
const HEX: &[u8; 16] = b"0123456789abcdef";
let digest = Sha256::digest(value);
let suffix: String = digest[..8]
.iter()
.flat_map(|byte| [HEX[(byte >> 4) as usize], HEX[(byte & 0x0f) as usize]])
.map(char::from)
.collect();
format!("{prefix}-{suffix}")
}
fn resolve_required_path(
base: &Path,
value: Option<PathBuf>,