Contexte : après le fix v1.0.5 (/download-url disambigué par filename), un
opérateur peut avoir un manifest avec deux entries partageant un numéro de
version sur des channels différents (ex : proserve-firefighter-1.5.4.32 vs
proserve-full-1.5.4.32). Deux problèmes restants :
1. Le client n'affichait qu'UNE row : `remote.ToDictionary(v.Version)` dans
RebuildList crashait sur duplicate key.
2. À l'install, les deux entries résolvaient au même dossier via le default
`installFolderTemplate = "PROSERVE v{version}"` → l'install le plus récent
écrasait silencieusement le précédent (ZipInstaller rename en .bak-{ts}
puis delete en background).
Solution end-to-end :
── Client ─────────────────────────────────────────────────────────────
• RebuildList refactor : index par folder name (résolu via GetInstallFolder-
Name()) au lieu de par version. Deux entries au même numéro deviennent
visibles dès qu'elles ont des templates distincts. Warning log si deux
entries résolvent au même folder.
• VersionRowViewModel : nouveau RowKey (basename du folder ou fallback
Version), ChannelBadge (premier channel non-default). Sites de lookup
(DL-in-flight preservation, 404 retry) migrés sur RowKey.
• MainWindow.xaml : badge bleu channel affiché à côté du badge BÊTA, dans
la row compact ET dans FeaturedVersion.
• Install guard : refuse une install si le dossier cible contient déjà un
.proserve-meta.json avec un entryId différent. Le meta stocke maintenant
l'entryId à chaque WriteInstallMetadataAsync. Message clair localisé
(FR/EN/CN/TH/AR/ES/DE) qui pointe l'opérateur vers le backoffice.
• VersionManifest client model : nouveau champ optionnel `Id` (mappé sur
le champ serveur existant), utilisé pour identifier l'entrée source.
• Registry regex broadened : accepte `PROSERVE(-<channel>)? v...` en plus
du `PROSERVE v...` legacy. Les folders custom par channel sont scannés.
── Serveur admin (versions.php) ──────────────────────────────────────
• Nouveau champ éditable `install_folder_template` dans le formulaire
d'ajout ET dans edit_meta. Validation regex (contient {version}, charset
whitelisted).
• Default intelligent à la création : si un seul channel non-default est
coché, pré-remplit avec "PROSERVE-<channel> v{version}". Sinon garde
"PROSERVE v{version}" (legacy).
• Validation croisée : refuse la save si deux entries résolvent au même
dossier, avec un message clair qui suggère un template alternatif.
── Rétro-compat ──────────────────────────────────────────────────────
• Vieux installs (sans entryId dans meta) : install guard fail-open, se
laisse écraser à la ré-install et retrofit l'entryId.
• Vieux manifests (sans `id` sur les entries) : `Id` est null côté client,
l'install guard reste passif, comportement identique à v1.0.7.
• Vieux serveurs (sans `install_folder_template` éditable) : le manifest
reste avec le default généré par generate_entry_id, aucune breaking
change. Le badge channel s'affiche quand même si `channels` est renseigné.
• Setups mono-channel (99 % des cas) : aucun changement visible, sort et
matching identiques.
Bump : 1.0.7 → 1.0.8.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
PS_Launcher — Installeur
Génère un setup Windows (PSLauncher-Setup-{version}.exe) qui distribue
le launcher principal + son updater dans Program Files\ASTERION VR\PSLauncher\.
Pré-requis
- .NET 8 SDK (déjà installé pour build le projet)
- Inno Setup 6.x — https://jrsoftware.org/isdl.php (gratuit, ~6 Mo)
Build en une commande
Depuis la racine du repo :
powershell -ExecutionPolicy Bypass -File installer\build-installer.ps1
Ça enchaîne :
dotnet publish src/PSLauncher.App -c Release→PSLauncher.exe(~77 Mo)dotnet publish src/PSLauncher.Updater -c Release→PSLauncher.Updater.exe(~10 Mo)ISCC PSLauncher.iss→installer/output/PSLauncher-Setup-X.Y.Z.exe
Le fichier final fait ~80 Mo (compression ultra Inno + binaires self-contained).
Distribution
Tu balances un seul .exe au client :
- Il lance, choisit français/anglais, clique Suivant×3, c'est installé.
- Raccourci bureau + menu Démarrer créés.
- Désinstallation propre via Programmes et fonctionnalités.
Le cache utilisateur (%LocalAppData%\PSLauncher\config.json, logs, downloads partiels)
n'est pas touché par l'install/désinstall — la license et les paramètres survivent.
Mise à jour du launcher
Le launcher se met à jour seul via PSLauncher.Updater.exe (cf. flow auto-update).
Le client n'a pas besoin de rejouer l'installeur sauf changement majeur (TFM, déps).
Bumper la version
Avant de rebuild un installeur :
src/PSLauncher.App/PSLauncher.App.csproj→<Version>X.Y.Z</Version>(et AssemblyVersion / FileVersion)installer/PSLauncher.iss→#define MyAppVersion "X.Y.Z"installer/build-installer.ps1
Côté serveur, mets aussi à jour manifest/versions.json section launcher avec la
nouvelle version + URL de download du nouveau PSLauncher.exe (via le backoffice
ou édition manuelle, puis php tools/sign-manifest.php).
Code-signing (optionnel)
Pour éviter le warning SmartScreen "éditeur inconnu", il faut signer
PSLauncher.exe, PSLauncher.Updater.exe et PSLauncher-Setup-X.Y.Z.exe avec
un certificat Authenticode (OV ~250 €/an, EV ~400 €/an, ou gratuit via SignPath
pour OSS). Hors-scope du script actuel.