How the Modly Desktop App Checks for and Applies Updates with the Auto‑Updater

The Modly desktop client uses electron‑updater to automatically download patch updates on macOS and Windows, while prompting users manually for major or minor version upgrades.

The Modly auto-updater balances seamless background updates with user control by distinguishing between patch releases (applied silently) and feature releases (deferred to the user). This article examines the complete implementation in the lightningpixel/modly repository, from initialization through renderer notification.

Initializing the Auto‑Updater in the Main Process

When the Electron main process starts, electron/main/index.ts initializes the updater with a reference to the main window:

// electron/main/index.ts – line 108
initAutoUpdater(() => mainWindow);

This wiring enables the updater to send IPC messages to the renderer. On macOS, auto-download is disabled entirely because Modly's builds are unsigned, preventing automatic gatekeeper bypasses.

Configuring the Updater Behavior

The core configuration resides in electron/main/updater.ts (lines 19‑23):

autoUpdater.logger = createAppLogger();
autoUpdater.autoDownload = false;           // patches download only after confirmation
autoUpdater.autoInstallOnAppQuit = true;    // install when app exits normally
autoUpdater.disableWebInstaller = true;

Key design decisions:

  • autoDownload: false — Prevents immediate downloads, allowing the app to inspect version semantics first
  • autoInstallOnAppQuit: true — Ensures updates apply without forcing an immediate restart
  • Web installer disabled — Keeps all update traffic within the native auto-updater channels

How Modly Checks for Updates

The updater employs a three-trigger strategy defined in electron/main/updater.ts:

1. Startup Check (Lines 59‑62)

autoUpdater.checkForUpdates().catch(err => {
  logger.error('Initial update check failed', err);
});

Executes immediately when the app launches, ensuring users receive critical patches promptly.

2. Periodic Re‑check (Lines 64‑67)

setInterval(() => {
  autoUpdater.checkForUpdates().catch(() => {});
}, 2 * 60 * 60 * 1000); // 2 hours

Background polling catches updates published while the app remains open for extended sessions.

3. Manual Check via IPC (Lines 1437‑1443)

The renderer can force an immediate check through electron/main/ipc-handlers.ts:

ipcMain.handle('updater:check', async () => {
  const result = await autoUpdater.checkForUpdates();
  return { success: true, versionInfo: result?.updateInfo };
});

Differentiating Patch vs. Major/Minor Updates

When update-available fires, electron/main/updater.ts (lines 24‑40) performs semantic version analysis:

autoUpdater.on('update-available', (info) => {
  const current = app.getVersion();      // e.g., "1.2.3"
  const incoming = info.version;          // e.g., "1.2.4" or "1.3.0"
  
  const currentParts = current.split('.').map(Number);
  const incomingParts = incoming.split('.').map(Number);
  
  const isPatch = (
    currentParts[0] === incomingParts[0] &&  // same major
    currentParts[1] === incomingParts[1]      // same minor
  );
  
  if (isPatch) {
    autoUpdater.downloadUpdate();  // silent background download
  } else {
    mainWindow?.webContents.send('updater:major-minor-available', {
      version: incoming,
      currentVersion: current
    });
  }
});

Patch updates (1.2.3 → 1.2.4) download automatically. Major/minor updates (1.2.3 → 1.3.0) surface in the UI for explicit user action.

Downloading and Applying Patch Updates

Once a patch downloads, the update-downloaded event triggers (lines 42‑49):

autoUpdater.on('update-downloaded', (info) => {
  mainWindow?.webContents.send('updater:applying', {
    version: info.version
  });
  
  setTimeout(() => {
    autoUpdater.quitAndInstall(true, true);
    // first true = silent, second true = force run after update
  }, 800);  // brief delay for UI transition
});

The 800ms timeout allows the renderer to render an "Applying update..." panel before the app terminates.

Renderer‑Side API for Update Control

The preload script in electron/preload/electron-api.ts (lines 63‑76) exposes a typed updater object to the React frontend:

updater: {
  check: () => ipcRenderer.invoke('updater:check'),
  quitAndInstall: () => ipcRenderer.invoke('updater:quitAndInstall'),
  
  onApplying: (callback) => 
    ipcRenderer.on('updater:applying', (_e, data) => callback(data)),
  offApplying: () => 
    ipcRenderer.removeAllListeners('updater:applying'),
  
  onMajorMinorAvailable: (callback) => 
    ipcRenderer.on('updater:major-minor-available', (_e, data) => callback(data)),
  offMajorMinorAvailable: () => 
    ipcRenderer.removeAllListeners('updater:major-minor-available')
}

Triggering a Manual Check from React

import { api } from '@/electron/preload';

const checkForUpdates = async () => {
  const result = await api.updater.check();
  if (result.success) {
    console.log('Update check initiated');
  }
};

Displaying the Applying State

useEffect(() => {
  api.updater.onApplying(({ version }) => {
    setApplyingVersion(version);
    setShowApplyingPanel(true);
  });
  
  return () => api.updater.offApplying();
}, []);

Handling Major/Minor Version Notifications

useEffect(() => {
  api.updater.onMajorMinorAvailable(({ version }) => {
    openDownloadDialog(version);  // user-controlled download flow
  });
  
  return () => api.updater.offMajorMinorAvailable();
}, []);

Platform-Specific Considerations

Platform Behavior
macOS Auto-download disabled (autoDownload: false) due to unsigned builds; manual download required for all updates
Windows Full auto-updater support; patches download and install automatically
Linux Not configured in current implementation; typically distributed via package managers

Summary

  • Initialization occurs in electron/main/index.ts via initAutoUpdater(), binding the updater to the main window
  • Configuration in electron/main/updater.ts disables auto-download and enables quit-time installation
  • Three triggers check for updates: startup, 2‑hour interval, and manual IPC invocation
  • Semantic versioning differentiates patches (auto-downloaded) from major/minor releases (renderer notification)
  • Patch application uses quitAndInstall(true, true) with an 800ms UI grace period
  • Renderer API in electron/preload/electron-api.ts provides check(), onApplying(), and onMajorMinorAvailable() for complete UI control

Frequently Asked Questions

How often does Modly check for updates automatically?

The app checks immediately on startup and then every 2 hours via setInterval in electron/main/updater.ts (lines 64‑67). Users can also trigger manual checks through the updater:check IPC handler.

Why doesn't Modly auto-download updates on macOS?

macOS builds are unsigned, so autoDownload remains false to prevent Gatekeeper warnings and security prompts. Users must manually download and replace the application for macOS updates.

What happens when a major version update is available?

The main process sends updater:major-minor-available to the renderer with version details. The UI typically displays a dialog directing users to the GitHub releases page rather than applying the update automatically.

Can users cancel an update that's already downloading?

Once downloadUpdate() begins for a patch, cancellation isn't exposed through the current API. However, major/minor updates require explicit user action before any download starts, providing natural cancellation.

Where is the update server configured?

The electron-updater module reads update metadata from latest.yml (Windows) or latest-mac.yml (macOS) hosted alongside the release artifacts. The feed URL derives from the publish configuration in package.json, not hardcoded in the source files examined.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →