Auto-update
Vérifiez, téléchargez, validez et installez les mises à jour de votre application depuis Kotlin, sans service tiers.
Le module updater-runtime vérifie, télécharge, valide et installe les mises à jour de votre
application depuis Kotlin. Il a une moitié build-time — les métadonnées latest-*.yml générées à
côté de vos installateurs — et une moitié runtime, la classe NucleusUpdater. Le format des
métadonnées est compatible avec le format de mise à jour d'electron-builder.
Ajouter la dépendance
dependencies {
implementation("dev.nucleusframework:nucleus.updater-runtime:2.0.7")
// Optionnel, pour les réseaux d'entreprise avec une CA racine privée :
implementation("dev.nucleusframework:nucleus.native-http:2.0.7")
}Vérifier et installer une mise à jour
Créez un NucleusUpdater, interrogez le provider configuré, puis téléchargez et installez :
import dev.nucleusframework.updater.NucleusUpdater
import dev.nucleusframework.updater.UpdateResult
import dev.nucleusframework.updater.provider.GitHubProvider
import java.io.File
val updater = NucleusUpdater {
provider = GitHubProvider(owner = "myorg", repo = "myapp")
}
when (val result = updater.checkForUpdates()) {
is UpdateResult.Available -> {
var installer: File? = null
updater.downloadUpdate(result.info).collect { progress ->
println("${progress.percent.toInt()}%")
progress.file?.let { installer = it }
}
// installAndRestart lance l'installateur, quitte, puis relance
installer?.let(updater::installAndRestart)
}
UpdateResult.NotAvailable -> println("À jour")
is UpdateResult.Error -> println("Erreur : ${result.exception.message}")
}checkForUpdates() est une fonction suspend ; appelez-la depuis une coroutine.
Formats pris en charge
Le runtime met à jour les formats installés en place et laisse les formats gérés par un store à leur store.
| Plateforme | Mise à jour possible | Géré par le store |
|---|---|---|
| macOS | DMG, ZIP | PKG |
| Windows | NSIS, NSIS Web, MSI | AppX/MSIX |
| Linux | DEB, RPM, AppImage | Snap, Flatpak |
macOS a besoin d'un ZIP en plus du DMG
L'updater remplace le .app silencieusement depuis le ZIP ; le DMG ne sert qu'à l'installation
initiale. Ajoutez TargetFormat.Zip à côté de TargetFormat.Dmg pour que les deux soient dans le
même release — latest-mac.yml référence les deux.
Fonctionnement
Métadonnées
Le plugin écrit un fichier YAML par plateforme, listant chaque installateur avec son SHA-512 et sa
taille. Les releases multi-arch ou multi-plateforme ont besoin d'un seul YAML par plateforme qui
liste chaque architecture ; la CI les fusionne dans le job de release. Un latest-mac.yml
ressemble à ceci :
version: 1.2.3
files:
- url: MyApp-1.2.3-macos-arm64.dmg
sha512: VkJl1gDqcBHYbYhMb0HRI...
size: 102400000
- url: MyApp-1.2.3-macos-arm64.zip
sha512: qJ8a5gFDCwv0R2rW6lM3k...
size: 98000000
releaseDate: '2026-03-01T12:00:00.000Z'Canaux
Le tag de version pilote le canal : v1.0.0 correspond à latest, v1.0.0-beta.1 à beta, et
v1.0.0-alpha.1 à alpha. Chaque canal a son propre *-mac.yml, *.yml et *-linux.yml. Les
utilisateurs sur beta voient latest et beta ; ceux sur alpha voient les trois.
Hébergement
L'hébergement se configure dans nativeDistributions.publish { } (voir
publication) :
- GitHub Releases — le workflow de release gère tout de bout en bout.
- S3 — renseignez les credentials via des variables d'environnement ; le plugin upload à côté du YAML.
- HTTP générique — upload vous-même sur n'importe quel host statique ; l'updater récupère
<baseUrl>/latest-*.yml.
Le bloc publish { } ne fait que générer la config electron-builder — il n'upload pas. C'est la CI qui upload.
Détection après mise à jour
Après installAndRestart ou installAndQuit, l'updater écrit un fichier marqueur dans le
répertoire app-data de la plateforme (résolu depuis NucleusApp.appId). Au lancement suivant,
wasJustUpdated() retourne true et consumeUpdateEvent() livre un UpdateEvent(previousVersion, newVersion, updateLevel) que vous pouvez utiliser pour une bannière « Quoi de neuf ».
Configurer les réseaux d'entreprise
Si vos utilisateurs sont derrière un proxy d'entreprise avec une CA racine privée, le
java.net.http.HttpClient par défaut échoue au handshake TLS. Injectez un NativeHttpClient qui lit
le trust store de l'OS :
import dev.nucleusframework.nativehttp.NativeHttpClient
val updater = NucleusUpdater {
provider = GitHubProvider(owner = "myorg", repo = "myapp")
httpClient = NativeHttpClient.create()
}Voir native-ssl pour les détails du trust manager.
Mettre en place les mises à jour sans mot de passe sur Linux
Par défaut, appliquer une mise à jour DEB ou RPM passe par pkexec, qui affiche un dialogue de mot
de passe PolicyKit. Vous pouvez laisser l'app installer les mises à jour vérifiées sans mot de passe, en
conditionnant l'install à une vérification de signature.
Ça s'appuie sur la signature Linux. Activez-le sur le bloc signing :
linux {
signing {
enabled.set(true)
keyId.set("AB12CD34EF56")
silentUpdate.set(true) // nécessite enabled = true
}
}Le script post-install root du paquet met alors en place les mises à jour sans mot de passe ainsi :
- Un helper de mise à jour root-owned dans
/opt/<App>/nucleus-update-helper, et une policy polkit scopée à ce binaire précis (allow_active=yes). - Votre clé publique de signature, bundlée dans
/opt/<App>/resources/nucleus-update.pub.asc. - Au moment de la mise à jour, le runtime télécharge le paquet et sa signature détachée
<file>.asc, puis lancepkexec <helper> <file>. - Le helper vérifie la signature contre la clé bundlée, contrôle que le nom du paquet correspond à
l'app installée, et seulement alors lance
dpkg -iourpm -U.
La règle polkit allow_active=yes est scopée au seul binaire helper, et le helper refuse tout
paquet dont la signature ne valide pas contre la clé bundlée : une install sans mot de passe ne peut
donc pas installer un paquet arbitraire — il faudrait votre clé privée de signature. Si le helper ou le
<file>.asc est absent, le runtime retombe sur l'install pkexec avec prompt de mot de passe.
L'install sans mot de passe nécessite une session locale active.
Référence de l'API
Méthodes runtime
NucleusUpdater expose :
suspend fun checkForUpdates(): UpdateResult— retourneAvailable(info, level),NotAvailableouError(exception).fun downloadUpdate(info: UpdateInfo): Flow<DownloadProgress>— émet la progression ; l'émission finale afile != null.fun installAndRestart(file: File)— lance l'installateur, quitte le processus, relance après installation.fun installAndQuit(file: File)— installation silencieuse, pas de relance. La mise à jour s'applique au prochain lancement manuel.fun isUpdateSupported(): Boolean— indique si le type d'exécutable courant peut s'auto-mettre à jour.fun wasJustUpdated(): Booleanetfun consumeUpdateEvent(): UpdateEvent?— détection après mise à jour.
Types de résultat et de niveau
UpdateResult.Available(info: UpdateInfo, level: UpdateLevel)UpdateResult.NotAvailableUpdateResult.Error(exception: UpdateException)
UpdateLevel indique l'ampleur du saut : MAJOR, MINOR, PATCH ou PRE_RELEASE. Branchez votre UI
dessus — dialogue forcé pour les bumps majeurs, installation silencieuse pour les patches.
Champs du builder
NucleusUpdater { } configure un UpdaterConfig : currentVersion, channel, provider,
httpClient, allowDowngrade, allowPrerelease et executableType.
Providers
GitHubProvider(owner, repo, token? = null)GenericProvider(baseUrl)
Les en-têtes d'authentification personnalisés proviennent de authHeaders() du provider, pas de la config.
Commandes d'installation
| OS | Format | Commande |
|---|---|---|
| Linux | DEB | pkexec dpkg -i <file> |
| Linux | RPM | pkexec rpm -U <file> |
| Linux | AppImage | remplacement sur place |
| macOS | DMG/ZIP | open <file> / extraction |
| Windows | NSIS/EXE | <file> /S |
| Windows | MSI | msiexec /i <file> /passive |
Exceptions
UpdateException est le type de base. Sous-types : NetworkException, ChecksumException,
NoMatchingFileException, ParseException.
Notes
- Les SHA-512 sont vérifiés après téléchargement ; un échec supprime le fichier et remonte une erreur.
- Les mises à jour Linux sans mot de passe (
signing.silentUpdate) ajoutent une deuxième vérification, plus forte : une signature GPG validée contre la clé bundlée dans l'app. - Les tokens GitHub passent par l'en-tête
Authorization, jamais dans les paramètres d'URL. - Pour le Mac App Store, le Microsoft Store, Snapcraft et Flathub, le store gère les mises à jour ;
ignorez
updater-runtimeentièrement.
Et ensuite
- Publication — configurer les providers dans
nativeDistributions.publish { }. - CI/CD — le workflow de release qui génère et uploade les métadonnées.
- Signature de code — signature GPG des DEB/RPM et mises à jour sans mot de passe.