Babysitter electron-auto-updater-setup
Configure electron-updater with code signing verification, delta updates, staged rollouts, and multiple update channels for Electron applications
install
source · Clone the upstream repo
git clone https://github.com/a5c-ai/babysitter
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/a5c-ai/babysitter "$T" && mkdir -p ~/.claude/skills && cp -r "$T/library/specializations/desktop-development/skills/electron-auto-updater-setup" ~/.claude/skills/a5c-ai-babysitter-electron-auto-updater-setup && rm -rf "$T"
manifest:
library/specializations/desktop-development/skills/electron-auto-updater-setup/SKILL.mdsource content
electron-auto-updater-setup
Configure electron-updater for Electron applications with advanced features including code signing verification, delta updates, staged rollouts, and multiple release channels. This skill creates a complete auto-update infrastructure.
Capabilities
- Configure electron-updater with multiple providers (GitHub, S3, Generic, Spaces)
- Set up staged rollouts with percentage-based distribution
- Configure delta (differential) updates for efficient bandwidth usage
- Implement multiple release channels (stable, beta, alpha)
- Set up code signing verification for update packages
- Create update notification UI components
- Configure silent updates vs. interactive updates
- Implement rollback mechanisms
Input Schema
{ "type": "object", "properties": { "projectPath": { "type": "string", "description": "Path to the Electron project root" }, "provider": { "type": "object", "properties": { "type": { "enum": ["github", "s3", "generic", "spaces", "keygen"] }, "config": { "type": "object", "description": "Provider-specific configuration" } }, "required": ["type"] }, "channels": { "type": "array", "items": { "type": "object", "properties": { "name": { "type": "string" }, "default": { "type": "boolean" }, "allowDowngrade": { "type": "boolean" } } }, "default": [{ "name": "latest", "default": true }] }, "features": { "type": "object", "properties": { "deltaUpdates": { "type": "boolean", "default": true }, "stagedRollout": { "type": "boolean", "default": false }, "silentUpdate": { "type": "boolean", "default": false }, "autoInstallOnQuit": { "type": "boolean", "default": true }, "forceDevUpdateConfig": { "type": "boolean", "default": false } } }, "stagedRollout": { "type": "object", "properties": { "enabled": { "type": "boolean" }, "initialPercentage": { "type": "number", "default": 10 }, "incrementStep": { "type": "number", "default": 20 }, "incrementInterval": { "type": "string", "default": "24h" } } }, "ui": { "type": "object", "properties": { "generateComponents": { "type": "boolean", "default": true }, "framework": { "enum": ["react", "vue", "svelte", "vanilla"] }, "notifications": { "type": "boolean", "default": true } } } }, "required": ["projectPath", "provider"] }
Output Schema
{ "type": "object", "properties": { "success": { "type": "boolean" }, "files": { "type": "array", "items": { "type": "object", "properties": { "path": { "type": "string" }, "type": { "enum": ["config", "main", "ui", "ipc", "utils"] } } } }, "electronBuilderConfig": { "type": "object", "description": "electron-builder publish configuration to merge" }, "envVariables": { "type": "array", "items": { "type": "object", "properties": { "name": { "type": "string" }, "description": { "type": "string" }, "required": { "type": "boolean" } } } }, "testCommands": { "type": "array", "items": { "type": "string" } } }, "required": ["success", "files"] }
Generated File Structure
src/ main/ updater/ auto-updater.ts # Main updater logic update-channels.ts # Channel management staged-rollout.ts # Staged rollout logic delta-updates.ts # Delta update handling preload/ updater-api.ts # Exposed updater API renderer/ components/ UpdateNotification/ # UI components UpdateNotification.tsx UpdateProgress.tsx ReleaseNotes.tsx shared/ update-types.ts # TypeScript types
Code Templates
Main Auto-Updater Module
import { autoUpdater, UpdateInfo } from 'electron-updater'; import { app, BrowserWindow, ipcMain } from 'electron'; import log from 'electron-log'; // Configure logging autoUpdater.logger = log; autoUpdater.logger.transports.file.level = 'info'; // Disable auto download - we control the flow autoUpdater.autoDownload = false; autoUpdater.autoInstallOnAppQuit = true; export class AppUpdater { private mainWindow: BrowserWindow | null = null; constructor(mainWindow: BrowserWindow) { this.mainWindow = mainWindow; this.setupEventHandlers(); this.setupIpcHandlers(); } private setupEventHandlers() { autoUpdater.on('checking-for-update', () => { this.sendToRenderer('update-status', { status: 'checking' }); }); autoUpdater.on('update-available', (info: UpdateInfo) => { this.sendToRenderer('update-status', { status: 'available', version: info.version, releaseNotes: info.releaseNotes, releaseDate: info.releaseDate, }); }); autoUpdater.on('update-not-available', () => { this.sendToRenderer('update-status', { status: 'not-available' }); }); autoUpdater.on('download-progress', (progress) => { this.sendToRenderer('update-progress', { percent: progress.percent, bytesPerSecond: progress.bytesPerSecond, transferred: progress.transferred, total: progress.total, }); }); autoUpdater.on('update-downloaded', (info: UpdateInfo) => { this.sendToRenderer('update-status', { status: 'downloaded', version: info.version, }); }); autoUpdater.on('error', (error) => { this.sendToRenderer('update-status', { status: 'error', error: error.message, }); }); } private setupIpcHandlers() { ipcMain.handle('updater:check', async () => { return autoUpdater.checkForUpdates(); }); ipcMain.handle('updater:download', async () => { return autoUpdater.downloadUpdate(); }); ipcMain.handle('updater:install', () => { autoUpdater.quitAndInstall(false, true); }); ipcMain.handle('updater:set-channel', async (_, channel: string) => { autoUpdater.channel = channel; return autoUpdater.checkForUpdates(); }); } private sendToRenderer(channel: string, data: unknown) { this.mainWindow?.webContents.send(channel, data); } async checkForUpdates() { return autoUpdater.checkForUpdates(); } }
Staged Rollout Implementation
import { machineIdSync } from 'node-machine-id'; import crypto from 'crypto'; export class StagedRollout { private machineId: string; private rolloutPercentage: number; constructor() { this.machineId = machineIdSync(); this.rolloutPercentage = 100; // Full rollout by default } /** * Deterministically decide if this machine should receive the update * based on a hash of the machine ID */ shouldReceiveUpdate(version: string, rolloutPercentage: number): boolean { const hash = crypto .createHash('sha256') .update(`${this.machineId}:${version}`) .digest('hex'); // Convert first 8 hex chars to number (0 to 4294967295) const hashNumber = parseInt(hash.substring(0, 8), 16); // Normalize to 0-100 const bucket = (hashNumber / 0xffffffff) * 100; return bucket < rolloutPercentage; } async fetchRolloutConfig(updateServerUrl: string, version: string) { try { const response = await fetch( `${updateServerUrl}/rollout/${version}` ); const config = await response.json(); return config.percentage || 100; } catch { return 100; // Default to full rollout on error } } }
Multi-Channel Support
import { autoUpdater } from 'electron-updater'; import Store from 'electron-store'; const store = new Store(); export const UPDATE_CHANNELS = ['stable', 'beta', 'alpha'] as const; export type UpdateChannel = typeof UPDATE_CHANNELS[number]; export function getUpdateChannel(): UpdateChannel { return store.get('updateChannel', 'stable') as UpdateChannel; } export function setUpdateChannel(channel: UpdateChannel) { if (!UPDATE_CHANNELS.includes(channel)) { throw new Error(`Invalid channel: ${channel}`); } store.set('updateChannel', channel); autoUpdater.channel = channel; // Optionally check for updates immediately autoUpdater.checkForUpdates(); } export function initializeUpdateChannel() { const channel = getUpdateChannel(); autoUpdater.channel = channel; // For beta/alpha, allow downgrade if switching channels autoUpdater.allowDowngrade = channel !== 'stable'; }
Update UI Component (React)
import React, { useEffect, useState } from 'react'; interface UpdateStatus { status: 'checking' | 'available' | 'not-available' | 'downloading' | 'downloaded' | 'error'; version?: string; releaseNotes?: string; error?: string; } interface UpdateProgress { percent: number; bytesPerSecond: number; transferred: number; total: number; } export function UpdateNotification() { const [status, setStatus] = useState<UpdateStatus | null>(null); const [progress, setProgress] = useState<UpdateProgress | null>(null); useEffect(() => { const unsubStatus = window.electronAPI.on('update-status', setStatus); const unsubProgress = window.electronAPI.on('update-progress', setProgress); return () => { unsubStatus(); unsubProgress(); }; }, []); const handleDownload = () => { window.electronAPI.invoke('updater:download'); }; const handleInstall = () => { window.electronAPI.invoke('updater:install'); }; if (!status || status.status === 'not-available') return null; return ( <div className="update-notification"> {status.status === 'available' && ( <div className="update-available"> <p>Version {status.version} is available!</p> <button onClick={handleDownload}>Download</button> </div> )} {status.status === 'downloading' && progress && ( <div className="update-progress"> <p>Downloading update...</p> <progress value={progress.percent} max={100} /> <span>{Math.round(progress.percent)}%</span> </div> )} {status.status === 'downloaded' && ( <div className="update-ready"> <p>Update ready to install!</p> <button onClick={handleInstall}>Restart & Install</button> </div> )} {status.status === 'error' && ( <div className="update-error"> <p>Update error: {status.error}</p> </div> )} </div> ); }
Provider Configurations
GitHub Releases
# electron-builder.yml publish: provider: github owner: your-org repo: your-repo releaseType: release # or 'draft', 'prerelease' private: false
Amazon S3
publish: provider: s3 bucket: your-bucket region: us-east-1 acl: public-read path: /releases/${os}/${arch}
Generic Server
publish: provider: generic url: https://updates.example.com/releases channel: latest
DigitalOcean Spaces
publish: provider: spaces name: your-space region: nyc3
Environment Variables
| Variable | Description | Required |
|---|---|---|
| GitHub personal access token | For GitHub provider |
| AWS access key | For S3 provider |
| AWS secret key | For S3 provider |
| DigitalOcean Spaces key | For Spaces provider |
| DigitalOcean Spaces secret | For Spaces provider |
Testing Auto-Updates
# Test update flow locally DEBUG=electron-updater npm run dev # Force update check in development ELECTRON_FORCE_DEV_UPDATE_CONFIG=true npm run dev # Simulate specific version npm run build -- --publish never
Best Practices
- Always sign updates - Verify code signatures before installing
- Use delta updates - Reduce bandwidth for large applications
- Implement staged rollouts - Catch issues before full deployment
- Provide release notes - Users appreciate knowing what changed
- Test rollback scenarios - Ensure users can recover from bad updates
- Monitor update analytics - Track adoption rates and failures
Community References
Related Skills
- Build configurationelectron-builder-config
- Main/preload scriptselectron-main-preload-generator
- Crash reporting for update issuessentry-desktop-setup
Related Agents
- Electron best practiceselectron-architect
- Release coordinationrelease-manager