Release 1.0.3: Windows sharp in CI, manual update check, updater IPC
Release / release (push) Failing after 29s

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Ivan Fontosh
2026-05-12 07:30:03 +08:00
parent e165a41180
commit e4b989c60f
9 changed files with 261 additions and 8 deletions
+2 -1
View File
@@ -252,6 +252,7 @@ async function main() {
registerHandler(ipcChannels.app.getVersion, () => ({
version: getAppSemanticVersion(),
buildNumber: getOptionalBuildNumber(),
packaged: app.isPackaged,
}));
registerHandler(ipcChannels.license.getStatus, () => licenseService.getStatus());
registerHandler(ipcChannels.license.setToken, async ({ token }) => licenseService.setToken(token));
@@ -526,9 +527,9 @@ async function main() {
return { ok: true };
});
installAutoUpdater(licenseService, registerHandler);
installIpcRouter();
applyDockIconIfNeeded();
installAutoUpdater(licenseService);
await runStartupAfterHandlers(licenseService);
app.on('activate', () => {
+2
View File
@@ -36,6 +36,8 @@ export function registerHandler<K extends keyof IpcInvokeMap>(channel: K, handle
handlers.set(channelStr, inner);
}
export type IpcRegisterHandler = typeof registerHandler;
export function installIpcRouter(): void {
for (const [channel, handler] of handlers.entries()) {
ipcMain.handle(channel, async (_event, payload: unknown) => handler(payload));
+57 -1
View File
@@ -1,6 +1,12 @@
import { app, dialog } from 'electron';
import { autoUpdater } from 'electron-updater';
import {
ipcChannels,
type UpdaterCheckResponse,
type UpdaterDownloadResponse,
} from '../../shared/ipc/contracts';
import type { IpcRegisterHandler } from '../ipc/router';
import { addLicenseChangeListener } from '../license/licenseService';
import type { LicenseService } from '../license/licenseService';
@@ -9,6 +15,10 @@ const STARTUP_CHECK_DELAY_MS = 12_000;
const RE_CHECK_COOLDOWN_MS = 30_000;
let lastCheckAt = 0;
/** Ручная установка: не показывать второй диалог из `update-downloaded`. */
let suppressAutoInstallDialog = false;
type RegisterFn = IpcRegisterHandler;
function isLicensedForUpdates(licenseService: LicenseService): boolean {
const snap = licenseService.getStatusSync();
@@ -24,11 +34,53 @@ function maybeCheckForUpdates(licenseService: LicenseService, ignoreCooldown: bo
void autoUpdater.checkForUpdates().catch(() => undefined);
}
async function runManualUpdaterCheck(licenseService: LicenseService): Promise<UpdaterCheckResponse> {
if (!app.isPackaged) {
return { outcome: 'not_packaged' };
}
if (!isLicensedForUpdates(licenseService)) {
return { outcome: 'no_license' };
}
try {
const result = await autoUpdater.checkForUpdates();
if (result && result.isUpdateAvailable && result.updateInfo.version) {
return { outcome: 'available', version: result.updateInfo.version };
}
return { outcome: 'current', currentVersion: app.getVersion() };
} catch (e) {
const message = e instanceof Error ? e.message : String(e);
return { outcome: 'error', message };
}
}
async function runManualDownloadAndRestart(): Promise<UpdaterDownloadResponse> {
if (!app.isPackaged) {
return { ok: false, message: 'NOT_PACKAGED' };
}
try {
suppressAutoInstallDialog = true;
await autoUpdater.downloadUpdate();
autoUpdater.quitAndInstall(false, true);
return { ok: true };
} catch (e) {
suppressAutoInstallDialog = false;
const message = e instanceof Error ? e.message : String(e);
return { ok: false, message };
}
}
function registerUpdaterHandlers(register: RegisterFn, licenseService: LicenseService): void {
register(ipcChannels.updater.check, () => runManualUpdaterCheck(licenseService));
register(ipcChannels.updater.downloadAndRestart, () => runManualDownloadAndRestart());
}
/**
* Проверка обновлений: только упакованное приложение, только при активной лицензии.
* Канал и URL задаются при сборке (`publish` → `app-update.yml` внутри установки).
*/
export function installAutoUpdater(licenseService: LicenseService): void {
export function installAutoUpdater(licenseService: LicenseService, register: RegisterFn): void {
registerUpdaterHandlers(register, licenseService);
if (!app.isPackaged) return;
const feedOverride = process.env.DND_UPDATE_FEED_URL?.trim();
@@ -41,6 +93,10 @@ export function installAutoUpdater(licenseService: LicenseService): void {
autoUpdater.autoInstallOnAppQuit = true;
autoUpdater.on('update-downloaded', (info) => {
if (suppressAutoInstallDialog) {
suppressAutoInstallDialog = false;
return;
}
void dialog
.showMessageBox({
type: 'info',