v0.27.4 — Exe per-version configurable + auto-install redists Unreal
== Exe par-version, configurable via le backoffice == Pour éviter de devoir bumper le launcher à chaque changement d'Unreal Engine (PROSERVE_UE_5_5.exe → PROSERVE_UE_5_7.exe), l'exe à lancer est maintenant déclaré par version dans le manifest serveur, configurable depuis le form admin. - server/admin/versions.php : nouveau champ "Nom de l'exécutable" dans le form d'ajout de version (regex strict anti-path-traversal). Par défaut PROSERVE_UE_5_7.exe. - InstallationRegistry : nouveau .proserve-meta.json écrit dans chaque dossier d'install fraîchement extrait (contient l'exe declaré par le manifest). Scan() lit cette meta pour résoudre l'exe sans avoir besoin du manifest en mémoire (offline / pré-check). - Fallback glob PROSERVE_UE_*.exe pour les vieux installs sans metadata — garantit la backward compat de l'UE 5.5 actuel sans intervention. == Auto-install des redists Unreal depuis _redist/ == Quand une version change d'Unreal (typique : UE 5.5 → 5.7), les redists Microsoft VC + UEPrereqSetup doivent être installés sur le PC. Au lieu de demander à l'opérateur de les installer à la main, le launcher détecte maintenant un dossier _redist/ dans la racine du ZIP de release et lance automatiquement tous les .exe dedans. - MainViewModel.InstallRedistsAsync : après le ZipInstaller, scan _redist/ pour .exe, lance chacun avec /install /quiet /norestart + Verb=runas (UAC popup par installer, inévitable car les redists écrivent dans Program Files). Tri alphabétique (préfixe 01_, 02_, … pour forcer un ordre si besoin). - InstallationRegistry.MarkRedistInstalledAsync : trace redistInstalledAt dans .proserve-meta.json après succès. Reinstall de la même version : skip silencieux, pas de UAC popup chain. - Best-effort sur les exit codes : les "already installed" retournent souvent un code non-zero, on log mais on continue (l'install PROSERVE ne doit pas être bloquée par un redist mineur). Côté ZIP de release : crée _redist/ à la racine avec les .exe à installer (typiquement VC_redist.x64.exe + UEPrereqSetup_x64.exe). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1081,6 +1081,29 @@ public sealed partial class MainViewModel : ObservableObject
|
||||
});
|
||||
await _zipInstaller.InstallAsync(zipPath, target, installProgress, ct);
|
||||
|
||||
// Persiste le nom du .exe à lancer dans le dossier d'install fraîchement
|
||||
// extrait. InstallationRegistry.Scan() le lira pour résoudre l'exe sans
|
||||
// avoir besoin du manifest en mémoire (cas démarrage offline / pré-check).
|
||||
// Le nom vient du manifest serveur (champ "executable" par version),
|
||||
// configurable dans le backoffice.
|
||||
try
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(row.Remote.Executable))
|
||||
await _registry.WriteInstallMetadataAsync(target, row.Remote.Executable, ct);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning(ex, "Failed to write install metadata (will fall back to glob detection)");
|
||||
}
|
||||
|
||||
// Installation des redists Unreal (VC_redist, UEPrereqSetup, etc.) si la
|
||||
// version contient un dossier `_redist/`. Run silent + UAC élévation par
|
||||
// installer. Track via metadata pour ne pas re-runner à chaque réinstall.
|
||||
// Si un installer fail (already-installed retournent souvent un exit code
|
||||
// non-zero), on log mais on continue — l'install PROSERVE ne doit pas être
|
||||
// bloquée par un redist mineur.
|
||||
await InstallRedistsAsync(target, row.Version, ct);
|
||||
|
||||
// Promote le ZIP au cache LAN (sidecar SHA écrit) puis prune les vieilles
|
||||
// versions selon MaxCachedVersions. Le ZIP est déjà au bon endroit
|
||||
// (DownloadManager.VerifyAndFinalizeAsync l'a Move'd vers `proserve-X.zip`),
|
||||
@@ -1311,6 +1334,83 @@ public sealed partial class MainViewModel : ObservableObject
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Si <c>_redist/</c> existe dans le dossier d'install, lance tous les .exe
|
||||
/// dedans en silent + UAC élevé pour installer les redists Unreal (VC_redist,
|
||||
/// UEPrereqSetup, etc.). Skip si déjà fait (tracé dans .proserve-meta.json).
|
||||
/// Best-effort : on log mais on ne fail pas l'install si un redist plante
|
||||
/// (typiquement les "already installed" retournent un exit code non-zero).
|
||||
/// </summary>
|
||||
private async Task InstallRedistsAsync(string installDir, string version, CancellationToken ct)
|
||||
{
|
||||
var redistDir = Path.Combine(installDir, "_redist");
|
||||
if (!Directory.Exists(redistDir))
|
||||
{
|
||||
_logger.LogDebug("No _redist/ in v{Version}, skipping redists install", version);
|
||||
return;
|
||||
}
|
||||
if (_registry.IsRedistInstalled(installDir))
|
||||
{
|
||||
_logger.LogInformation("Redists already installed for v{Version} (per metadata), skipping", version);
|
||||
return;
|
||||
}
|
||||
|
||||
var exes = Directory.GetFiles(redistDir, "*.exe", SearchOption.TopDirectoryOnly);
|
||||
if (exes.Length == 0)
|
||||
{
|
||||
_logger.LogDebug("_redist/ exists but contains no .exe, skipping");
|
||||
return;
|
||||
}
|
||||
Array.Sort(exes, StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
StatusMessage = Strings.StatusInstallingRedists(version);
|
||||
ProgressDetail = null;
|
||||
for (int i = 0; i < exes.Length; i++)
|
||||
{
|
||||
ct.ThrowIfCancellationRequested();
|
||||
var exe = exes[i];
|
||||
var name = Path.GetFileName(exe);
|
||||
ProgressDetail = Strings.ProgressRedistInstalling(i + 1, exes.Length, name);
|
||||
_logger.LogInformation("Running redist {Idx}/{Total} : {Exe}", i + 1, exes.Length, name);
|
||||
try
|
||||
{
|
||||
// Silent flags qui couvrent VC_redist (/install /quiet /norestart) et
|
||||
// UEPrereqSetup (/install /quiet /norestart). MSI std accepte aussi.
|
||||
// UseShellExecute=true + Verb=runas → UAC popup pour élévation admin.
|
||||
var psi = new System.Diagnostics.ProcessStartInfo
|
||||
{
|
||||
FileName = exe,
|
||||
Arguments = "/install /quiet /norestart",
|
||||
UseShellExecute = true,
|
||||
Verb = "runas",
|
||||
CreateNoWindow = true,
|
||||
};
|
||||
using var proc = System.Diagnostics.Process.Start(psi);
|
||||
if (proc is null) continue;
|
||||
await proc.WaitForExitAsync(ct).ConfigureAwait(false);
|
||||
_logger.LogInformation("Redist {Name} exit code {Code}", name, proc.ExitCode);
|
||||
// Codes typiques :
|
||||
// 0 = succès
|
||||
// 3010 = succès mais reboot requis (on ne fait pas, le user gérera)
|
||||
// 1638 = "already installed, newer version" — OK, on continue
|
||||
// 1602/1603 = user cancel UAC ou install error — log mais continue
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning(ex, "Redist {Name} install failed (continuing)", name);
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await _registry.MarkRedistInstalledAsync(installDir, ct).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning(ex, "Failed to mark redist installed in metadata");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task UninstallVersionAsync(VersionRowViewModel row)
|
||||
{
|
||||
if (row.Installed is null) return;
|
||||
|
||||
Reference in New Issue
Block a user