дзеркало
https://github.com/l0ng-ai/openidea.git
синхронізовано 2026-11-09 03:05:40 +02:00
feat(app): Enable signed auto-updates from GitHub releases
Configure the updater public key and the releases/latest latest.json endpoint, and build updater artifacts so tauri-action uploads signatures and latest.json. Check for updates once per app start (release builds only) and offer Install and Restart when one is available.
Цей коміт міститься в:
1 джерело
99b4f8bdf6
коміт
3d8a6f07b4
6 files changed
+67
-21
No files matched your search
@@ -239,6 +239,7 @@ pub fn builder() -> tauri_specta::Builder<tauri::Wry> {
|
||||
window::app_log_location,
|
||||
window::app_build_info,
|
||||
window::app_check_update,
|
||||
window::app_startup_update_check,
|
||||
window::app_install_update,
|
||||
debug::debug_exception_filters,
|
||||
debug::debug_set_exception_breakpoints,
|
||||
|
||||
@@ -268,6 +268,26 @@ pub async fn app_check_update(app: AppHandle) -> IdeResult<UpdateCheck> {
|
||||
})
|
||||
}
|
||||
|
||||
/// The background check made once per process when the first window starts;
|
||||
/// later calls (other windows) and debug builds get `None`. Failures are logged,
|
||||
/// not reported: the user didn't ask for this check.
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub async fn app_startup_update_check(app: AppHandle) -> Option<UpdateInfo> {
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
static DONE: AtomicBool = AtomicBool::new(false);
|
||||
if cfg!(debug_assertions) || DONE.swap(true, Ordering::SeqCst) {
|
||||
return None;
|
||||
}
|
||||
match app_check_update(app).await {
|
||||
Ok(check) => check.update,
|
||||
Err(e) => {
|
||||
tracing::warn!("startup update check failed: {e}");
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Downloads and installs the available update, then quits through every
|
||||
/// window's unsaved-changes check and relaunches. If a window cancels the quit,
|
||||
/// the update applies on the next start.
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
"bundle": {
|
||||
"active": true,
|
||||
"targets": "all",
|
||||
"createUpdaterArtifacts": true,
|
||||
"icon": [
|
||||
"icons/32x32.png",
|
||||
"icons/128x128.png",
|
||||
@@ -56,8 +57,8 @@
|
||||
"mainBinaryName": "openidea",
|
||||
"plugins": {
|
||||
"updater": {
|
||||
"pubkey": "",
|
||||
"endpoints": []
|
||||
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDFCOUU0MUFFOUY4NzZCNkQKUldSdGE0ZWZya0dlRzNnRFJUR2loUVdNZ1BmamxkNzFoaWNPUTBsRmI0cjhxZFRqQURybkMwakEK",
|
||||
"endpoints": ["https://github.com/l0ng-ai/openidea/releases/latest/download/latest.json"]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -459,6 +459,17 @@ export const commands = {
|
||||
appLogLocation: () => typedError<LogLocation, IdeError>(__TAURI_INVOKE("app_log_location")),
|
||||
appBuildInfo: () => __TAURI_INVOKE<BuildInfo>("app_build_info"),
|
||||
appCheckUpdate: () => typedError<UpdateCheck, IdeError>(__TAURI_INVOKE("app_check_update")),
|
||||
/**
|
||||
* The background check made once per process when the first window starts;
|
||||
* later calls (other windows) and debug builds get `None`. Failures are logged,
|
||||
* not reported: the user didn't ask for this check.
|
||||
*/
|
||||
appStartupUpdateCheck: () => __TAURI_INVOKE<{
|
||||
version: string,
|
||||
currentVersion: string,
|
||||
notes: string | null,
|
||||
date: string | null,
|
||||
} | null>("app_startup_update_check"),
|
||||
/**
|
||||
* Downloads and installs the available update, then quits through every
|
||||
* window's unsaved-changes check and relaunches. If a window cancels the quit,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { Action } from "@/platform/actions";
|
||||
import { t } from "@/platform/i18n";
|
||||
import { call, commands, isTauri } from "@/platform/ipc";
|
||||
import { call, commands, isTauri, type UpdateInfo } from "@/platform/ipc";
|
||||
import { notifications } from "@/workbench/contributions";
|
||||
import { showAbout } from "./AboutDialog";
|
||||
import { quitApp } from "./closeGuard";
|
||||
@@ -42,28 +42,39 @@ export async function checkForUpdates(): Promise<void> {
|
||||
key: "app.update",
|
||||
});
|
||||
} else {
|
||||
notifications().notify({
|
||||
level: "info",
|
||||
title: t("app.update.available", { version: r.update.version }),
|
||||
body: r.update.notes ?? undefined,
|
||||
sticky: true,
|
||||
key: "app.update",
|
||||
actions: [
|
||||
{
|
||||
label: t("app.update.install"),
|
||||
run: () =>
|
||||
void call(commands.appInstallUpdate()).catch((e) =>
|
||||
notifications().notify({ level: "error", title: t("app.update.installFailed"), body: errorText(e) }),
|
||||
),
|
||||
},
|
||||
],
|
||||
});
|
||||
notifyUpdate(r.update);
|
||||
}
|
||||
} catch (e) {
|
||||
notifications().notify({ level: "error", title: t("app.update.failed"), body: errorText(e), key: "app.update" });
|
||||
}
|
||||
}
|
||||
|
||||
/** Silent check once per app start; only an available update is shown. */
|
||||
export async function checkForUpdatesOnStartup(): Promise<void> {
|
||||
if (!isTauri()) return;
|
||||
const update = await commands.appStartupUpdateCheck().catch(() => null);
|
||||
if (update) notifyUpdate(update);
|
||||
}
|
||||
|
||||
function notifyUpdate(update: UpdateInfo) {
|
||||
notifications().notify({
|
||||
level: "info",
|
||||
title: t("app.update.available", { version: update.version }),
|
||||
body: update.notes ?? undefined,
|
||||
sticky: true,
|
||||
key: "app.update",
|
||||
actions: [
|
||||
{
|
||||
label: t("app.update.install"),
|
||||
run: () =>
|
||||
void call(commands.appInstallUpdate()).catch((e) =>
|
||||
notifications().notify({ level: "error", title: t("app.update.installFailed"), body: errorText(e) }),
|
||||
),
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit menu items on Windows/Linux (macOS uses native roles). They act on the
|
||||
* focused element like the native commands would; CodeMirror handles the
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*/
|
||||
import { registerOverlay } from "@/workbench/contributions";
|
||||
import { AboutOverlay } from "./AboutDialog";
|
||||
import { appActions, updateActions } from "./appActions";
|
||||
import { appActions, checkForUpdatesOnStartup, updateActions } from "./appActions";
|
||||
import { installCloseGuard } from "./closeGuard";
|
||||
import { InvalidateCachesOverlay, registerInvalidateCachesActions } from "./InvalidateCaches";
|
||||
import { buildInfo } from "./host";
|
||||
@@ -32,7 +32,9 @@ export function activate(): () => void {
|
||||
];
|
||||
let disposed = false;
|
||||
void buildInfo().then((b) => {
|
||||
if (b?.updates && !disposed) disposers.push(registerLocalizedActions(updateActions));
|
||||
if (!b?.updates || disposed) return;
|
||||
disposers.push(registerLocalizedActions(updateActions));
|
||||
void checkForUpdatesOnStartup();
|
||||
});
|
||||
return () => {
|
||||
disposed = true;
|
||||
|
||||
Reference in new issue
Заблокувати користувача