fix: classify official download failures

Distinguish terminal HTTP errors from retryable CDN/network failures, record quarantined resources, and report failed download progress.

Fixes #7
This commit is contained in:
2026-07-14 01:30:12 +08:00
parent 25c2c4d40f
commit 4192d7ee4c
15 changed files with 1034 additions and 195 deletions
+53 -1
View File
@@ -474,6 +474,16 @@ pub struct OfficialUpdateProgress {
pub download_bytes: Option<u64>,
/// Bytes transferred in this run for the URL when known.
pub download_transferred_bytes: Option<u64>,
/// Stable failure kind when a URL failed.
pub download_failure_kind: Option<String>,
/// HTTP status parsed from curl stderr when a URL failed.
pub download_failure_http_status: Option<u16>,
/// Whether the final failure was retryable.
pub download_failure_retryable: Option<bool>,
/// Number of transfer attempts executed before failure.
pub download_failure_attempts: Option<usize>,
/// Whether the URL was recorded in the quarantine manifest.
pub download_quarantined: Option<bool>,
}
impl OfficialUpdateProgress {
@@ -488,6 +498,11 @@ impl OfficialUpdateProgress {
download_status: None,
download_bytes: None,
download_transferred_bytes: None,
download_failure_kind: None,
download_failure_http_status: None,
download_failure_retryable: None,
download_failure_attempts: None,
download_quarantined: None,
}
}
@@ -495,9 +510,21 @@ impl OfficialUpdateProgress {
self.download_index = Some(event.index);
self.download_total = Some(event.total);
self.download_url = Some(event.url.clone());
self.download_status = event.status.map(|status| status.as_str().to_string());
self.download_status = event
.status
.map(|status| status.as_str().to_string())
.or_else(|| {
(event.kind == OfficialResourcePullProgressKind::Failed)
.then(|| "failed".to_string())
});
self.download_bytes = event.bytes;
self.download_transferred_bytes = event.transferred_bytes;
self.download_failure_kind = event.failure_kind.clone();
self.download_failure_http_status = event.failure_http_status;
self.download_failure_retryable = event.failure_retryable;
self.download_failure_attempts = event.failure_attempts;
self.download_quarantined =
(event.kind == OfficialResourcePullProgressKind::Failed).then_some(event.quarantined);
self
}
}
@@ -1391,6 +1418,31 @@ fn progress_from_pull_event(event: OfficialResourcePullProgress) -> OfficialUpda
)
.with_download_progress(&event)
}
OfficialResourcePullProgressKind::Failed => OfficialUpdateProgress::new(
"download",
format!(
"下载中断:总体 {}/{} ({:.1}%);失败类型={} HTTP={} 可重试={} 尝试次数={} quarantine={};本轮跳过该 URL 且不会发布不完整资源 URL={}",
event.index,
event.total,
download_progress_percent(event.index, event.total),
event.failure_kind.as_deref().unwrap_or("unknown"),
event
.failure_http_status
.map(|status| status.to_string())
.unwrap_or_else(|| "none".to_string()),
event
.failure_retryable
.map(|retryable| retryable.to_string())
.unwrap_or_else(|| "unknown".to_string()),
event
.failure_attempts
.map(|attempts| attempts.to_string())
.unwrap_or_else(|| "0".to_string()),
event.quarantined,
event.url
),
)
.with_download_progress(&event),
}
}