Реализация автообновления (Auto-Update) десктоп-приложения

Наша компания занимается разработкой, поддержкой и обслуживанием сайтов любой сложности. От простых одностраничных сайтов до масштабных кластерных систем построенных на микро сервисах. Опыт разработчиков подтвержден сертификатами от вендоров.

Разработка и обслуживание любых видов сайтов:

Информационные сайты или веб-приложения
Сайты визитки, landing page, корпоративные сайты, онлайн каталоги, квиз, промо-сайты, блоги, новостные ресурсы, информационные порталы, форумы, агрегаторы
Сайты или веб-приложения электронной коммерции
Интернет-магазины, B2B-порталы, маркетплейсы, онлайн-обменники, кэшбэк-сайты, биржи, дропшиппинг-платформы, парсеры товаров
Веб-приложения для управления бизнес-процессами
CRM-системы, ERP-системы, корпоративные порталы, системы управления производством, парсеры информации
Сайты или веб-приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, конструкторы сайтов, порталы предоставления электронных услуг, видеохостинги, тематические порталы

Это лишь некоторые из технических типов сайтов, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента

Предлагаемые услуги
Показано 1 из 1 услугВсе 2065 услуг
Реализация автообновления (Auto-Update) десктоп-приложения
Средняя
~2-3 рабочих дня
Часто задаваемые вопросы

Наши компетенции:

Этапы разработки

Последние работы

  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    874
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Разработка веб-сайта для компании ФИКСПЕР
    851

Реализация автообновления (Auto-Update) десктоп-приложения

Автообновление — критичная функция для десктоп-приложений. Без неё пользователи месяцами работают со старыми версиями. С плохой реализацией — теряют данные или получают сломанные обновления. Рассмотрим реализацию для Electron и Tauri.

Electron: electron-updater

electron-updater (из пакета electron-builder) — стандартный инструмент для обновлений в Electron. Поддерживает GitHub Releases, S3, собственный сервер.

npm install electron-updater
// main/updater.js
const { autoUpdater } = require('electron-updater');
const { app, BrowserWindow } = require('electron');
const log = require('electron-log');

autoUpdater.logger = log;
autoUpdater.logger.transports.file.level = 'info';

// Не загружать и устанавливать обновление автоматически
autoUpdater.autoDownload = false;
autoUpdater.autoInstallOnAppQuit = true;

function setupAutoUpdater(mainWindow) {
  autoUpdater.on('checking-for-update', () => {
    mainWindow.webContents.send('update:checking');
  });

  autoUpdater.on('update-available', (info) => {
    mainWindow.webContents.send('update:available', {
      version: info.version,
      releaseNotes: info.releaseNotes,
      releaseDate: info.releaseDate
    });
  });

  autoUpdater.on('update-not-available', () => {
    mainWindow.webContents.send('update:not-available');
  });

  autoUpdater.on('download-progress', (progress) => {
    mainWindow.webContents.send('update:progress', {
      percent: Math.round(progress.percent),
      transferred: progress.transferred,
      total: progress.total,
      bytesPerSecond: progress.bytesPerSecond
    });
  });

  autoUpdater.on('update-downloaded', (info) => {
    mainWindow.webContents.send('update:downloaded', {
      version: info.version
    });
  });

  autoUpdater.on('error', (error) => {
    mainWindow.webContents.send('update:error', error.message);
    log.error('Updater error:', error);
  });

  // IPC handlers
  const { ipcMain } = require('electron');

  ipcMain.handle('updater:check', () => autoUpdater.checkForUpdates());
  ipcMain.handle('updater:download', () => autoUpdater.downloadUpdate());
  ipcMain.handle('updater:install', () => {
    autoUpdater.quitAndInstall(false, true); // isSilent, isForceRunAfter
  });

  // Проверять обновления каждые 4 часа
  setInterval(() => autoUpdater.checkForUpdates(), 4 * 60 * 60 * 1000);

  // Проверить при старте (после небольшой задержки)
  setTimeout(() => autoUpdater.checkForUpdates(), 10000);
}

module.exports = { setupAutoUpdater };

Конфигурация для GitHub Releases

# electron-builder.yml
publish:
  provider: github
  owner: your-github-username
  repo: your-repo-name
  private: false

При сборке electron-builder создаёт файл latest.yml (Windows/Linux) или latest-mac.yml (macOS) с метаданными версии. autoUpdater читает эти файлы для проверки обновлений.

Релиз в GitHub:

# Сборка и публикация в GitHub Releases
GH_TOKEN=$GITHUB_TOKEN npm run build -- --publish always

Конфигурация для собственного сервера обновлений

Если не хотите GitHub:

# electron-builder.yml
publish:
  provider: generic
  url: https://updates.your-domain.com/releases/

Сервер должен отдавать latest.yml и бинарники. Структура:

/releases/
  latest.yml
  latest-mac.yml
  MyApp-1.2.0-setup.exe
  MyApp-1.2.0.dmg
  MyApp-1.2.0.AppImage

Формат latest.yml:

version: 1.2.0
files:
  - url: MyApp-1.2.0-setup.exe
    sha512: <hash>
    size: 75423821
path: MyApp-1.2.0-setup.exe
sha512: <hash>
releaseDate: '2025-03-28T10:00:00.000Z'

UI компонент обновления в renderer

// renderer/components/UpdateNotification.tsx
import { useEffect, useState } from 'react';

type UpdateState =
  | { status: 'idle' }
  | { status: 'checking' }
  | { status: 'available'; version: string; releaseNotes: string }
  | { status: 'downloading'; percent: number }
  | { status: 'ready'; version: string }
  | { status: 'error'; message: string };

export function UpdateNotification() {
  const [state, setState] = useState<UpdateState>({ status: 'idle' });

  useEffect(() => {
    const unsubscribers = [
      window.electronAPI.onUpdateChecking(() => setState({ status: 'checking' })),
      window.electronAPI.onUpdateAvailable((info) =>
        setState({ status: 'available', version: info.version, releaseNotes: info.releaseNotes ?? '' })
      ),
      window.electronAPI.onUpdateProgress((p) =>
        setState({ status: 'downloading', percent: p.percent })
      ),
      window.electronAPI.onUpdateDownloaded((info) =>
        setState({ status: 'ready', version: info.version })
      ),
      window.electronAPI.onUpdateError((msg) =>
        setState({ status: 'error', message: msg })
      ),
    ];

    return () => unsubscribers.forEach(fn => fn?.());
  }, []);

  if (state.status === 'idle' || state.status === 'checking') return null;

  if (state.status === 'available') {
    return (
      <div className="update-banner">
        <span>Доступна версия {state.version}</span>
        <button onClick={() => window.electronAPI.downloadUpdate()}>
          Загрузить
        </button>
      </div>
    );
  }

  if (state.status === 'downloading') {
    return (
      <div className="update-banner">
        <span>Загрузка обновления: {state.percent}%</span>
        <div className="progress-bar" style={{ width: `${state.percent}%` }} />
      </div>
    );
  }

  if (state.status === 'ready') {
    return (
      <div className="update-banner update-ready">
        <span>Версия {state.version} загружена</span>
        <button onClick={() => window.electronAPI.installUpdate()}>
          Перезапустить и установить
        </button>
      </div>
    );
  }

  return null;
}

Tauri: встроенное автообновление

# src-tauri/Cargo.toml
[dependencies]
tauri-plugin-updater = "2"
// src-tauri/src/lib.rs
pub fn run() {
    tauri::Builder::default()
        .plugin(tauri_plugin_updater::Builder::default().build())
        .invoke_handler(tauri::generate_handler![check_for_updates])
        .run(tauri::generate_context!())
        .expect("error running app");
}

#[tauri::command]
async fn check_for_updates(app: tauri::AppHandle) -> Result<(), String> {
    let updater = app.updater().map_err(|e| e.to_string())?;
    let response = updater.check().await.map_err(|e| e.to_string())?;

    if let Some(update) = response {
        app.emit("update:available", &update.version).unwrap();

        // Скачиваем с прогрессом
        let mut downloaded = 0u64;
        update.download_and_install(
            |chunk, total| {
                downloaded += chunk as u64;
                if let Some(total) = total {
                    let percent = (downloaded * 100 / total) as u8;
                    app.emit("update:progress", percent).unwrap();
                }
            },
            || { app.emit("update:installed", ()).unwrap(); }
        ).await.map_err(|e| e.to_string())?;
    }

    Ok(())
}
// src-tauri/tauri.conf.json
{
  "plugins": {
    "updater": {
      "pubkey": "YOUR_PUBLIC_KEY",
      "endpoints": ["https://updates.your-domain.com/{{target}}/{{arch}}/{{current_version}}"]
    }
  }
}

Tauri требует подпись обновлений — нельзя установить неподписанный апдейт. Генерация ключей: tauri signer generate -w ~/.tauri/myapp.key.

Обновление без перезапуска

Полностью бесшовное обновление (без перезапуска) технически невозможно для бинарника, который работает. Но можно минимизировать неудобство:

  • Скачивать обновление в фоне, не беспокоя пользователя
  • Предлагать установку при следующем закрытии приложения (autoInstallOnAppQuit: true)
  • Показывать нотификацию «обновление готово» в трее
  • Сохранять состояние приложения перед перезапуском и восстанавливать после