Fix: Updater funktioniert jetzt auch bei lokalen Builds [appimage]
All checks were successful
Build AppImage / build (push) Successful in 8m51s

Dev-Check entfernt der Updates bei lokalen Builds blockiert hat.
Jede gültige Remote-Version wird bei dev/dev-local als neuer erkannt.
HTTP/Netzwerk-Fehler werden graceful behandelt statt zu crashen.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Eddy 2026-04-20 13:19:54 +02:00
parent 0a447591da
commit 9be09897dd

View file

@ -103,7 +103,7 @@ const PACKAGE_BASE_URL: &str =
"https://git.data-it-solution.de/api/packages/data/generic/claude-desktop/latest/"; "https://git.data-it-solution.de/api/packages/data/generic/claude-desktop/latest/";
/// Version der laufenden App. Wird von der CI als APP_VERSION zur Build-Zeit gesetzt. /// Version der laufenden App. Wird von der CI als APP_VERSION zur Build-Zeit gesetzt.
/// Lokaler Build ohne Env-Var → "dev" → Update-Check wird übersprungen. /// Lokaler Build ohne Env-Var → "dev" → Update wird trotzdem geprüft (jede Remote-Version gilt als neuer).
const CURRENT_VERSION: &str = match option_env!("APP_VERSION") { const CURRENT_VERSION: &str = match option_env!("APP_VERSION") {
Some(v) => v, Some(v) => v,
None => "dev", None => "dev",
@ -171,36 +171,43 @@ fn authed_get(client: &reqwest::Client, url: &str) -> reqwest::RequestBuilder {
} }
/// Prüft ob ein Update verfügbar ist. /// Prüft ob ein Update verfügbar ist.
/// Lokale Dev-Builds ohne APP_VERSION-Env geben sofort available=false zurück. /// Auch Dev-Builds prüfen — jede Remote-Version gilt dann als neuer.
#[tauri::command] #[tauri::command]
pub async fn check_for_update() -> Result<UpdateStatus, String> { pub async fn check_for_update() -> Result<UpdateStatus, String> {
// Dev-Build → kein Update-Check let client = reqwest::Client::new();
if CURRENT_VERSION == "dev" { let response = match authed_get(&client, UPDATE_JSON_URL).send().await {
Ok(r) => r,
Err(e) => {
// Netzwerkfehler → kein Crash, einfach "kein Update gefunden"
return Ok(UpdateStatus {
available: false,
current_version: CURRENT_VERSION.to_string(),
latest_version: None,
release_notes: Some(format!("Update-Check fehlgeschlagen (Netzwerk): {}", e)),
download_url: None,
download_size: None,
sha256: None,
});
}
};
if !response.status().is_success() {
// 401/403 ohne Token oder mit falschem Token → kein Crash, nur Hinweis
return Ok(UpdateStatus { return Ok(UpdateStatus {
available: false, available: false,
current_version: CURRENT_VERSION.to_string(), current_version: CURRENT_VERSION.to_string(),
latest_version: None, latest_version: None,
release_notes: Some("Entwicklungs-Build — Updates deaktiviert.".to_string()), release_notes: Some(format!(
"Update-Check fehlgeschlagen (HTTP {}). Token konfiguriert: {}",
response.status(),
UPDATE_TOKEN.is_some()
)),
download_url: None, download_url: None,
download_size: None, download_size: None,
sha256: None, sha256: None,
}); });
} }
let client = reqwest::Client::new();
let response = authed_get(&client, UPDATE_JSON_URL)
.send()
.await
.map_err(|e| format!("Netzwerkfehler beim Update-Check: {}", e))?;
if !response.status().is_success() {
return Err(format!(
"Manifest-Abruf fehlgeschlagen ({}). Token konfiguriert: {}",
response.status(),
UPDATE_TOKEN.is_some()
));
}
let manifest: UpdateManifest = response let manifest: UpdateManifest = response
.json() .json()
.await .await
@ -448,13 +455,26 @@ pub fn get_current_version() -> String {
CURRENT_VERSION.to_string() CURRENT_VERSION.to_string()
} }
/// Prüft ob eine Version ein gültiges Zeitstempel-Format hat (nur Ziffern und Bindestriche).
/// Beispiel: "20260420-1300" → true, "dev" → false, "dev-local" → false
fn is_timestamp_version(v: &str) -> bool {
!v.is_empty() && v.chars().all(|c| c.is_ascii_digit() || c == '-')
}
/// String-Vergleich für `YYYYMMDD-HHMM`-Versionen. /// String-Vergleich für `YYYYMMDD-HHMM`-Versionen.
/// Lexikographisch > ist bei Zeitstempel-Format korrekt. /// Lexikographisch > ist bei Zeitstempel-Format korrekt.
/// "dev" ist immer "nicht neuer". /// Wenn die lokale Version kein gültiger Zeitstempel ist (z.B. "dev", "dev-local"),
/// gilt jede Remote-Version als neuer → Update immer anbieten.
fn is_newer(candidate: &str, current: &str) -> bool { fn is_newer(candidate: &str, current: &str) -> bool {
if candidate == "dev" || current == "dev" { // Remote-Version muss ein gültiger Zeitstempel sein
if !is_timestamp_version(candidate) {
return false; return false;
} }
// Lokale Version kein Zeitstempel → jede gültige Remote-Version ist neuer
if !is_timestamp_version(current) {
return true;
}
// Beide gültig → lexikographischer Vergleich
candidate > current candidate > current
} }
@ -471,9 +491,28 @@ mod tests {
} }
#[test] #[test]
fn test_is_newer_dev_always_false() { fn test_is_newer_dev_local_immer_update() {
// Lokale Version "dev" oder "dev-local" → jede gültige Remote-Version ist neuer
assert!(is_newer("20260420-1200", "dev"));
assert!(is_newer("20260420-1200", "dev-local"));
assert!(is_newer("20260101-0000", "dev"));
}
#[test]
fn test_is_newer_remote_dev_nie() {
// Remote-Version "dev" → niemals als neuer behandeln
assert!(!is_newer("dev", "20260420-1200")); assert!(!is_newer("dev", "20260420-1200"));
assert!(!is_newer("20260420-1200", "dev"));
assert!(!is_newer("dev", "dev")); assert!(!is_newer("dev", "dev"));
assert!(!is_newer("dev-local", "dev"));
}
#[test]
fn test_is_timestamp_version() {
assert!(is_timestamp_version("20260420-1300"));
assert!(is_timestamp_version("20260101-0000"));
assert!(!is_timestamp_version("dev"));
assert!(!is_timestamp_version("dev-local"));
assert!(!is_timestamp_version("v1.0.0"));
assert!(!is_timestamp_version(""));
} }
} }