Comment distribuer une application de bureau développée en solo sans avertissement de sécurité
TuBrief 편집팀
2026년 7월 15일
0
Computing/Software원본 영상을 바탕으로 AI의 도움을 받아 작성했습니다. 원본 영상이 기준입니다.
커뮤니티의 다른 글
댓글 (0)
Log in to leave a comment
아직 작성된 글이 없습니다
원본 영상을 바탕으로 AI의 도움을 받아 작성했습니다. 원본 영상이 기준입니다.
Log in to leave a comment
아직 작성된 글이 없습니다
Une fois votre application de bureau terminée et fonctionnelle en local, vous n'avez fait que la moitié du chemin. Le véritable défi commence au moment où l'utilisateur clique sur le lien de téléchargement et voit apparaître des fenêtres d'avertissement rouges telles que "Fichier endommagé" ou "Votre ordinateur a été protégé".
Surmonter les barrières de sécurité des systèmes d'exploitation, contrôler la vitesse de build et mettre en place un système de mise à jour automatique pour que l'utilisateur dispose toujours de la dernière version à chaque lancement est plus laborieux qu'il n'y paraît. Même si vous avez choisi Tauri v2 parce qu'il est plus léger qu'Electron, les problèmes concrets liés à la distribution demeurent. Voici un guide pour les développeurs solo ou les petites équipes pour distribuer leurs produits proprement, sans gaspiller de temps ni d'argent inutilement.
Une application de bureau sans signature de code (Code Signing) est traitée comme un logiciel malveillant au niveau du système d'exploitation. Pour éviter les avertissements de sécurité sur le PC de l'utilisateur, il faut dépenser de l'argent et gérer de la paperasse.
Pour distribuer sur macOS, l'adhésion au programme Apple Developer (99 $ par an) est indispensable. Une fois le compte obtenu, vous devez créer un fichier src-tauri/Entitlements.plist définissant les autorisations d'exception de sécurité mémoire pour permettre à la WebView de Tauri de fonctionner correctement. Si ce paramètre est manquant, l'application plantera immédiatement au lancement.
`xml
com.apple.security.cs.allow-jit
com.apple.security.cs.allow-unsigned-executable-memory
`
Spécifiez ce fichier dans les options de bundle de src-tauri/tauri.conf.json.
`json
{
"bundle": {
"macOS": {
"signingIdentity": "Developer ID Application: Your Name (TEAMID)",
"entitlements": "./Entitlements.plist",
"minimumSystemVersion": "11.0",
"dmg": {
"appPosition": { "x": 180, "y": 170 },
"applicationFolderPosition": { "x": 480, "y": 170 }
}
}
}
}
`
Pour passer le filtre SmartScreen de Windows, il fallait autrefois obtenir un certificat EV (Extended Validation) sous forme de jeton USB physique pour 400 à 700 dollars par an. C'est non seulement coûteux, mais aussi trop fastidieux à gérer pour un particulier.
L'alternative est Azure Trusted Signing (ATS), le service de signature basé sur le cloud de Microsoft. Pour un coût d'abonnement d'environ 9,99 $ par mois, la signature est traitée au sein du cloud HSM géré par Microsoft, éliminant le besoin de conserver une clé physique.
AZURE_TENANT_ID, CLIENT_ID, CLIENT_SECRET) et les informations ATS dans les variables d'environnement (Secrets) de GitHub Actions.sign-tool pendant le processus de build pour appliquer une signature numérique aux fichiers MSI ou EXE compilés par Tauri.Une application ainsi signée évite l'avertissement Windows SmartScreen dès le premier téléchargement, ce qui permet de retenir les utilisateurs qui auraient pu abandonner lors de l'installation.
Tauri est léger, mais le processus de build doit compiler l'intégralité du compilateur Rust et les chaînes d'outils natives de chaque système d'exploitation. Même si cela fonctionne bien sur votre ordinateur, il est courant de rencontrer des erreurs de lien sur l'ordinateur d'un autre membre de l'équipe, ou que la build soit corrompue à cause de la pollution des dépendances de l'environnement local. Pour une distribution sécurisée, le build de déploiement doit impérativement être effectué dans un pipeline CI/CD isolé.
Le problème est que les runners hébergés par défaut sur GitHub manquent de puissance pour compiler Rust. Si la structure consiste à télécharger les dépendances et à compiler à chaque fois, une build de release peut facilement dépasser 10 minutes.
Dans ce cas, au lieu de actions/cache qui compresse et décompresse simplement les fichiers, combinez un plugin de cache dédié fonctionnant sur un stockage NVMe haute performance (swatinem/rust-cache) ou des runners dédiés (Namespace, Depot, etc.) pour changer la donne.
Basé sur les journaux de build du projet open source de lecteur de musique spotify-player, voici la comparaison des performances entre un runner GitHub standard et un runner dédié avec cache de volume local :
| Plateforme et configuration du cache | Durée avec runner GitHub standard | Durée avec optimisation du cache | Taux de réduction du temps de build |
|---|---|---|---|
| Ubuntu Linux | 9 min 31 s | 34 s | 94,0 % de réduction |
| macOS Darwin | 9 min 31 s | 27 s | 95,2 % de réduction |
| Windows MSVC | 9 min 31 s | 44 s | 92,2 % de réduction |
| Coût du workflow | 0,44 $ par exécution vert{} 0,074 $ par exécution | 83,1 % d'économie |
La simple intégration d'une infrastructure de cache de volume persistant réduit le temps d'attente de build de votre équipe de développement d'au moins 40 %.
Configurez l'automatisation du déploiement dans .github/workflows/publish.yml comme suit :
`yaml
jobs:
build-binaries:
strategy:
matrix:
platform: [macos-latest, windows-latest]
runs-on: ${{ matrix.platform }}
# ... Après le processus de build, appelez tauri-action
`
En plaçant tauri-apps/tauri-action à la toute fin du workflow, des installateurs signés pour les deux systèmes d'exploitation sont automatiquement enregistrés dans le brouillon de GitHub Release à chaque fois que vous poussez une nouvelle balise.
Lors de l'empaquetage de l'installateur Windows, vous devez décider de la méthode d'installation du moteur WebView, WebView2. Si la connexion Internet est garantie et que vous devez réduire au maximum la taille du téléchargement, la méthode downloadBootstrapper, qui n'augmente pas la taille du bundle, est appropriée. À l'inverse, si vous ciblez des réseaux fermés ou des environnements hors ligne, il est plus sûr d'inclure offlineInstaller, même si cela ajoute environ 127 Mo au fichier d'installation.
La façon dont vous gérez les données lors de l'exploitation d'une application basée sur Tauri est également importante. Il est risqué de stocker aveuglément des données dans le stockage du navigateur, comme IndexedDB ou LocalStorage.
En fait, lors du passage de Tauri v1 à v2, il y a eu un changement interne dans le schéma de domaine WebView sous Windows, passant de [https://tauri.localhost](https://tauri.localhost) à [http://tauri.localhost](http://tauri.localhost). À cause de cela, le chemin du cache du navigateur a été modifié de force, entraînant la perte de toutes les données existantes.
Pour éviter une catastrophe où les données sont réinitialisées après le déploiement, les informations clés doivent être stockées directement dans un fichier SQLite dans la zone du système de fichiers natif, au lieu d'utiliser le stockage WebView. L'API appDataDir de Tauri v2 trouve automatiquement un chemin de sandboxing sécurisé conforme aux normes du système d'exploitation.
C:\Users\<UserName>\AppData\Roaming\<BundleIdentifier>/Users/<UserName>/Library/Application Support/<BundleIdentifier>Voici un exemple d'interception du cycle de vie de l'application dans le code Rust (src-tauri/src/lib.rs), qui constitue le backend de Tauri v2, pour lier la base de données SQLite à une zone sécurisée et exécuter une migration de schéma :
`rust
use std::fs;
use tauri::Manager;
use tauri_plugin_sql::{Migration, MigrationKind};
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
let database_migrations = vec![
Migration {
version: 1,
description: "initialize_user_profiles_table",
sql: "CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
email TEXT NOT NULL UNIQUE
);",
kind: MigrationKind::Up,
}
];
tauri::Builder::default()
.setup(|app| {
let local_app_dir = app.path().app_data_dir()
.expect("Critical: Could not resolve target operating system app data path.");
if !local_app_dir.exists() {
fs::create_dir_all(&local_app_dir)
.expect("Critical: Failed to establish persistent storage directory structure.");
}
Ok(())
})
.plugin(
tauri_plugin_sql::Builder::default()
.add_migrations("sqlite:users.db", database_migrations)
.build()
)
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
`
Avec cette configuration, même si le cache interne d'Electron ou de Chromium WebView est entièrement effacé suite à une mise à jour automatique ou une réinstallation, la base de données réelle de l'utilisateur est conservée en toute sécurité.
Induire l'utilisateur à venir sur le site web pour télécharger à chaque fois une nouvelle version augmente le taux d'abandon. Vous devez établir une structure qui sert silencieusement les fichiers de mise à jour en combinant le stockage d'objets cloud et un CDN.
Pour le serveur de distribution, la combinaison de Cloudflare R2 et d'AWS CloudFront est efficace. Cloudflare R2 ne facture pas de frais de transfert de données (Egress Fees), ce qui permet de réduire à zéro les coûts de trafic réseau générés lors de la publication de fichiers de mise à jour volumineux.
Le fichier de métadonnées (latest.json) que le client vérifie pour savoir si une nouvelle version est disponible ne doit pas être mis en cache par le CDN ou le navigateur. Vous devez spécifier la politique suivante dans les en-têtes de réponse :
`http
Cache-Control: no-cache, no-store, must-revalidate
`
En revanche, comme les fichiers binaires d'installation réels sont immuables (contiennent un hash unique), configurez-les pour qu'ils soient conservés aussi longtemps que possible par le CDN afin de réduire la charge de trafic sur le serveur d'origine :
`http
Cache-Control: public, max-age=31536000, immutable
`
Dans Tauri v2, l'emplacement des options liées à la mise à jour a été déplacé sous le bloc plugins.updater. Voici les spécifications de configuration pour tauri.conf.json :
`json
{
"bundle": {
"createUpdaterArtifacts": true
},
"plugins": {
"updater": {
"active": true,
"endpoints": [
"https://cdn.myapp.com/releases/latest.json"
],
"dialog": false,
"pubkey": "dW5zaWduZWQgYm91bmRmaXg...",
"windows": {
"installMode": "passive"
}
}
}
}
`
Pour faire en sorte qu'un utilisateur sous Windows puisse mettre à jour sans cliquer sur des fenêtres de confirmation gênantes, vous devez régler installMode sur passive ou quiet. Le mode passive affiche une barre de progression discrète au lieu de la fenêtre de l'assistant d'installation, puis termine le remplacement silencieusement.
Une fois la configuration terminée, intégrez @tauri-apps/plugin-updater et @tauri-apps/plugin-process dans le frontend pour vérifier les nouveaux correctifs lors du lancement de l'application et forcer le redémarrage.
`typescript
import { check } from "@tauri-apps/plugin-updater";
import { ask } from "@tauri-apps/plugin-dialog";
import { relaunch } from "@tauri-apps/plugin-process";
export async function runBackgroundUpdater(): Promise {
try {
const updatePayload = await check();
if (updatePayload && updatePayload.available) {
const userResponse = await ask(
`Une nouvelle version [v${updatePayload.version}] est disponible. Voulez-vous mettre à jour et redémarrer l'application maintenant ?`,
{
title: "Avis de mise à jour automatique du logiciel",
kind: "info",
okLabel: "Installer la mise à jour et redémarrer",
cancelLabel: "Appliquer plus tard"
}
);
if (userResponse) {
await updatePayload.downloadAndInstall();
await relaunch();
}
}
} catch (error) {
console.error("Erreur lors du processus de vérification de mise à jour automatique :", error);
}
}
`
Il suffit d'insérer cette fonction dans le composant React de haut niveau ou dans l'étape de montage initial de votre vue pour que les utilisateurs utilisent toujours une version à jour du logiciel sans avoir à parcourir le site web.
appDataDir contrôlé par le natif et exécutez une migration de schéma à long terme pour éviter que les données ne soient corrompues lors des mises à jour de l'application.