Daedalus doesn't instantly fail when upstream loses versions (#5518)

* Daedalus doesn't instantly fail when upstream loses versions

* fix shear

* Revert DashMap changes for try_join_all
This commit is contained in:
aecsocket
2026-03-10 18:59:47 +00:00
committed by GitHub
parent 0c98f6bf45
commit 2b8175ad66
4 changed files with 119 additions and 79 deletions

View File

@@ -1,17 +1,17 @@
use crate::util::{download_file, fetch_json, format_url}; use crate::util::{download_file, fetch_json, format_url};
use crate::{Error, MirrorArtifact, UploadFile, insert_mirrored_artifact}; use crate::{
Error, FetchResult, MirrorArtifact, UploadFile, insert_mirrored_artifact,
};
use daedalus::modded::{DUMMY_REPLACE_STRING, Manifest, PartialVersionInfo}; use daedalus::modded::{DUMMY_REPLACE_STRING, Manifest, PartialVersionInfo};
use dashmap::DashMap; use dashmap::DashMap;
use serde::Deserialize; use serde::Deserialize;
use std::sync::Arc; use std::sync::Arc;
use tokio::sync::Semaphore; use tokio::sync::Semaphore;
#[tracing::instrument(skip(semaphore, upload_files, mirror_artifacts))] #[tracing::instrument(skip(semaphore))]
pub async fn fetch_fabric( pub async fn fetch_fabric(
semaphore: Arc<Semaphore>, semaphore: Arc<Semaphore>,
upload_files: &DashMap<String, UploadFile>, ) -> Result<FetchResult, Error> {
mirror_artifacts: &DashMap<String, MirrorArtifact>,
) -> Result<(), Error> {
fetch( fetch(
daedalus::modded::CURRENT_FABRIC_FORMAT_VERSION, daedalus::modded::CURRENT_FABRIC_FORMAT_VERSION,
"fabric", "fabric",
@@ -19,18 +19,14 @@ pub async fn fetch_fabric(
"https://maven.fabricmc.net/", "https://maven.fabricmc.net/",
&[], &[],
semaphore, semaphore,
upload_files,
mirror_artifacts,
) )
.await .await
} }
#[tracing::instrument(skip(semaphore, upload_files, mirror_artifacts))] #[tracing::instrument(skip(semaphore))]
pub async fn fetch_quilt( pub async fn fetch_quilt(
semaphore: Arc<Semaphore>, semaphore: Arc<Semaphore>,
upload_files: &DashMap<String, UploadFile>, ) -> Result<FetchResult, Error> {
mirror_artifacts: &DashMap<String, MirrorArtifact>,
) -> Result<(), Error> {
fetch( fetch(
daedalus::modded::CURRENT_QUILT_FORMAT_VERSION, daedalus::modded::CURRENT_QUILT_FORMAT_VERSION,
"quilt", "quilt",
@@ -41,14 +37,12 @@ pub async fn fetch_quilt(
"0.17.5-beta.4", "0.17.5-beta.4",
], ],
semaphore, semaphore,
upload_files,
mirror_artifacts,
) )
.await .await
} }
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
#[tracing::instrument(skip(semaphore, upload_files, mirror_artifacts))] #[tracing::instrument(skip(semaphore))]
async fn fetch( async fn fetch(
format_version: usize, format_version: usize,
mod_loader: &str, mod_loader: &str,
@@ -56,9 +50,9 @@ async fn fetch(
maven_url: &str, maven_url: &str,
skip_versions: &[&str], skip_versions: &[&str],
semaphore: Arc<Semaphore>, semaphore: Arc<Semaphore>,
upload_files: &DashMap<String, UploadFile>, ) -> Result<FetchResult, Error> {
mirror_artifacts: &DashMap<String, MirrorArtifact>, let upload_files = DashMap::new();
) -> Result<(), Error> { let mirror_artifacts = DashMap::<String, MirrorArtifact>::new();
let modrinth_manifest = fetch_json::<Manifest>( let modrinth_manifest = fetch_json::<Manifest>(
&format_url(&format!("{mod_loader}/v{format_version}/manifest.json",)), &format_url(&format!("{mod_loader}/v{format_version}/manifest.json",)),
&semaphore, &semaphore,
@@ -124,7 +118,7 @@ async fn fetch(
None, None,
vec![maven_url.to_string()], vec![maven_url.to_string()],
false, false,
mirror_artifacts, &mirror_artifacts,
)?; )?;
} }
} }
@@ -175,7 +169,7 @@ async fn fetch(
.unwrap_or_else(|| maven_url.to_string()), .unwrap_or_else(|| maven_url.to_string()),
], ],
false, false,
mirror_artifacts, &mirror_artifacts,
)?; )?;
} else { } else {
lib.name = new_name; lib.name = new_name;
@@ -268,7 +262,10 @@ async fn fetch(
); );
} }
Ok(()) Ok(FetchResult {
upload_files,
mirror_artifacts,
})
} }
#[derive(Deserialize, Debug, Clone)] #[derive(Deserialize, Debug, Clone)]

View File

@@ -1,5 +1,7 @@
use crate::util::{download_file, fetch_json, fetch_xml, format_url}; use crate::util::{download_file, fetch_json, fetch_xml, format_url};
use crate::{Error, MirrorArtifact, UploadFile, insert_mirrored_artifact}; use crate::{
Error, FetchResult, MirrorArtifact, UploadFile, insert_mirrored_artifact,
};
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
use daedalus::get_path_from_artifact; use daedalus::get_path_from_artifact;
use daedalus::modded::PartialVersionInfo; use daedalus::modded::PartialVersionInfo;
@@ -13,12 +15,10 @@ use std::collections::HashMap;
use std::sync::Arc; use std::sync::Arc;
use tokio::sync::Semaphore; use tokio::sync::Semaphore;
#[tracing::instrument(skip(semaphore, upload_files, mirror_artifacts))] #[tracing::instrument(skip(semaphore))]
pub async fn fetch_forge( pub async fn fetch_forge(
semaphore: Arc<Semaphore>, semaphore: Arc<Semaphore>,
upload_files: &DashMap<String, UploadFile>, ) -> Result<FetchResult, Error> {
mirror_artifacts: &DashMap<String, MirrorArtifact>,
) -> Result<(), Error> {
let forge_manifest = fetch_json::<IndexMap<String, Vec<String>>>( let forge_manifest = fetch_json::<IndexMap<String, Vec<String>>>(
"https://files.minecraftforge.net/net/minecraftforge/forge/maven-metadata.json", "https://files.minecraftforge.net/net/minecraftforge/forge/maven-metadata.json",
&semaphore, &semaphore,
@@ -90,18 +90,14 @@ pub async fn fetch_forge(
"https://maven.minecraftforge.net/", "https://maven.minecraftforge.net/",
forge_versions, forge_versions,
semaphore, semaphore,
upload_files,
mirror_artifacts,
) )
.await .await
} }
#[tracing::instrument(skip(semaphore, upload_files, mirror_artifacts))] #[tracing::instrument(skip(semaphore))]
pub async fn fetch_neo( pub async fn fetch_neo(
semaphore: Arc<Semaphore>, semaphore: Arc<Semaphore>,
upload_files: &DashMap<String, UploadFile>, ) -> Result<FetchResult, Error> {
mirror_artifacts: &DashMap<String, MirrorArtifact>,
) -> Result<(), Error> {
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
struct Metadata { struct Metadata {
versioning: Versioning, versioning: Versioning,
@@ -188,27 +184,20 @@ pub async fn fetch_neo(
"https://maven.neoforged.net/", "https://maven.neoforged.net/",
parsed_versions, parsed_versions,
semaphore, semaphore,
upload_files,
mirror_artifacts,
) )
.await .await
} }
#[tracing::instrument(skip( #[tracing::instrument(skip(forge_versions, semaphore))]
forge_versions,
semaphore,
upload_files,
mirror_artifacts
))]
async fn fetch( async fn fetch(
format_version: usize, format_version: usize,
mod_loader: &str, mod_loader: &str,
maven_url: &str, maven_url: &str,
forge_versions: Vec<ForgeVersion>, forge_versions: Vec<ForgeVersion>,
semaphore: Arc<Semaphore>, semaphore: Arc<Semaphore>,
upload_files: &DashMap<String, UploadFile>, ) -> Result<FetchResult, Error> {
mirror_artifacts: &DashMap<String, MirrorArtifact>, let upload_files = DashMap::new();
) -> Result<(), Error> { let mirror_artifacts = DashMap::<String, MirrorArtifact>::new();
let modrinth_manifest = fetch_json::<daedalus::modded::Manifest>( let modrinth_manifest = fetch_json::<daedalus::modded::Manifest>(
&format_url(&format!("{mod_loader}/v{format_version}/manifest.json",)), &format_url(&format!("{mod_loader}/v{format_version}/manifest.json",)),
&semaphore, &semaphore,
@@ -708,8 +697,8 @@ async fn fetch(
loader, loader,
maven_url, maven_url,
mod_loader, mod_loader,
upload_files, &upload_files,
mirror_artifacts, &mirror_artifacts,
) )
}), }),
) )
@@ -778,7 +767,10 @@ async fn fetch(
); );
} }
Ok(()) Ok(FetchResult {
upload_files,
mirror_artifacts,
})
} }
#[derive(Debug)] #[derive(Debug)]

View File

@@ -43,47 +43,64 @@ async fn main() -> Result<()> {
.unwrap_or(10), .unwrap_or(10),
)); ));
// path, upload file let mut fetch_result = FetchResult::default();
let upload_files: DashMap<String, UploadFile> = DashMap::new();
// path, mirror artifact
let mirror_artifacts: DashMap<String, MirrorArtifact> = DashMap::new();
minecraft::fetch(semaphore.clone(), &upload_files, &mirror_artifacts) match minecraft::fetch(semaphore.clone()).await {
.await?; Ok(fetched) => merge_fetch_result(&mut fetch_result, fetched),
fabric::fetch_fabric(semaphore.clone(), &upload_files, &mirror_artifacts) Err(err) => tracing::warn!(error = %err, "Minecraft fetch failed"),
.await?; }
fabric::fetch_quilt(semaphore.clone(), &upload_files, &mirror_artifacts)
.await?;
forge::fetch_neo(semaphore.clone(), &upload_files, &mirror_artifacts)
.await?;
forge::fetch_forge(semaphore.clone(), &upload_files, &mirror_artifacts)
.await?;
futures::future::try_join_all(upload_files.iter().map(|x| { match fabric::fetch_fabric(semaphore.clone()).await {
Ok(fetched) => merge_fetch_result(&mut fetch_result, fetched),
Err(err) => tracing::warn!(error = %err, "Fabric fetch failed"),
}
match fabric::fetch_quilt(semaphore.clone()).await {
Ok(fetched) => merge_fetch_result(&mut fetch_result, fetched),
Err(err) => tracing::warn!(error = %err, "Quilt fetch failed"),
}
match forge::fetch_neo(semaphore.clone()).await {
Ok(fetched) => merge_fetch_result(&mut fetch_result, fetched),
Err(err) => tracing::warn!(error = %err, "NeoForge fetch failed"),
}
match forge::fetch_forge(semaphore.clone()).await {
Ok(fetched) => merge_fetch_result(&mut fetch_result, fetched),
Err(err) => tracing::warn!(error = %err, "Forge fetch failed"),
}
let FetchResult {
upload_files,
mirror_artifacts,
} = fetch_result;
futures::future::try_join_all(upload_files.iter().map(|entry| {
upload_file_to_bucket( upload_file_to_bucket(
x.key().clone(), entry.key().clone(),
x.value().file.clone(), entry.value().file.clone(),
x.value().content_type.clone(), entry.value().content_type.clone(),
&semaphore, &semaphore,
) )
})) }))
.await?; .await?;
futures::future::try_join_all(mirror_artifacts.iter().map(|x| { futures::future::try_join_all(mirror_artifacts.iter().map(|entry| {
upload_url_to_bucket_mirrors( upload_url_to_bucket_mirrors(
format!("maven/{}", x.key()), format!("maven/{}", entry.key()),
x.value() entry
.value()
.mirrors .mirrors
.iter() .iter()
.map(|mirror| { .map(|mirror| {
if mirror.entire_url { if mirror.entire_url {
mirror.path.clone() mirror.path.clone()
} else { } else {
format!("{}{}", mirror.path, x.key()) format!("{}{}", mirror.path, entry.key())
} }
}) })
.collect(), .collect(),
x.sha1.clone(), entry.value().sha1.clone(),
&semaphore, &semaphore,
) )
})) }))
@@ -139,17 +156,47 @@ pub struct UploadFile {
content_type: Option<String>, content_type: Option<String>,
} }
#[derive(Default)]
pub struct FetchResult {
pub upload_files: DashMap<String, UploadFile>,
pub mirror_artifacts: DashMap<String, MirrorArtifact>,
}
pub struct MirrorArtifact { pub struct MirrorArtifact {
pub sha1: Option<String>, pub sha1: Option<String>,
pub mirrors: DashSet<Mirror>, pub mirrors: DashSet<Mirror>,
} }
#[derive(Eq, PartialEq, Hash)] #[derive(Clone, Eq, PartialEq, Hash)]
pub struct Mirror { pub struct Mirror {
path: String, path: String,
entire_url: bool, entire_url: bool,
} }
fn merge_fetch_result(fetch_result: &mut FetchResult, fetched: FetchResult) {
for (path, upload_file) in fetched.upload_files {
fetch_result.upload_files.insert(path, upload_file);
}
for (artifact_path, fetched_mirror_artifact) in fetched.mirror_artifacts {
let mut val = fetch_result
.mirror_artifacts
.entry(artifact_path)
.or_insert(MirrorArtifact {
sha1: fetched_mirror_artifact.sha1.clone(),
mirrors: DashSet::new(),
});
if val.sha1.is_none() {
val.sha1 = fetched_mirror_artifact.sha1;
}
for mirror in fetched_mirror_artifact.mirrors {
val.mirrors.insert(mirror);
}
}
}
#[tracing::instrument(skip(mirror_artifacts))] #[tracing::instrument(skip(mirror_artifacts))]
pub fn insert_mirrored_artifact( pub fn insert_mirrored_artifact(
artifact: &str, artifact: &str,
@@ -158,13 +205,17 @@ pub fn insert_mirrored_artifact(
entire_url: bool, entire_url: bool,
mirror_artifacts: &DashMap<String, MirrorArtifact>, mirror_artifacts: &DashMap<String, MirrorArtifact>,
) -> Result<()> { ) -> Result<()> {
let val = mirror_artifacts let mut val = mirror_artifacts
.entry(get_path_from_artifact(artifact)?) .entry(get_path_from_artifact(artifact)?)
.or_insert(MirrorArtifact { .or_insert(MirrorArtifact {
sha1, sha1: sha1.clone(),
mirrors: DashSet::new(), mirrors: DashSet::new(),
}); });
if val.sha1.is_none() {
val.sha1 = sha1;
}
for mirror in mirrors { for mirror in mirrors {
val.mirrors.insert(Mirror { val.mirrors.insert(Mirror {
path: mirror, path: mirror,

View File

@@ -1,6 +1,6 @@
use crate::util::fetch_json; use crate::util::fetch_json;
use crate::{ use crate::{
Error, MirrorArtifact, UploadFile, util::download_file, util::format_url, Error, FetchResult, UploadFile, util::download_file, util::format_url,
util::sha1_async, util::sha1_async,
}; };
use daedalus::minecraft::{ use daedalus::minecraft::{
@@ -12,12 +12,9 @@ use serde::Deserialize;
use std::sync::Arc; use std::sync::Arc;
use tokio::sync::Semaphore; use tokio::sync::Semaphore;
#[tracing::instrument(skip(semaphore, upload_files, _mirror_artifacts))] #[tracing::instrument(skip(semaphore))]
pub async fn fetch( pub async fn fetch(semaphore: Arc<Semaphore>) -> Result<FetchResult, Error> {
semaphore: Arc<Semaphore>, let upload_files = DashMap::new();
upload_files: &DashMap<String, UploadFile>,
_mirror_artifacts: &DashMap<String, MirrorArtifact>,
) -> Result<(), Error> {
let modrinth_manifest = fetch_json::<VersionManifest>( let modrinth_manifest = fetch_json::<VersionManifest>(
&format_url(&format!( &format_url(&format!(
"minecraft/v{}/manifest.json", "minecraft/v{}/manifest.json",
@@ -169,7 +166,10 @@ pub async fn fetch(
); );
} }
Ok(()) Ok(FetchResult {
upload_files,
mirror_artifacts: DashMap::new(),
})
} }
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]