Resolve connected library branch fallbacks
This commit is contained in:
@@ -9,3 +9,4 @@ All notable Modrinth Plus changes are documented here.
|
|||||||
- Added Windows installer publishing to the Gitea generic package registry.
|
- Added Windows installer publishing to the Gitea generic package registry.
|
||||||
- Added Codex repository context and release/security documentation.
|
- Added Codex repository context and release/security documentation.
|
||||||
- Fixed startup compatibility with existing official Modrinth App databases that recorded different checksums for historical SQLite migrations.
|
- Fixed startup compatibility with existing official Modrinth App databases that recorded different checksums for historical SQLite migrations.
|
||||||
|
- Fixed Connected Library Git repository URL resolution for repositories that use `master` instead of `main`.
|
||||||
|
|||||||
@@ -157,9 +157,7 @@ pub async fn list() -> crate::Result<Vec<ConnectedPack>> {
|
|||||||
|
|
||||||
pub async fn connect(source_url: String) -> crate::Result<ConnectedPack> {
|
pub async fn connect(source_url: String) -> crate::Result<ConnectedPack> {
|
||||||
let state = State::get().await?;
|
let state = State::get().await?;
|
||||||
let manifest_url = normalize_manifest_url(&source_url)?;
|
let (manifest_url, manifest) = resolve_manifest(&source_url).await?;
|
||||||
let manifest = fetch_manifest(&manifest_url).await?;
|
|
||||||
validate_manifest(&manifest)?;
|
|
||||||
upsert_manifest(&state.pool, None, &source_url, &manifest_url, &manifest)
|
upsert_manifest(&state.pool, None, &source_url, &manifest_url, &manifest)
|
||||||
.await?;
|
.await?;
|
||||||
get_by_source(&state.pool, &source_url).await
|
get_by_source(&state.pool, &source_url).await
|
||||||
@@ -197,14 +195,17 @@ pub async fn set_auto_update(
|
|||||||
pub async fn check(id: String) -> crate::Result<ConnectedCheckResult> {
|
pub async fn check(id: String) -> crate::Result<ConnectedCheckResult> {
|
||||||
let state = State::get().await?;
|
let state = State::get().await?;
|
||||||
let pack = get_by_id(&state.pool, &id).await?;
|
let pack = get_by_id(&state.pool, &id).await?;
|
||||||
let manifest = fetch_manifest(&pack.manifest_url).await?;
|
let (manifest_url, manifest) = match fetch_manifest(&pack.manifest_url).await
|
||||||
validate_manifest(&manifest)?;
|
{
|
||||||
|
Ok(manifest) => (pack.manifest_url.clone(), manifest),
|
||||||
|
Err(_) => resolve_manifest(&pack.source_url).await?,
|
||||||
|
};
|
||||||
|
|
||||||
upsert_manifest(
|
upsert_manifest(
|
||||||
&state.pool,
|
&state.pool,
|
||||||
Some(&pack.id),
|
Some(&pack.id),
|
||||||
&pack.source_url,
|
&pack.source_url,
|
||||||
&pack.manifest_url,
|
&manifest_url,
|
||||||
&manifest,
|
&manifest,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
@@ -306,7 +307,7 @@ pub async fn install(id: String) -> crate::Result<ConnectedPack> {
|
|||||||
get_by_id(&state.pool, &id).await
|
get_by_id(&state.pool, &id).await
|
||||||
}
|
}
|
||||||
|
|
||||||
fn normalize_manifest_url(source_url: &str) -> crate::Result<String> {
|
fn manifest_url_candidates(source_url: &str) -> crate::Result<Vec<String>> {
|
||||||
let source = source_url.trim();
|
let source = source_url.trim();
|
||||||
let parsed = Url::parse(source)?;
|
let parsed = Url::parse(source)?;
|
||||||
|
|
||||||
@@ -318,7 +319,7 @@ fn normalize_manifest_url(source_url: &str) -> crate::Result<String> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if source.ends_with(MANIFEST_FILE_NAME) || source.contains("/raw/") {
|
if source.ends_with(MANIFEST_FILE_NAME) || source.contains("/raw/") {
|
||||||
return Ok(source.to_string());
|
return Ok(vec![source.to_string()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
let host = parsed.host_str().unwrap_or_default();
|
let host = parsed.host_str().unwrap_or_default();
|
||||||
@@ -329,30 +330,52 @@ fn normalize_manifest_url(source_url: &str) -> crate::Result<String> {
|
|||||||
|
|
||||||
if host == "github.com" && segments.len() >= 2 {
|
if host == "github.com" && segments.len() >= 2 {
|
||||||
let repo = segments[1].trim_end_matches(".git");
|
let repo = segments[1].trim_end_matches(".git");
|
||||||
return Ok(format!(
|
return Ok(vec![
|
||||||
"https://raw.githubusercontent.com/{}/{}/main/{}",
|
format!(
|
||||||
segments[0], repo, MANIFEST_FILE_NAME
|
"https://raw.githubusercontent.com/{}/{}/main/{}",
|
||||||
));
|
segments[0], repo, MANIFEST_FILE_NAME
|
||||||
|
),
|
||||||
|
format!(
|
||||||
|
"https://raw.githubusercontent.com/{}/{}/master/{}",
|
||||||
|
segments[0], repo, MANIFEST_FILE_NAME
|
||||||
|
),
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if host == "gitlab.com" && segments.len() >= 2 {
|
if host == "gitlab.com" && segments.len() >= 2 {
|
||||||
let repo = segments[1].trim_end_matches(".git");
|
let repo = segments[1].trim_end_matches(".git");
|
||||||
return Ok(format!(
|
return Ok(vec![
|
||||||
"https://gitlab.com/{}/{}/-/raw/main/{}",
|
format!(
|
||||||
segments[0], repo, MANIFEST_FILE_NAME
|
"https://gitlab.com/{}/{}/-/raw/main/{}",
|
||||||
));
|
segments[0], repo, MANIFEST_FILE_NAME
|
||||||
|
),
|
||||||
|
format!(
|
||||||
|
"https://gitlab.com/{}/{}/-/raw/master/{}",
|
||||||
|
segments[0], repo, MANIFEST_FILE_NAME
|
||||||
|
),
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if segments.len() >= 2 {
|
if segments.len() >= 2 {
|
||||||
let repo = segments[1].trim_end_matches(".git");
|
let repo = segments[1].trim_end_matches(".git");
|
||||||
return Ok(format!(
|
return Ok(vec![
|
||||||
"{}://{}/{}/{}/raw/branch/main/{}",
|
format!(
|
||||||
parsed.scheme(),
|
"{}://{}/{}/{}/raw/branch/main/{}",
|
||||||
host,
|
parsed.scheme(),
|
||||||
segments[0],
|
host,
|
||||||
repo,
|
segments[0],
|
||||||
MANIFEST_FILE_NAME
|
repo,
|
||||||
));
|
MANIFEST_FILE_NAME
|
||||||
|
),
|
||||||
|
format!(
|
||||||
|
"{}://{}/{}/{}/raw/branch/master/{}",
|
||||||
|
parsed.scheme(),
|
||||||
|
host,
|
||||||
|
segments[0],
|
||||||
|
repo,
|
||||||
|
MANIFEST_FILE_NAME
|
||||||
|
),
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(ErrorKind::InputError(
|
Err(ErrorKind::InputError(
|
||||||
@@ -362,6 +385,26 @@ fn normalize_manifest_url(source_url: &str) -> crate::Result<String> {
|
|||||||
.into())
|
.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn resolve_manifest(
|
||||||
|
source_url: &str,
|
||||||
|
) -> crate::Result<(String, ConnectedManifest)> {
|
||||||
|
let candidates = manifest_url_candidates(source_url)?;
|
||||||
|
let mut errors = Vec::new();
|
||||||
|
|
||||||
|
for manifest_url in candidates {
|
||||||
|
match fetch_manifest(&manifest_url).await {
|
||||||
|
Ok(manifest) => return Ok((manifest_url, manifest)),
|
||||||
|
Err(err) => errors.push(format!("{manifest_url}: {err}")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(ErrorKind::InputError(format!(
|
||||||
|
"Could not fetch {MANIFEST_FILE_NAME}. Tried: {}",
|
||||||
|
errors.join("; ")
|
||||||
|
))
|
||||||
|
.into())
|
||||||
|
}
|
||||||
|
|
||||||
async fn fetch_manifest(url: &str) -> crate::Result<ConnectedManifest> {
|
async fn fetch_manifest(url: &str) -> crate::Result<ConnectedManifest> {
|
||||||
let manifest = reqwest::get(url)
|
let manifest = reqwest::get(url)
|
||||||
.await?
|
.await?
|
||||||
|
|||||||
Reference in New Issue
Block a user