chore: release 1.0.2-beta12

This commit is contained in:
Disy committed 2026-10-21 09:16:55 +08:00
1 parent bf95d715a4
commit c44fb1a0a5
43 files changed
+2130 -470

No files matched your search

+42 -82
View File
@@ -1,4 +1,5 @@
use crate::api::Result;
use crate::update_policy::{UpdateTarget, is_rollback, requires_update};
use futures::StreamExt;
use semver::Version;
use serde::{Deserialize, Serialize};
@@ -27,82 +28,6 @@ const STARLIGHT_UPDATE_BASE_URL: &str = "https://skin.starlight.cool/";
const UPDATE_DOWNLOAD_TIMEOUT: std::time::Duration =
std::time::Duration::from_secs(15 * 60);
// semver compares prerelease identifiers lexically, so "beta10" < "beta9"
// (because the character '1' sorts before '9'). Our release names use a
// "betaN" scheme where N is a decimal counter, so a strict lexical compare
// would wrongly report an older beta as the latest. Use a comparator that
// treats the trailing digits of a prerelease identifier numerically.
fn is_remote_newer(current: &Version, remote: &Version) -> bool {
use std::cmp::Ordering;
let core = (remote.major, remote.minor, remote.patch).cmp(&(
current.major,
current.minor,
current.patch,
));
if core != Ordering::Equal {
return core == Ordering::Greater;
}
// Same x.y.z. A final release outranks any prerelease of that version.
match (current.pre.is_empty(), remote.pre.is_empty()) {
(true, true) => false,
(true, false) => false,
(false, true) => true,
(false, false) => {
compare_prerelease(remote.pre.as_str(), current.pre.as_str())
== Ordering::Greater
}
}
}
/// Split a prerelease identifier into its non-digit prefix and an optional
/// trailing decimal number (so "beta10" -> ("beta", Some(10))).
fn split_prerelease_identifier(segment: &str) -> (&str, Option<u64>) {
let digit_start = segment
.char_indices()
.find(|(_, c)| c.is_ascii_digit())
.map(|(i, _)| i);
match digit_start {
Some(i) => (&segment[..i], segment[i..].parse::<u64>().ok()),
None => (segment, None),
}
}
/// Compare two prerelease strings, treating a trailing run of ASCII digits
/// as a decimal number (so "beta10" > "beta9"). All other segments fall back
/// to a plain lexical comparison.
fn compare_prerelease(left: &str, right: &str) -> std::cmp::Ordering {
use std::cmp::Ordering;
let left_segments: Vec<&str> = left.split('.').collect();
let right_segments: Vec<&str> = right.split('.').collect();
for (l, r) in left_segments.iter().zip(right_segments.iter()) {
let (l_prefix, l_num) = split_prerelease_identifier(l);
let (r_prefix, r_num) = split_prerelease_identifier(r);
match (l_num, r_num) {
(Some(ln), Some(rn)) if l_prefix == r_prefix => match ln.cmp(&rn) {
Ordering::Equal => continue,
other => return other,
},
_ => {
let prefix_cmp = l_prefix.cmp(r_prefix);
if prefix_cmp != Ordering::Equal {
return prefix_cmp;
}
let num_cmp = l_num.cmp(&r_num);
if num_cmp != Ordering::Equal {
return num_cmp;
}
}
}
}
left_segments.len().cmp(&right_segments.len())
}
// ── Shared types ─────────────────────────────────────────────────
#[derive(Serialize)]
@@ -115,11 +40,12 @@ pub struct UpdateMetadata {
body: Option<String>,
published_at: Option<String>,
force_update: bool,
rollback: bool,
raw_json: serde_json::Value,
}
#[derive(Default)]
pub struct PendingUpdateData(pub Mutex<Option<(Arc<Update>, Vec<u8>)>>);
pub struct PendingUpdateData(pub Mutex<UpdateTarget<(Arc<Update>, Vec<u8>)>>);
// ── Update Server API types ─────────────────────────────────────
@@ -307,7 +233,7 @@ async fn check_with_endpoints<R: Runtime>(
.header("X-Axolotl-Platform", platform)?
.header("X-Axolotl-Version", current_version)?
.version_comparator(|current, release| {
is_remote_newer(&current, &release.version)
requires_update(&current, &release.version)
});
#[cfg(target_os = "windows")]
@@ -345,7 +271,20 @@ async fn check_with_updater<R: Runtime>(
webview: &Webview<R>,
channel: &str,
) -> Result<Option<UpdateMetadata>> {
let Some(mut update) = check_with_endpoints(webview, channel).await? else {
let pending_data = webview.state::<PendingUpdateData>();
let revision = pending_data.0.lock().unwrap().begin_check();
let checked = check_with_endpoints(webview, channel).await?;
if !pending_data.0.lock().unwrap().select_checked(
revision,
checked.as_ref().map(|update| update.version.as_str()),
) {
return Err(theseus::Error::from(theseus::ErrorKind::OtherError(
"A newer launcher update check superseded this response"
.to_string(),
))
.into());
}
let Some(mut update) = checked else {
return Ok(None);
};
update.timeout = Some(UPDATE_DOWNLOAD_TIMEOUT);
@@ -369,6 +308,10 @@ async fn check_with_updater<R: Runtime>(
.and_then(serde_json::Value::as_bool)
.unwrap_or(false);
let metadata = UpdateMetadata {
rollback: is_rollback(
&Version::parse(&update.current_version).map_err(Error::from)?,
&Version::parse(&update.version).map_err(Error::from)?,
),
rid: webview.resources_table().add(update.clone()),
current_version: update.current_version.clone(),
version: update.version.clone(),
@@ -449,6 +392,12 @@ pub async fn enqueue_update_for_installation<R: Runtime>(
let update = webview.resources_table().get::<Update>(rid)?;
if !pending_data.0.lock().unwrap().is_selected(&update.version) {
return Err(theseus::Error::from(theseus::ErrorKind::OtherError(
"The server-selected launcher version changed; check for updates again".to_string(),
)).into());
}
let progress = init_loading(
LoadingBarType::LauncherUpdate {
version: update.version.clone(),
@@ -565,11 +514,19 @@ pub async fn enqueue_update_for_installation<R: Runtime>(
let download_duration = download_start.elapsed();
tracing::info!("Downloaded update in {download_duration:?}");
pending_data
let version = update.version.clone();
if !pending_data
.0
.lock()
.unwrap()
.replace((update, update_data));
.enqueue(&version, (update, update_data))
{
return Err(theseus::Error::from(theseus::ErrorKind::OtherError(
"The server-selected launcher version changed during download"
.to_string(),
))
.into());
}
Ok(())
}
@@ -577,7 +534,9 @@ pub async fn enqueue_update_for_installation<R: Runtime>(
#[tauri::command]
pub fn remove_enqueued_update<R: Runtime>(webview: Webview<R>) {
let pending_data = webview.state::<PendingUpdateData>().inner();
pending_data.0.lock().unwrap().take();
let mut pending = pending_data.0.lock().unwrap();
pending.begin_check();
pending.select(None);
}
// ── Debian / derivatives apt update ─────────────────────────────
@@ -630,6 +589,7 @@ pub async fn install_apt_package(version: &str, data: &[u8]) -> Result<()> {
.arg("apt")
.arg("install")
.arg("-y")
.arg("--allow-downgrades")
.arg(&install_path)
.output()
})