Hai rilasciato la build di 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 trova l'update. Un altro lo ha scaricato ma non riesce a installarlo. Un terzo sta ancora usando un vecchio binario con il flusso di autenticazione rotto, mentre i tuoi log non mostrano quasi nulla di utile.
Questa è la scomoda realtà dell' 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 ciclo di vita, dall'osservabilità, dai controlli di rollout e da un percorso di rollback. Se consideri uno qualsiasi di questi elementi come opzionale, una patch di routine può trasformarsi in un incidente improvviso.
Indice dei contenuti
- L'incidente dell'update alle 2 del mattino che ha dato vita a questa guida
- Scegliere il giusto approccio per l'updater di Electron
- Implementare il flusso di auto-update nel Main Process
- Configurare la CI/CD per release firmate e manifest
- Rollout, Channel e strategia di rollback
- Gestire l'auto-update come controllo di sicurezza
- Runbook e checklist per gli update in produzione
L'incidente dell'aggiornamento alle 2:00 che ha dato vita a questa guida
Il rilascio aveva superato la CI e sembrava tutto normale. Tarda venerdì, uno sviluppatore 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 da un build di produzione installato.
Alle 2:08, PagerDuty ha allertato l'ingegnere di turno. Un nuovo flusso di autenticazione è fallito per parte della flotta, e gli utenti che avevano ricevuto l'update non riuscivano a fare il login. Altri utenti sono rimasti 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 una spiegazione chiara.
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.
- Ispeziona la firma. Il rilascio non era firmato correttamente, quindi la verifica è fallita sulle piattaforme colpite. La firma deve bloccare la pubblicazione se manca o è non valida.
- Confronta i log dei client. Gli errori di aggiornamento non raggiungevano mai la telemetria centrale. L'applicazione ignorava l'evento e continuava a girare, lasciando il team senza prove affidabili.
- Controlla i rollout controls. Non c'erano canali interni o cohort di staging. Ogni client idoneo utilizzava lo stesso feed, quindi il guasto si è propagato senza un punto di contenimento.
- Cerca un rollback. Il team non aveva procedure testate per ripubblicare la versione precedente o per allontanare i client dalla release corrotta.
La documentazione ufficiale di Electron chiarisce i limiti della piattaforma. Linux non ha il supporto nativo per l'auto-updater, 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 della release. La documentazione di Electron autoUpdater definisce i vincoli di API, mentre il sistema di release deve imporre i controlli operativi circostanti.
Lezione postmortem: 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 il giorno lavorativo successivo a ricostruire un processo di release che avrebbe dovuto esistere prima dell'incidente.
Tratta l'auto-update come un sistema operativo. La firma è un gate di release, 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 giusto percorso 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 firma, manifest, installer e rollback. Un cambio di solo JavaScript o CSS nel renderer segue un percorso diverso. Contano anche i vincoli di hosting: un piccolo progetto ospitato su GitHub non richiede gli stessi controlli di release di un servizio di distribuzione enterprise.
Per i progetti che usano electron-builder con pubblicazione di artifact firmati, electron-updater è solitamente il default più pratico. 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 tuo team resta responsabile di firma, disponibilità del feed, policy dei channel, controlli di rollout e monitoring. L'integrazione dell'updater di Integrazione dell'updater di Electron per Capgo è rilevante quando si valuta un modello di delivery ibrido per i bundle del web layer insieme ai Native Build.
update-electron-app si adatta a team che desiderano un'integrazione leggera con le GitHub Releases. Effettua un controllo all'avvio e poi a intervalli ricorrenti, mantenendo il setup semplice ma offrendo meno flessibilità per la selezione avanzata dei canali, il rollout graduale e le regole di rollback personalizzate. Il pacchetto è adeguato per un processo di rilascio ridotto, a patto che le GitHub Releases e la loro disponibilità soddisfino i tuoi requisiti operativi.
| Opzione | Controllo Hosting | Supporto Signing | Canali & Rollout Graduali | Carico di Manutenzione |
|---|---|---|---|---|
| electron-updater | S3, GitHub, HTTPS generico e altri target di pubblicazione | Si integra con il signing del rilascio pacchettizzato | Base solida, la policy personalizzata risiede solitamente nel feed | Moderato |
| aggiornare un'app Electron | 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 | Dipende dai requisiti di firma della piattaforma | Possibile, ma richiede infrastrutture di release extra | Moderato per applicazioni legacy |
| Servizio custom | Controllo totale su manifest, autorizzazioni, cohort e feed | Tuo il design della verifica e la gestione delle chiavi | Massima flessibilità | Alto |
| Live Updates di Capgo | Delivery gestita per i bundle del web layer | Usa il suo modello di updater e delivery | Targeting del pubblico e delivery basata sui 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, audit log o regole di deployment regolamentate giustificano il costo di implementazione. Il compromesso è la gestione continua: il tuo team deve definire la semantica del manifest, proteggere le chiavi di firma, preservare la compatibilità con i client e testare download falliti, rilasci rifiutati e comportamenti di rollback.
Con i Live Updates puoi distribuire modifiche solo al renderer senza ricostruire la shell nativa. Non sostituiscono gli update binari quando cambiano Electron, i moduli nativi, i permessi o il comportamento dell'installer. Usa electron-updater se non ti serve una logica di rollout custom. Se serve una logica custom, costruiscila seguendo le convenzioni consolidate di manifest e artifact, invece di ricreare da zero il comportamento di download e differential-update. La strada più affidabile è quella che il tuo team può osservare, mettere in staging e invertire sotto pressione.
Implementare il flusso di Auto-Update nel Main Process
Il main process deve gestire il controllo e l'installazione degli update. Il renderer può mostrare lo stato, ma non deve decidere se un update eseguibile è affidabile o quando l'app deve chiudersi.
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 channel è una policy di rilascio, non un'etichetta nella UI. Ogni channel deve puntare all'artifact firmato e al manifest corretti.
Pianifica i controlli ed espone gli eventi del ciclo di vita
Chiamare checkForUpdates() solo all'avvio è un errore comune in produzione. Un utente può lasciare l'app aperta per giorni, quindi il main process necessita di un intervallo controllato e di una strategia di retry che gestisca l'operatività 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 update varia per piattaforma e setup di packaging, quindi testa dagli artifact installati invece che in modalità sviluppo. Anche la documentazione di Electron segnala problemi di timing all'avvio su Windows, incluso il caso Squirrel al primo avvio. Non triggerare un controllo update prima che l'app abbia completato l'inizializzazione specifica della piattaforma.
Informa 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.';
}
});
Limita l'installazione al consenso dell'utente in produzione, a meno che la tua app non debba riavviarsi subito. Imposta un isQuitting flag prima quitAndInstall(), altrimenti i normali handler di chiusura finestra potrebbero impedire l'avvio dell'installer.

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 si baserà su supposizioni invece che su prove concrete.
Configurare la CI/CD per Release Firmate e Manifest
La pipeline di release è l'unica fonte di verità per ciò che installano gli utenti. Un build locale che funziona sulla macchina di un dev non garantisce che il binario, il manifest, la firma e il channel pubblicati 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 invisibile ai client un binario perfettamente valido.
Rendi 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 interrompere il job, non generare un fallback non firmato che qualcuno carichi 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 delle pipeline possono trarre vantaggio dal capire quando usare insieme Jenkins e Ansible.
I comandi che spesso causano 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 internal, beta, e ultima i canali sono separati, ognuno con il proprio manifest e set di artifact firmati. La promozione deve spostare una release testata tra le policy, non sovrascrivere un file durante il download dei client.
La separazione dei canali protegge anche la produzione dalle build di test accidentali. L'updater deve sapere se un client appartiene a una cohort interna, a un pubblico beta o alla popolazione stable prima di valutare il feed.
Usa le cohort prima del rilascio su larga scala
Un campo custom nel manifest può gestire il rilascio 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 tra stati elegibili e non elegibili a ogni controllo avrà un comportamento imprevedibile, rendendo difficili i report di supporto.
Espandi la cohort solo dopo che la release ha superato la finestra di osservazione. La finestra deve riflettere i tuoi pattern di utilizzo, ma la decisione deve basarsi su segnali, non solo sul calendario. Monitora i risultati 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 |
|---|---|---|
| Gli errori di feed o di firma superano la soglia approvata dal team | Blocca il rollout | I client potrebbero non riuscire a validare o trovare il rilascio |
| I controlli post-update al lancio falliscono | Ripristina il feed | Il binario potrebbe installarsi ma fallire all'avvio |
| Le eccezioni del renderer aumentano dopo la promozione | Mantieni la cohort attuale | L'installer nativo potrebbe essere integro mentre la nuova applicazione code no |
| I segnali rientrano nel budget del rilascio | Espandi la cohort | I dati 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 del feed e un comportamento del client in grado di recuperare.
Regola operativa: Il rollback deve poter essere eseguito dall'ingegnere on-call senza dover ricostruire l'applicazione durante l'incidente.
In pratica, il runbook dovrebbe ripristinare il manifest della release precedente sul canale interessato, invalidare lo staging marker e confermare che i nuovi controlli puntino alla versione sicura. Se il problema riguarda il renderer code invece del native shell, un rollback mirato al web-layer potrebbe essere più veloce. Una piattaforma come i rollout graduali di Capgo può essere rilevante per quel layer di delivery separato, ma non deve oscurare il confine tra un rollback del binario nativo e uno del 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 una semplice funzione di comodità. La documentazione ufficiale di Electron descrive vincoli di piattaforma come macOS ATS, e la sicurezza ha documentato uno scenario del 2022 in cui attaccanti che controllavano l'infrastruttura di update potevano distribuire pacchetti malevoli che superavano comunque i controlli di firma code, come discusso nella documentazione sulla sicurezza dell'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 documentata per la rotazione delle chiavi. 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 ai build. Linux richiede una strategia specifica per la distribuzione, poiché Electron non offre un updater universale integrato.
Proteggi i metadati con la stessa cura del binario
Un binario firmato può essere associato alla release sbagliata se il canale dei metadati è compromesso o configurato male. Valuta l'aggiunta di una firma del manifest verificata tramite una chiave pubblica integrata nell'app, imposta una versione minima consentita e rifiuta i downgrade inaspettati, a meno che un percorso di recovery autorizzato non lo permetta esplicitamente.
Anche il feed richiede 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 di segreti gestito, 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.
- Richiedere un trasporto sicuro: Seguire i requisiti ATS e HTTPS rigorosi per le richieste di update.
- Monitorare 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 tooling di Electron continua ad aggiungere supporto 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 alla verifica della firma fornisce il contesto utile per progettare questo ulteriore livello di verifica.

Runbook e Checklist per l'Update in Produzione
Un rilascio è pronto solo quando un altro ingegnere può gestirlo sotto pressione. Tieni la checklist vicina al job di deployment e al canale degli incidenti.
Gate pre-rilascio
- 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 sia firmato e che la notarizzazione o la validazione equivalente sia completata.
- Contratto del manifest: Verifica che
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: Conferma che i controlli degli aggiornamenti, il download, l'installazione, l'avvio e gli errori vengano ricevuti correttamente.
Canary e rollout completo
- Controllo dei cohort: Inizia con un pubblico beta o interno molto ristretto.
- Budget di salute: Sospendi la promozione se fallimenti nel lancio, download degli update, eccezioni del renderer o errori di autenticazione superano i limiti approvati dal team.
- Approvazione promozione: Richiedi una decisione esplicita (go/no-go) prima di spostare il rilascio nel feed stabile.
- Impatto sui clienti: 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, il processo di spawning si interrompe o i download degli update falliscono, ferma subito la promozione. 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 dispositivi si stia riprendendo prima di comunicare la chiusura dell'incidente.
I comandi esatti di rollback dipendono dal tuo provider, ma la sequenza deve sempre essere documentata: riporta il manifest del canale alla versione precedente, invalida il tag di staging, invia un marker di forced-update se necessario e verifica il percorso di downgrade con 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 il Native Build per ogni modifica al renderer. Se desideri separare il rilascio dei binari nativi dalla distribuzione controllata di JavaScript e CSS, visita Capgo ed effettua una valutazione insieme alla tua attuale pipeline di rilascio Electron.