Auto-update is an important feature for desktop applications, allowing users to get the latest features and fixes. Electron provides multiple auto-update solutions. This article will detail how to implement auto-update.
electron-updater Basics
electron-updater is the most commonly used Electron auto-update solution.
Installation
bashnpm install electron-updater
Basic Configuration
javascript// main.js const { app, BrowserWindow } = require('electron') const { autoUpdater } = require('electron-updater') let mainWindow app.whenReady().then(() => { createWindow() // Check for updates autoUpdater.checkForUpdatesAndNotify() }) function createWindow() { mainWindow = new BrowserWindow({ width: 800, height: 600 }) mainWindow.loadFile('index.html') }
Configuring Update Server
1. GitHub Releases (Recommended)
json// package.json { "build": { "publish": { "provider": "github", "owner": "your-username", "repo": "your-repo" } } }
javascript// main.js autoUpdater.setFeedURL({ provider: 'github', owner: 'your-username', repo: 'your-repo' })
2. Custom Server
javascript// main.js autoUpdater.setFeedURL({ url: 'https://your-server.com/updates', headers: { 'Authorization': 'Bearer your-token' } })
3. S3 Storage
json// package.json { "build": { "publish": { "provider": "s3", "bucket": "your-bucket-name", "path": 'updates' } } }
Update Event Listeners
javascript// main.js // Update available autoUpdater.on('update-available', (info) => { console.log('Update available:', info.version) sendStatusToWindow('Update available') }) // Update downloaded autoUpdater.on('update-downloaded', (info) => { console.log('Update downloaded:', info.version) sendStatusToWindow('Update downloaded') // Prompt user to restart app dialog.showMessageBox(mainWindow, { type: 'info', title: 'Update Available', message: 'A new version has been downloaded. Restart the application to apply the update.', buttons: ['Restart', 'Later'] }).then((result) => { if (result.response === 0) { autoUpdater.quitAndInstall() } }) }) // Update not available autoUpdater.on('update-not-available', (info) => { console.log('Update not available') sendStatusToWindow('Update not available') }) // Update error autoUpdater.on('error', (err) => { console.error('Update error:', err) sendStatusToWindow('Update error: ' + err.message) }) // Download progress autoUpdater.on('download-progress', (progressObj) => { let log_message = "Download speed: " + progressObj.bytesPerSecond log_message = log_message + ' - Downloaded ' + progressObj.percent + '%' log_message = log_message + ' (' + progressObj.transferred + "/" + progressObj.total + ')' sendStatusToWindow(log_message) }) function sendStatusToWindow(text) { mainWindow.webContents.send('update-status', text) }
Renderer Process Handling
javascript// renderer.js const { ipcRenderer } = require('electron') // Listen for update status ipcRenderer.on('update-status', (event, message) => { console.log('Update status:', message) updateStatusElement.textContent = message }) // Manually check for updates document.getElementById('check-update').addEventListener('click', () => { ipcRenderer.send('check-for-updates') }) // Main process handling ipcMain.on('check-for-updates', () => { autoUpdater.checkForUpdates() })
Advanced Configuration
1. Scheduled Update Checks
javascript// main.js const CHECK_UPDATE_INTERVAL = 24 * 60 * 60 * 1000 // 24 hours app.whenReady().then(() => { // Check on app startup autoUpdater.checkForUpdatesAndNotify() // Scheduled checks setInterval(() => { autoUpdater.checkForUpdates() }, CHECK_UPDATE_INTERVAL) })
2. Silent Updates
javascript// main.js autoUpdater.autoDownload = true autoUpdater.autoInstallOnAppQuit = true app.on('before-quit', () => { if (autoUpdater.isUpdateDownloaded()) { autoUpdater.quitAndInstall() } })
3. Custom Update Check
javascript// main.js async function checkForUpdates() { try { const updateCheckResult = await autoUpdater.checkForUpdates() if (updateCheckResult.updateInfo.version !== app.getVersion()) { // New version available return { hasUpdate: true, version: updateCheckResult.updateInfo.version, releaseNotes: updateCheckResult.updateInfo.releaseNotes } } else { // No new version return { hasUpdate: false } } } catch (error) { console.error('Update check failed:', error) return { hasUpdate: false, error: error.message } } }
Version Management
1. Version Number Format
json// package.json { "version": "1.0.0" }
Follow Semantic Versioning (SemVer):
- MAJOR.MINOR.PATCH
- For example: 1.0.0, 1.2.3, 2.0.0
2. Releasing New Version
bash# Update version number npm version patch # 1.0.0 -> 1.0.1 npm version minor # 1.0.0 -> 1.1.0 npm version major # 1.0.0 -> 2.0.0 # Build application npm run build # Publish to GitHub git push --follow-tags
3. Release Notes
Add release notes in GitHub Releases:
markdown## Version 1.1.0 ### New Features - Added feature X - Added feature Y ### Bug Fixes - Fixed bug A - Fixed bug B ### Improvements - Improved performance - Enhanced user experience
Security Considerations
1. Verify Update Packages
javascript// main.js autoUpdater.on('update-downloaded', (info) => { // Verify update package signature if (verifyUpdateSignature(info)) { autoUpdater.quitAndInstall() } else { console.error('Update signature verification failed') } }) function verifyUpdateSignature(info) { // Implement signature verification logic return true }
2. Use HTTPS
javascript// main.js autoUpdater.setFeedURL({ url: 'https://your-server.com/updates', headers: { 'Authorization': 'Bearer your-token' } })
3. Limit Update Frequency
javascript// main.js let lastUpdateCheck = 0 const UPDATE_CHECK_INTERVAL = 24 * 60 * 60 * 1000 // 24 hours async function checkForUpdatesWithRateLimit() { const now = Date.now() if (now - lastUpdateCheck < UPDATE_CHECK_INTERVAL) { console.log('Update check skipped - too soon') return } lastUpdateCheck = now return await autoUpdater.checkForUpdates() }
Error Handling
1. Network Errors
javascript// main.js autoUpdater.on('error', (err) => { if (err.message.includes('ERR_INTERNET_DISCONNECTED')) { console.error('Network disconnected') sendStatusToWindow('Network disconnected. Please check your internet connection.') } else if (err.message.includes('ERR_CONNECTION_REFUSED')) { console.error('Connection refused') sendStatusToWindow('Cannot connect to update server.') } else { console.error('Update error:', err) sendStatusToWindow('Update failed: ' + err.message) } })
2. Insufficient Disk Space
javascript// main.js autoUpdater.on('error', (err) => { if (err.message.includes('ENOSPC')) { console.error('No disk space') sendStatusToWindow('Not enough disk space to download update.') } })
Best Practices
1. User Experience
javascript// main.js // Check for updates when app is idle app.on('ready', () => { setTimeout(() => { autoUpdater.checkForUpdatesAndNotify() }, 5000) // Delay 5 seconds to avoid affecting startup speed }) // Provide update progress feedback autoUpdater.on('download-progress', (progress) => { const percentage = Math.round(progress.percent) sendStatusToWindow(`Downloading update: ${percentage}%`) })
2. Rollback Mechanism
javascript// main.js // Keep old version const { app } = require('electron') app.on('before-quit', () => { // Backup current version before update const currentVersion = app.getVersion() const backupPath = path.join(app.getPath('userData'), 'backup', currentVersion) // Implement backup logic })
3. Testing Updates
javascript// Development environment testing if (process.env.NODE_ENV === 'development') { autoUpdater.setFeedURL({ url: 'http://localhost:3000/updates' }) }
Common Questions
Q: How to implement incremental updates?A: electron-updater supports incremental updates by default, just ensure server configuration is correct and use the same release process.
Q: How to retry after update failure?A: Listen to error event and implement retry logic:
javascriptlet retryCount = 0 const MAX_RETRIES = 3 autoUpdater.on('error', (err) => { if (retryCount < MAX_RETRIES) { retryCount++ setTimeout(() => { autoUpdater.checkForUpdates() }, 5000 * retryCount) } })
Q: How to skip a specific version?A: Save skipped version in app settings, compare when checking for updates:
javascriptconst skippedVersion = getSkippedVersion() if (newVersion !== skippedVersion) { // Show update prompt }
Q: How to migrate user data after update?A: Listen to before-quit event in main process, execute data migration logic before update.