Hai rilasciato la build Electron, la pagina di release è online e il primo ticket di supporto arriva prima ancora che il caffè sia pronto. Un utente dice che l'app non ha trovato l'aggiornamento. Un altro lo ha scaricato ma non riesce a installarlo. Un terzo sta ancora usando un vecchio binario con un flusso di autenticazione rotto, mentre i tuoi log non mostrano quasi nulla di utile.
Questa è la scomoda realtà di auto-update per app Electron. L'updater API è solo un componente. Un rilascio in produzione dipende anche dalla firma della piattaforma, dalle policy di trasporto, dai manifest, dall'hosting, dagli eventi del lifecycle, dall'osservabilità, dai controlli di rollout e da una strategia di rollback. Considera anche solo uno di questi elementi come opzionale e una semplice patch può trasformarsi in un incidente critico improvviso.
Indice dei contenuti
- L'incidente dell'aggiornamento delle 2:00 che ha dato vita a questa guida
- Scegliere il percorso giusto per l'updater di Electron
- Implementare il flusso di auto-update nel Main Process
- Configurare la CI/CD per rilasci firmati e manifest
- Rollout, Channel e strategia di rollback
- Considerare l'Auto-Update come un controllo di sicurezza
- Runbook e checklist per gli update in produzione
L'incidente delle 2:00 che ha dato vita a questa guida
Il rilascio aveva superato la CI e sembrava tutto normale. Venerdì sera, un developer ha pushato un build Electron non firmato, e il job di publish ha caricato abbastanza asset da far apparire il rilascio completo. L'app si avviava in testing, ma nessuno aveva testato il percorso di update su un build di produzione installato.
Alle 2:08, PagerDuty ha allertato l'ingegnere di turno. Un nuovo flusso di autenticazione falliva per parte della flotta, e gli utenti che avevano ricevuto l'update non riuscivano a fare il login. Altri utenti restavano sulla versione precedente perché l'updater non riusciva a verificare o installare l'artifact. Alcuni clienti avevano un rilascio corrotto, mentre il resto della flotta eseguiva una versione diversa senza spiegazioni chiare.
L'indagine ha seguito cinque passaggi:
- Controlla il feed del rilascio. Il binario esisteva, ma i metadati attesi non stabilivano chiaramente quali client dovessero riceverlo. Un manifest è un contratto tra la release pipeline e i client installati, non un dettaglio opzionale dell'upload.
- Controlla la firma. Il rilascio non era firmato correttamente, quindi la verifica è fallita sulle piattaforme interessate. La firma deve bloccare la pubblicazione se mancante o non valida.
- Confronta i log del client. Gli errori di aggiornamento non sono mai arrivati alla telemetria centrale. L'applicazione ha ignorato l'evento continuando a girare, lasciando il team senza prove affidabili.
- Verifica i controlli di rollout. Non c'erano canali interni o cohort graduali. Ogni client idoneo ha usato lo stesso feed, così il fallimento si è diffuso senza un punto di contenimento.
- Cerca un rollback. Il team non aveva una procedura testata per ripubblicare la versione precedente o per allontanare i client dal rilascio corrotto.
La documentazione ufficiale di Electron chiarisce i limiti della piattaforma. Linux non ha supporto nativo per l'auto-update, e le richieste di aggiornamento su macOS devono soddisfare i requisiti di App Transport Security. La documentazione identifica inoltre la firma come prerequisito per aggiornamenti macOS affidabili e per la verifica del rilascio. La documentazione di Electron autoUpdater definisce i vincoli di API, mentre il sistema di rilascio deve garantire i controlli operativi necessari.
Lezione post-mortem: Un updater che non sa spiegare cosa è successo è solo un tentativo di installazione remota senza telemetria.
Il costo è andato oltre il tempo di engineering. I clienti hanno perso fiducia nel client desktop, il supporto ha dovuto spiegare comportamenti inconsistenti e il team ha passato l'intera giornata lavorativa successiva a ricostruire un processo di rilascio che avrebbe dovuto esistere prima dell'incidente.
Tratta l'auto-update come un sistema operativo. La firma è un gate di rilascio, i manifest definiscono il contratto con il client, i rollout channel limitano l'esposizione e il rollback deve essere un percorso testato, non un'invenzione d'emergenza.
Scegliere il percorso giusto per l'updater di Electron
Alle 2 del mattino, una scelta errata dell'updater diventa un problema operativo. Un aggiornamento di un binario nativo deve gestire firme, manifest, installer e rollback. Una modifica solo JavaScript o CSS del renderer segue un percorso diverso. Anche i vincoli di hosting contano: un piccolo progetto ospitato su GitHub non richiede gli stessi controlli di rilascio di un servizio di distribuzione enterprise.
Per i progetti che usano electron-builder con pubblicazione di artifact firmati, electron-updater è solitamente la scelta predefinita più pratica. Il suo ecosistema copre i target di pubblicazione, i manifest di release, il download degli artifact e l'installazione al prossimo avvio. Supporta diversi modelli di hosting, ma il controllo su firma, disponibilità del feed, policy dei canali, rollout e monitoraggio resta in mano al tuo team. L'integrazione di Integrazione dell'updater di Electron per Capgo è rilevante quando si valuta un modello di delivery ibrido per i bundle del web layer insieme alle release native.
update-electron-app si adatta a team che cercano un'integrazione leggera con le release di GitHub. Effettua il controllo all'avvio e poi a intervalli regolari, mantenendo il setup semplice ma offrendo meno flessibilità per la selezione avanzata dei canali, il traffico graduale e le regole di rollback personalizzate. Il pacchetto è ideale per processi di release contenuti, a patto che le release di GitHub e la sua disponibilità soddisfino i tuoi requisiti operativi.
| Opzione | Controllo dell'Hosting | Supporto alla Firma | Canali e Rollout Graduali | Carico di Manutenzione |
|---|---|---|---|---|
| electron-updater | S3, GitHub, HTTPS generico e altri target di pubblicazione | Si integra con la firma dei release pacchettizzati | Base solida, la policy custom di solito risiede nel feed | Moderato |
| update-electron-app | Principalmente semplici workflow GitHub Releases | Utilizza il modello di firma Electron sottostante | Limitato, a meno di aggiungere servizi esterni | Basso |
| Squirrel.Windows o Squirrel.Mac | Flusso di distribuzione orientato alla piattaforma | Si basa sui requisiti di firma della piattaforma | Possibile, ma richiede solitamente infrastrutture di release extra | Moderata per applicazioni legacy |
| Servizio personalizzato | Controllo totale su manifest, autorizzazioni, cohort e feed | Progettazione della verifica e gestione delle chiavi a tuo carico | Massima flessibilità | Alto |
| Live updates di Capgo | Delivery gestita per bundle web-layer | Usa il proprio updater e modello di delivery | Targeting del pubblico e delivery basata su canali | Modello operativo separato dagli update del binario nativo |
Un servizio custom come Hazel, Nuts o un feed interno è la scelta giusta quando autorizzazione al rilascio, targeting dei tenant, record di audit o regole di deployment regolamentate giustificano il costo di implementazione. Il compromesso è la gestione continua: il tuo team deve definire la semantica dei manifest, proteggere le chiavi di firma, garantire la compatibilità con i client e testare download non riusciti, rilasci rifiutati e comportamento di rollback.
Con i live update puoi distribuire modifiche solo al renderer senza ricostruire la shell nativa. Non sostituiscono gli aggiornamenti binari quando cambiano Electron, i moduli nativi, i permessi o il comportamento dell'installer. Use electron-updater unless you need custom rollout logic. Se serve una logica custom, costruisci sulle convenzioni consolidate di manifest e artifact invece di reinventare download e differential update. Un percorso affidabile è quello che il tuo team può osservare, mettere in staging e annullare sotto pressione.
Implementare l'auto-update flow nel main process
Il main process dovrebbe occuparsi dei controlli di aggiornamento e dell'installazione. Il renderer può mostrare lo stato, ma non dovrebbe decidere se un aggiornamento dell'eseguibile è attendibile o quando l'applicazione deve terminare.
Configura prima il target di pubblicazione
Una configurazione minima di electron-builder potrebbe essere questa:
{
"build": {
"appId": "com.example.desktop",
"publish": [
{
"provider": "s3",
"bucket": "example-electron-releases",
"channel": "stable"
}
],
"nsis": {
"oneClick": false,
"allowToChangeInstallationDirectory": true
}
}
}
Mantieni separati i feed beta e stable. Un canale è una policy di rilascio, non un'etichetta nella UI. Ogni canale deve puntare all'artifact firmato e al manifest corretti.
Pianifica i controlli ed esponi gli eventi del ciclo di vita
In chiamata checkForUpdates() Controllare solo all'avvio è un errore comune in produzione. Un utente può lasciare l'app aperta per giorni, quindi il main process ha bisogno di un intervallo controllato e di una strategia di retry che rispetti il funzionamento offline.
const { app, BrowserWindow, ipcMain } = require('electron');
const { autoUpdater } = require('electron-updater');
let mainWindow;
let isQuitting = false;
let retryDelay = 60 * 1000;
function sendUpdateStatus(status, payload = {}) {
if (mainWindow && !mainWindow.isDestroyed()) {
mainWindow.webContents.send('update-status', { status, ...payload });
}
}
function scheduleUpdateCheck() {
setTimeout(async () => {
try {
await autoUpdater.checkForUpdates();
retryDelay = 60 * 1000;
} catch (error) {
sendUpdateStatus('error', { message: error.message });
retryDelay = Math.min(retryDelay * 2, 30 * 60 * 1000);
}
scheduleUpdateCheck();
}, retryDelay);
}
app.whenReady().then(() => {
mainWindow = new BrowserWindow({
webPreferences: {
preload: require('path').join(__dirname, 'preload.js')
}
});
autoUpdater.autoDownload = true;
autoUpdater.autoInstallOnAppQuit = false;
autoUpdater.on('checking-for-update', () => {
sendUpdateStatus('checking');
});
autoUpdater.on('update-available', info => {
sendUpdateStatus('available', { version: info.version });
});
autoUpdater.on('download-progress', progress => {
sendUpdateStatus('progress', { percent: progress.percent });
});
autoUpdater.on('update-downloaded', info => {
sendUpdateStatus('downloaded', { version: info.version });
});
autoUpdater.on('error', error => {
sendUpdateStatus('error', { message: error.message });
});
autoUpdater.checkForUpdates().catch(error => {
sendUpdateStatus('error', { message: error.message });
});
scheduleUpdateCheck();
});
ipcMain.handle('install-update', () => {
isQuitting = true;
autoUpdater.quitAndInstall(false, true);
});
app.on('before-quit', event => {
if (!isQuitting) {
return;
}
});
Il comportamento esatto dell'evento di aggiornamento varia in base alla piattaforma e alla configurazione di packaging; testa quindi sugli artefatti installati invece che in modalità sviluppo. Anche la documentazione di Electron segnala problemi di timing all'avvio su Windows, incluso il caso del primo avvio con Squirrel. Non avviare il controllo degli aggiornamenti prima che l'app abbia completato l'inizializzazione specifica della piattaforma.
Mantieni informato il renderer senza bloccare il lavoro
Il preload bridge dovrebbe esporre un API limitato:
const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('updates', {
onStatus(callback) {
ipcRenderer.on('update-status', (_event, status) => callback(status));
},
install() {
return ipcRenderer.invoke('install-update');
}
});
Una barra di progresso lato renderer può rimanere volutamente semplice:
window.updates.onStatus(status => {
const progress = document.querySelector('#update-progress');
const message = document.querySelector('#update-message');
if (status.status === 'progress') {
progress.hidden = false;
progress.value = status.percent;
message.textContent = `Downloading update, ${Math.round(status.percent)}%`;
}
if (status.status === 'downloaded') {
message.textContent = `Version ${status.version} is ready to install`;
}
if (status.status === 'error') {
message.textContent = 'The update could not be downloaded. We will retry later.';
}
});
In produzione, richiedi il consenso dell'utente prima dell'installazione, a meno che l'app non debba riavviarsi immediatamente. Imposta un isQuitting flag prima quitAndInstall(), altrimenti i normali handler di chiusura della finestra potrebbero impedire all'installer di prendere il controllo.

Due fallimenti richiedono test espliciti. Primo, un client già in esecuzione deve chiamare checkForUpdates() a intervalli regolari, non solo all'avvio. Secondo, l'evento error deve arrivare ai log e alla telemetria. Se l'app lo ignora senza segnalarlo, il tuo workflow di troubleshooting dell'app inizia con supposizioni invece che con prove concrete.
Configurare la CI/CD per release firmate e manifest
La pipeline di release è la fonte di verità per ciò che installano gli utenti. Un build locale che funziona sulla macchina di un developer non garantisce che il binario pubblicato, il manifest, la firma e il channel descrivano la stessa release.
Il modello di publish di electron-builder prevede che i metadati della release e l'update target viaggino insieme. In molte configurazioni, ciò significa un artifact come latest.yml per Windows e latest-mac.yml per macOS, insieme ai pacchetti specifici della piattaforma e ai file blockmap. Un manifest mancante può rendere un binario perfettamente valido invisibile ai client.
Rendere esplicita la firma nella CI
Un pattern semplificato per GitHub Actions è questo:
name: release
on:
push:
tags:
- "v*"
jobs:
build:
strategy:
matrix:
os: [macos-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 24
cache: npm
- run: npm ci
- run: npm run test
- run: npm run build
- name: Build and publish
shell: bash
env:
CSC_LINK: ${{ secrets.CSC_LINK }}
CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }}
WIN_CSC_LINK: ${{ secrets.WIN_CSC_LINK }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
run: npx electron-builder --publish always
Usa secret specifici per piattaforma e tieni i materiali di firma fuori dal repository. Un errore di firma deve bloccare il job, non generare un fallback non firmato che qualcuno carica manualmente.
| Variabile | Scopo |
|---|---|
CSC_LINK |
certificato macOS o riferimento al certificato |
CSC_KEY_PASSWORD |
Password per il materiale di firma macOS |
WIN_CSC_LINK |
Certificato Windows o riferimento al certificato |
AWS_ACCESS_KEY_ID |
Credenziali di pubblicazione con accesso limitato |
AWS_SECRET_ACCESS_KEY |
Secret associato alle credenziali di pubblicazione |
La configurazione di pubblicazione deve identificare provider e channel in modo coerente:
{
"build": {
"publish": {
"provider": "s3",
"bucket": "example-electron-releases",
"channel": "stable",
"publishAutoUpdate": true,
"updaterCacheDirName": "example-desktop-updater"
}
}
}
Prima della pubblicazione, blocca il job in base alla versione del pacchetto, al tag, al commit SHA e al risultato dello smoke-test. Dopo la pubblicazione, verifica che il feed contenga il manifest previsto e che il manifest punti all'artifact esatto generato da quel job. La continuous integration setup guide è utile quando si formalizzano questi controlli; anche i team che confrontano l'orchestrazione della pipeline possono trarre vantaggio dal capire quando usare insieme Jenkins e Ansible.
I comandi che comunemente espongono un rilascio incompleto sono intenzionalmente banali:
npx electron-builder --publish never
test -f dist/latest.yml
test -f dist/latest-mac.yml
find dist -name "*.blockmap" -print
Questi controlli non sostituiscono un test di installazione firmata. Servono però a intercettare l'errore operativo di caricare un binario senza i metadati necessari ai client per scoprirlo.
Rollout, Channel e Strategia di Rollback
Un feed di rilascio dovrebbe comportarsi più come un target di deployment che come una cartella di download. Mantieni interno, beta, e più recente i canali separati, ognuno con il proprio manifest e un set di artifact firmati. La promozione deve spostare un rilascio testato tra diverse policy, senza sovrascrivere un file mentre i client lo stanno scaricando.
La separazione dei canali protegge anche la produzione dai build di test accidentali. L'updater deve sapere se un client appartiene a una cohort interna, a un pubblico beta o alla versione stable prima di valutare il feed.
Usa le cohort prima di un rilascio su larga scala
Un campo custom nel manifest può gestire la distribuzione graduale:
version: 4.8.0
path: Example-Setup-4.8.0.exe
sha512: signed-artifact-hash
rolloutPercentage: 10
Il main process può assegnare un bucket stabile per utente, per poi confrontarlo con rolloutPercentage. L'assegnazione stabile è fondamentale. Un utente che passa continuamente tra stati elegibili e non elegibili causerà comportamenti imprevedibili, rendendo difficili i report di supporto.
Espandi la cohort solo dopo che il rilascio ha superato la finestra di osservazione. La finestra deve riflettere i tuoi pattern di utilizzo, ma la decisione deve basarsi su segnali reali, non solo sul calendario. Monitora gli esiti degli update-check, il completamento dei download, la salute al lancio, i crash, le eccezioni del renderer e il successo dell'autenticazione.
| Segnale | Azione | Motivo |
|---|---|---|
| Errori di feed o di firma oltre la soglia approvata | Blocca il rollout | I client potrebbero non validare o trovare il rilascio |
| Check post-update falliti | Ripristina il feed | Il binario si installa ma fallisce all'avvio |
| Aumento eccezioni renderer dopo la promozione | Mantieni l'attuale cohort | L'installer nativo potrebbe funzionare correttamente, mentre la nuova applicazione code no |
| I segnali rimangono nel budget di rilascio | Espandi la coorte | Le prove supportano un'esposizione più ampia |
Non confondere il rollback con l'eliminazione di un artifact. I client esistenti potrebbero avere metadati in cache e alcuni potrebbero già eseguire la versione errata. Un piano di rollback richiede un rilascio firmato precedente, un cambio di feed e un comportamento del client in grado di ripristinarsi.
Regola operativa: Il rollback deve essere eseguibile dall'ingegnere on-call senza dover ricostruire l'applicazione durante l'incidente.
In pratica, il runbook dovrebbe ripristinare il manifest del rilascio precedente sul canale interessato, invalidare il marker di staging e confermare che i nuovi controlli puntino alla versione sicura. Se il problema riguarda il renderer code invece della shell nativa, un rollback mirato al web-layer potrebbe essere più veloce. Una piattaforma come rollout graduali di Capgo può essere rilevante per quel layer di delivery separato, ma non deve oscurare il confine tra un rollback di un binario nativo e un rollback di un web-bundle.
Trattare l'Auto-Update come un controllo di sicurezza
Un updater di Electron scarica l'eseguibile code e può installarlo con pochissima interazione da parte dell'utente. Questo rende il percorso di aggiornamento un perimetro di sicurezza, non è solo una comodità. La documentazione ufficiale di Electron descrive vincoli di piattaforma come macOS ATS, e la ricerca sulla sicurezza ha documentato uno scenario del 2022 in cui attaccanti che controllavano l'infrastruttura di aggiornamento potevano distribuire pacchetti malevoli che superavano comunque i controlli di firma di code, come discusso nella Documentazione sulla sicurezza degli auto-update di Electron Builder.
La firma di Code resta fondamentale, ma non è l'intero modello di fiducia. Firma ogni release, verifica il certificato e l'identità durante la CI e mantieni una procedura di rotazione delle chiavi documentata. Su macOS, combina la firma con la notarizzazione e l'hardened runtime adatto alla tua app. Su Windows, rendi auditabili la proprietà del certificato, il rinnovo e l'accesso alle build. Linux richiede una strategia specifica per distribuzione, poiché Electron non fornisce un updater universale integrato.
Proteggi i metadati con la stessa cura del binario
Un binario firmato può comunque essere associato alla release sbagliata se il canale dei metadati è compromesso o configurato male. Valuta di aggiungere una firma del manifest verificata tramite una chiave pubblica incorporata nell'app, imponi una versione minima consentita e rifiuta i downgrade inaspettati, a meno che un percorso di ripristino autorizzato non lo consenta esplicitamente.
Anche il feed merita controlli di produzione:
- Limita l'accesso alla pubblicazione: Assegna alla CI solo i permessi necessari per pubblicare gli asset della release.
- Proteggi i segreti di firma: Conserva certificati e chiavi private in uno storage gestito di segreti, non nei file del repository.
- Blocca le dipendenze: Blocca Electron, electron-builder e le dipendenze transitive nella CI.
- Revisiona gli artifact: Scansiona i pacchetti generati e confrontali con il commit e la versione previsti.
- Richiedi un trasporto sicuro: Segui i requisiti ATS e HTTPS rigorosi per le richieste di update.
- Monitora i fallimenti di verifica: Tratta i ripetuti errori di firma o del manifest come eventi di sicurezza, non come semplice rumore di rete.
L'ecosistema di tool di Electron continua a espandere la copertura per packaging e updater, ma la manutenzione non elimina la necessità di un threat modeling. L'obiettivo pratico è garantire che un attaccante che compromette un bucket, una CDN o una fase di build non possa comunque far accettare un rilascio non autorizzato al client. La guida sulla verifica della firma fornisce un contesto utile per progettare questo ulteriore livello di verifica.

Runbook e Checklist per l'Update in Produzione
Una release è pronta solo quando un altro ingegnere può gestirla sotto pressione. Tieni la checklist vicina al job di deployment e al canale degli incidenti.
Gate di pre-release
- Identità della versione: Verifica che la versione del pacchetto, il tag di release, il commit SHA e il changelog coincidano.
- Firma: Verifica che ogni artifact di piattaforma sia firmato e che la notarizzazione o una validazione equivalente sia completata.
- Contratto del manifest: Verifica
latest.yml,latest-mac.yml, hash, percorsi e blockmap corrispondono agli artifact caricati. - Sicurezza del canale: Pubblica nel feed interno o beta prima di promuovere il canale di release.
- Telemetria: Verifica che i controlli di aggiornamento, il download, l'installazione, il lancio e gli errori vengano ricevuti.
Canary e rollout completo
- Controllo dei cohort: Inizia con un pubblico interno o beta volutamente ridotto.
- Budget di salute: Blocca il rollout se i fallimenti di lancio, download, eccezioni del renderer o errori di autenticazione superano i limiti stabiliti.
- Approvazione del rollout: Richiedi una decisione esplicita (go/no-go) prima di spostare il rilascio nel feed stabile.
- Impatto sugli utenti: Prepara i messaggi di supporto prima della distribuzione su larga scala, non dopo il primo incidente.
Gestione degli incidenti
Se il nuovo binario non si avvia, i processi si bloccano o i download si interrompono, ferma subito il rollout. Ripristina il precedente manifest firmato, invalida il marker di staging e verifica che i nuovi client puntino alla versione precedente. Conferma tramite telemetria che il parco macchine si stia riprendendo prima di chiudere l'incidente.
I comandi esatti per il rollback dipendono dal provider, ma la sequenza deve sempre essere documentata: cambia il manifest del canale alla versione precedente, invalida il tag di staging, pusha un marker di forced-update se il ripristino lo richiede e verifica il percorso di downgrade con la telemetria live. Un rollback non testato è solo una speranza.

Capgo offre un updater per Electron per distribuire modifiche firmate al web layer, canali mirati, controlli di rollout e osservabilità degli aggiornamenti, senza dover ricostruire la shell nativa per ogni modifica al renderer. Se vuoi separare il rilascio dei binari nativi dalla distribuzione controllata di JavaScript e CSS, visita Capgo e valutalo insieme alla tua attuale pipeline di rilascio Electron.