20260605 ROA增量验证和rsync host失败短路
This commit is contained in:
@@ -50,6 +50,16 @@ pub trait RsyncFetcher: Send + Sync {
|
||||
fn dedup_key(&self, rsync_base_uri: &str) -> String {
|
||||
normalize_rsync_base_uri(rsync_base_uri)
|
||||
}
|
||||
|
||||
/// Return an optional failure-only deduplication key.
|
||||
///
|
||||
/// This key is only used to short-circuit repeated failed rsync fallbacks. It must
|
||||
/// not be used to reuse successful fetch results unless it is identical to
|
||||
/// `dedup_key`, because a successful fetch for one publication point does not imply
|
||||
/// that another publication point under the same host has been fetched.
|
||||
fn failure_dedup_key(&self, _rsync_base_uri: &str) -> Option<String> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// A simple "rsync" implementation backed by a local directory.
|
||||
|
||||
@@ -15,23 +15,25 @@ use crate::fetch::rsync::{
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum RsyncScopePolicy {
|
||||
Host,
|
||||
PublicationPoint,
|
||||
ModuleRoot,
|
||||
}
|
||||
|
||||
impl Default for RsyncScopePolicy {
|
||||
fn default() -> Self {
|
||||
Self::PublicationPoint
|
||||
Self::Host
|
||||
}
|
||||
}
|
||||
|
||||
impl RsyncScopePolicy {
|
||||
pub fn parse_cli_value(value: &str) -> Result<Self, String> {
|
||||
match value {
|
||||
"host" => Ok(Self::Host),
|
||||
"publication-point" => Ok(Self::PublicationPoint),
|
||||
"module-root" => Ok(Self::ModuleRoot),
|
||||
_ => Err(format!(
|
||||
"invalid --rsync-scope: {value}; expected publication-point or module-root"
|
||||
"invalid --rsync-scope: {value}; expected host, publication-point, or module-root"
|
||||
)),
|
||||
}
|
||||
}
|
||||
@@ -296,7 +298,9 @@ impl SystemRsyncFetcher {
|
||||
|
||||
fn scope_fetch_uri(&self, rsync_base_uri: &str) -> String {
|
||||
match self.config.scope_policy {
|
||||
RsyncScopePolicy::PublicationPoint => normalize_rsync_base_uri(rsync_base_uri),
|
||||
RsyncScopePolicy::Host | RsyncScopePolicy::PublicationPoint => {
|
||||
normalize_rsync_base_uri(rsync_base_uri)
|
||||
}
|
||||
RsyncScopePolicy::ModuleRoot => rsync_module_root_uri(rsync_base_uri)
|
||||
.unwrap_or_else(|| normalize_rsync_base_uri(rsync_base_uri)),
|
||||
}
|
||||
@@ -348,6 +352,21 @@ impl RsyncFetcher for SystemRsyncFetcher {
|
||||
fn dedup_key(&self, rsync_base_uri: &str) -> String {
|
||||
self.scope_fetch_uri(rsync_base_uri)
|
||||
}
|
||||
|
||||
fn failure_dedup_key(&self, rsync_base_uri: &str) -> Option<String> {
|
||||
match self.config.scope_policy {
|
||||
RsyncScopePolicy::Host => rsync_host_scope_uri(rsync_base_uri),
|
||||
RsyncScopePolicy::PublicationPoint | RsyncScopePolicy::ModuleRoot => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn rsync_host_scope_uri(rsync_base_uri: &str) -> Option<String> {
|
||||
let parsed = url::Url::parse(rsync_base_uri).ok()?;
|
||||
if parsed.scheme() != "rsync" {
|
||||
return None;
|
||||
}
|
||||
Some(format!("rsync://{}/", parsed.host_str()?))
|
||||
}
|
||||
|
||||
struct TempDir {
|
||||
@@ -535,12 +554,57 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn system_rsync_dedup_key_uses_publication_point_scope_by_default() {
|
||||
fn rsync_host_scope_uri_returns_host_only() {
|
||||
assert_eq!(
|
||||
rsync_host_scope_uri("rsync://example.net/repo/ta/ca/publication-point/"),
|
||||
Some("rsync://example.net/".to_string())
|
||||
);
|
||||
assert_eq!(rsync_host_scope_uri("https://example.net/repo"), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn system_rsync_dedup_key_uses_host_scope_by_default() {
|
||||
let fetcher = SystemRsyncFetcher::new(SystemRsyncConfig::default());
|
||||
assert_eq!(
|
||||
fetcher.dedup_key("rsync://example.net/repo/ta/ca/publication-point/"),
|
||||
"rsync://example.net/repo/ta/ca/publication-point/"
|
||||
);
|
||||
assert_eq!(
|
||||
fetcher.failure_dedup_key("rsync://example.net/repo/ta/ca/publication-point/"),
|
||||
Some("rsync://example.net/".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn system_rsync_host_scope_does_not_widen_success_fetch_scope() {
|
||||
let fetcher = SystemRsyncFetcher::new(SystemRsyncConfig {
|
||||
scope_policy: RsyncScopePolicy::Host,
|
||||
..SystemRsyncConfig::default()
|
||||
});
|
||||
assert_eq!(
|
||||
fetcher.dedup_key("rsync://example.net/repo/ta/ca/publication-point/"),
|
||||
"rsync://example.net/repo/ta/ca/publication-point/"
|
||||
);
|
||||
assert_eq!(
|
||||
fetcher.scope_fetch_uri("rsync://example.net/repo/ta/ca/publication-point/"),
|
||||
"rsync://example.net/repo/ta/ca/publication-point/"
|
||||
);
|
||||
assert_eq!(
|
||||
fetcher.failure_dedup_key("rsync://example.net/repo/ta/ca/publication-point/"),
|
||||
Some("rsync://example.net/".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn system_rsync_dedup_key_uses_publication_point_scope_when_configured() {
|
||||
let fetcher = SystemRsyncFetcher::new(SystemRsyncConfig {
|
||||
scope_policy: RsyncScopePolicy::PublicationPoint,
|
||||
..SystemRsyncConfig::default()
|
||||
});
|
||||
assert_eq!(
|
||||
fetcher.dedup_key("rsync://example.net/repo/ta/ca/publication-point/"),
|
||||
"rsync://example.net/repo/ta/ca/publication-point/"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user