Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
89570947aa | ||
|
|
14b2167218 | ||
|
|
2e4d2ee76e | ||
|
|
7d9032fe20 | ||
|
|
ccd6b1a430 | ||
|
|
e4a21c6f5d | ||
|
|
1a7f45c38b | ||
|
|
b48d4222db |
No files matched your search
@@ -429,7 +429,7 @@ Le launcher (un seul `GameLauncher.exe`) vérifie au démarrage s'il existe une
|
||||
lui-même, la télécharge, l'installe et **redémarre tout seul**. Réglable dans **Paramètres du launcher** (roue crantée) :
|
||||
interrupteur « Mettre à jour le launcher automatiquement » (commun à tous les jeux) et bouton
|
||||
**« Vérifier les mises à jour »** (puis « Installer la x.y.z »). Option désactivée : un bandeau signale
|
||||
simplement qu'une nouvelle version existe.
|
||||
simplement qu'une nouvelle version existe — sauf pour une **version prioritaire** (ci-dessous), installée quand même.
|
||||
|
||||
### Sécurité
|
||||
|
||||
@@ -441,6 +441,7 @@ simplement qu'une nouvelle version existe.
|
||||
| Pas de rétrogradation | Seule une version **strictement supérieure** est installée ; le produit (`launcherId`) doit correspondre. |
|
||||
| Tailles bornées | `launcher-update.json` ≤ 256 Ko ; un serveur qui envoie plus que la taille annoncée est coupé (vaut aussi pour les fichiers du jeu). |
|
||||
| Retour arrière automatique | Si la nouvelle version ne démarre pas dans les 60 s (ou n'est pas la version annoncée), elle est arrêtée et l'ancienne remise en place. Cette version n'est plus proposée automatiquement (bouton manuel uniquement). |
|
||||
| Version prioritaire signée | L'indication « prioritaire » fait partie du contenu signé : un serveur compromis ne peut ni imposer une mise à jour, ni retirer la priorité d'un correctif. Elle ne permet pas d'installer autre chose qu'une version signée, strictement supérieure. |
|
||||
|
||||
### Déroulement
|
||||
|
||||
@@ -463,6 +464,22 @@ La mise à jour est refusée tant qu'un jeu est lancé ou qu'un téléchargement
|
||||
est dans un dossier protégé (Program Files), il ne peut pas se remplacer : le message l'explique.
|
||||
Depuis Visual Studio (F5), le bouton vérifie mais n'installe pas : seule la version publiée se remplace.
|
||||
|
||||
### Version prioritaire (correctif critique)
|
||||
|
||||
Une version publiée avec **`--priority`** est installée au démarrage **même chez les joueurs qui ont
|
||||
désactivé** « Mettre à jour le launcher automatiquement ». Exemple : une faille critique est corrigée en 3.4.0.
|
||||
|
||||
| Point | Comportement |
|
||||
|---|---|
|
||||
| Réglage du joueur | **Jamais modifié** : `autoUpdate` reste à `false` dans `self-update.json`. Effet ponctuel : une fois le launcher à jour, les versions normales suivantes ne sont plus installées automatiquement chez lui. |
|
||||
| Versions suivantes | La priorité est **reprise** par les publications suivantes (`priorityVersion` dans `launcher-update.json`) : si la 3.5.0 (normale) sort après le correctif 3.4.0, un launcher encore en 3.3.x passe directement en 3.5.0 ; un launcher déjà en 3.4.0 n'est pas concerné. `--clear-priority` arrête la reprise (priorité publiée par erreur). |
|
||||
| Fenêtre de mise à jour | Pastille **« Prioritaire »** et texte « Mise à jour importante… votre réglage ne change pas ». « Plus tard » reste possible : nouvelle tentative au démarrage suivant, bandeau d'avertissement en attendant. |
|
||||
| Erreur de téléchargement | Retentée au démarrage suivant (une version normale n'est plus retentée automatiquement). |
|
||||
| La nouvelle version ne démarre pas | Retour arrière, puis plus de tentative automatique (sinon 60 s d'attente à chaque lancement) : bandeau d'avertissement et statut orange dans les Paramètres du launcher, installation par le bouton. |
|
||||
| Dossier protégé (Program Files) | Pas d'installation possible sans droits administrateur : bandeau d'avertissement. |
|
||||
| Option désactivée | Le launcher interroge désormais le serveur au démarrage (`launcher-update.json`, 1 à 2 Ko, 6 s au plus) pour savoir si une version prioritaire existe. |
|
||||
| Launchers antérieurs à 3.4.0 | Ignorent l'indication : ils ne suivent que leur propre réglage. La priorité ne vaut que pour les joueurs déjà en 3.4.0 ou plus. |
|
||||
|
||||
### Mise en place (une seule fois)
|
||||
|
||||
1. **Clés** : double-cliquez `tools\keygen-launcher.bat`. Choisissez une phrase de passe (12 caractères
|
||||
@@ -490,6 +507,9 @@ Depuis Visual Studio (F5), le bouton vérifie mais n'installe pas : seule la ver
|
||||
signé par votre clé. Les launchers antérieurs à 1.1.3 reçoivent toujours le titre et la liste « à plat ».
|
||||
3. Double-cliquez `tools\publish-launcher-update.bat` : compilation, signature (phrase de passe demandée),
|
||||
puis envoi WinSCP — l'exe d'abord, `launcher-update.json` **en dernier**.
|
||||
**Correctif critique** : depuis une invite de commandes, `tools\publish-launcher-update.bat --priority`
|
||||
(confirmation demandée avant la signature). `--clear-priority` ne reprend pas la priorité déjà publiée.
|
||||
Le résumé affiché indique la priorité (« normale », « PRIORITAIRE » ou « version prioritaire x.y.z reprise »).
|
||||
|
||||
Vérification à tout moment :
|
||||
`dotnet run --project tools\LauncherPublisher -- verify --manifest https://…/launcher/launcher-update.json --public-key src\GameLauncher\Security\update-signing.pub`
|
||||
|
||||
@@ -50,9 +50,58 @@ public sealed partial class LauncherRelease
|
||||
/// </summary>
|
||||
public List<ChangelogEntry> Changelog { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Version PRIORITAIRE la plus récente publiée jusqu'à celle-ci incluse (ex. "3.4.0"), ou null s'il n'y en a pas.
|
||||
/// Un launcher plus ancien que cette version installe la mise à jour au démarrage MÊME SI le joueur a désactivé
|
||||
/// la mise à jour automatique (correctif critique). Le réglage du joueur (self-update.json) n'est jamais modifié :
|
||||
/// une fois le launcher à jour, il n'est plus concerné (effet ponctuel).
|
||||
/// Reprise d'une publication à l'autre par LauncherPublisher (voir <see cref="ResolvePriorityVersion"/>) : une
|
||||
/// version normale publiée après un correctif critique l'impose toujours aux launchers restés trop anciens.
|
||||
/// Fait partie du contenu SIGNÉ : un serveur compromis ne peut ni l'ajouter, ni la retirer, ni la modifier.
|
||||
/// Ignorée par les launchers antérieurs à 3.4.0 (propriété inconnue).
|
||||
/// </summary>
|
||||
public string? PriorityVersion { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public Version ParsedVersion => VersionUtil.Parse(Version);
|
||||
|
||||
/// <summary>Cette version a elle-même été publiée comme prioritaire (option --priority).</summary>
|
||||
[JsonIgnore]
|
||||
public bool IsPriorityRelease => TryGetPriorityVersion(out var priority) && priority == ParsedVersion;
|
||||
|
||||
/// <summary>
|
||||
/// Vrai si un launcher en version <paramref name="currentVersion"/> doit installer cette mise à jour même avec la
|
||||
/// mise à jour automatique désactivée : il est plus ancien que la dernière version prioritaire publiée.
|
||||
/// </summary>
|
||||
public bool IsPriorityFor(Version currentVersion) =>
|
||||
TryGetPriorityVersion(out var priority) && priority > currentVersion;
|
||||
|
||||
/// <summary>Version prioritaire lisible (null ou illisible : faux).</summary>
|
||||
public bool TryGetPriorityVersion(out Version priority)
|
||||
{
|
||||
priority = new Version(0, 0, 0);
|
||||
return !string.IsNullOrWhiteSpace(PriorityVersion) && VersionUtil.TryParse(PriorityVersion, out priority);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Version prioritaire à inscrire dans une nouvelle publication (LauncherPublisher) :
|
||||
/// <list type="bullet">
|
||||
/// <item><paramref name="markPriority"/> : la version publiée elle-même ;</item>
|
||||
/// <item><paramref name="clearPriority"/> : aucune (priorité publiée par erreur, par exemple) ;</item>
|
||||
/// <item>sinon : celle de la publication précédente (<paramref name="previous"/>, dont la signature a été
|
||||
/// VÉRIFIÉE par l'appelant), si elle n'est pas supérieure à la version publiée.</item>
|
||||
/// </list>
|
||||
/// </summary>
|
||||
public static string? ResolvePriorityVersion(Version published, bool markPriority, bool clearPriority, LauncherRelease? previous)
|
||||
{
|
||||
if (markPriority && clearPriority)
|
||||
throw new ArgumentException("Une publication ne peut pas à la fois être prioritaire et retirer la priorité.");
|
||||
if (markPriority) return VersionUtil.Display(published);
|
||||
if (clearPriority || previous is null || !previous.TryGetPriorityVersion(out var carried) || carried > published)
|
||||
return null;
|
||||
return VersionUtil.Display(carried);
|
||||
}
|
||||
|
||||
public void Validate()
|
||||
{
|
||||
if (Format != 1)
|
||||
@@ -67,6 +116,12 @@ public sealed partial class LauncherRelease
|
||||
throw new UpdateSecurityException("Description de mise à jour invalide : taille incorrecte.");
|
||||
if (!Sha256Regex().IsMatch(Sha256 ?? ""))
|
||||
throw new UpdateSecurityException("Description de mise à jour invalide : empreinte SHA-256 incorrecte.");
|
||||
if (string.IsNullOrWhiteSpace(PriorityVersion)) PriorityVersion = null;
|
||||
else if (!VersionUtil.TryParse(PriorityVersion, out var priority))
|
||||
throw new UpdateSecurityException($"Description de mise à jour invalide : version prioritaire « {PriorityVersion} » illisible.");
|
||||
else if (priority > ParsedVersion)
|
||||
throw new UpdateSecurityException(
|
||||
$"Description de mise à jour invalide : version prioritaire {PriorityVersion} supérieure à la version publiée ({Version}).");
|
||||
Notes ??= new List<string>();
|
||||
// Textes affichés tels quels dans l'interface : mêmes bornes et nettoyage que les notes du jeu.
|
||||
Changelog = ChangelogLimits.Normalize(Changelog);
|
||||
|
||||
@@ -10,6 +10,12 @@ public sealed record LauncherUpdateCheck(Version CurrentVersion, LauncherRelease
|
||||
public bool IsAvailable => Release is not null && DownloadUri is not null;
|
||||
public string CurrentDisplay => VersionUtil.Display(CurrentVersion);
|
||||
public string AvailableDisplay => Release is null ? "" : VersionUtil.Display(Release.ParsedVersion);
|
||||
|
||||
/// <summary>
|
||||
/// Mise à jour PRIORITAIRE pour ce launcher (plus ancien que la dernière version prioritaire publiée) :
|
||||
/// installée au démarrage même si la mise à jour automatique est désactivée. Information signée.
|
||||
/// </summary>
|
||||
public bool IsPriority => IsAvailable && Release!.IsPriorityFor(CurrentVersion);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -899,7 +899,8 @@ public partial class App : Application, ILauncherRestartHost
|
||||
|
||||
/// <summary>
|
||||
/// Nouvelle version non installée automatiquement (option désactivée, échec précédent,
|
||||
/// dossier protégé) : simple information dans le bandeau.
|
||||
/// dossier protégé, « Plus tard ») : simple information dans le bandeau, en avertissement pour une
|
||||
/// version prioritaire.
|
||||
/// </summary>
|
||||
private async Task NotifyLauncherUpdateAsync()
|
||||
{
|
||||
@@ -907,7 +908,13 @@ public partial class App : Application, ILauncherRestartHost
|
||||
{
|
||||
await Task.Delay(TimeSpan.FromSeconds(4));
|
||||
var check = _updates!.SkippedUpdate ?? await _updates.CheckAsync();
|
||||
if (check.IsAvailable)
|
||||
if (check.IsPriority)
|
||||
// Correctif critique non installé au démarrage (« Plus tard », échec précédent, dossier protégé…)
|
||||
_launcher?.SelectedGame?.ShowNotice("Mise à jour importante du launcher",
|
||||
$"La version {check.AvailableDisplay} corrige un problème important et n'est pas encore installée : " +
|
||||
"Paramètres du launcher (roue crantée) › Vérifier les mises à jour.",
|
||||
DialogKind.Warning);
|
||||
else if (check.IsAvailable)
|
||||
_launcher?.SelectedGame?.ShowNotice("Nouvelle version du launcher",
|
||||
$"La version {check.AvailableDisplay} est disponible : Paramètres du launcher (roue crantée) › Vérifier les mises à jour.",
|
||||
DialogKind.Info);
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
(1.0.0 → 1.0.1 → 1.1.0…). Les launchers des joueurs ne se mettent à jour que vers
|
||||
une version STRICTEMENT supérieure à la leur.
|
||||
-->
|
||||
<Version>3.3.1</Version>
|
||||
<Version>3.4.0</Version>
|
||||
<Authors>Lanisys</Authors>
|
||||
<Company>LanisysGames</Company>
|
||||
<Copyright>© LanisysGames</Copyright>
|
||||
|
||||
@@ -128,12 +128,15 @@ public sealed class LauncherUpdateService : ILauncherUpdateService
|
||||
/// <summary>
|
||||
/// Au démarrage, avant l'affichage de la fenêtre principale : recherche rapide et, si une
|
||||
/// nouvelle version existe et que la mise à jour automatique est activée, installation.
|
||||
/// Mise à jour automatique désactivée : la recherche a lieu quand même, et seule une version
|
||||
/// PRIORITAIRE (correctif critique, information signée) est installée. Le réglage du joueur
|
||||
/// (self-update.json) n'est jamais modifié : il reste désactivé pour les versions suivantes.
|
||||
/// Renvoie vrai si le launcher redémarre (l'appelant doit alors s'arrêter là).
|
||||
/// </summary>
|
||||
public async Task<bool> RunStartupCheckAsync()
|
||||
{
|
||||
ExecutableSwapper.CleanupLeftovers(TargetPath, CurrentVersionValue);
|
||||
if (_client is null || !AutoUpdate || !IsSingleFileBuild) return false;
|
||||
if (_client is null || !IsSingleFileBuild) return false;
|
||||
|
||||
LauncherUpdateCheck check;
|
||||
try
|
||||
@@ -148,6 +151,18 @@ public sealed class LauncherUpdateService : ILauncherUpdateService
|
||||
}
|
||||
|
||||
if (!check.IsAvailable) return false;
|
||||
if (!AutoUpdate && !check.IsPriority)
|
||||
{
|
||||
// Option désactivée, version normale : simple information dans le bandeau (sans seconde recherche).
|
||||
SkippedUpdate = check;
|
||||
return false;
|
||||
}
|
||||
if (check.IsPriority)
|
||||
Log.Warn($"Launcher {check.AvailableDisplay} : mise à jour PRIORITAIRE (correctif publié en {check.Release!.PriorityVersion})" +
|
||||
(AutoUpdate ? "." : ", installée bien que la mise à jour automatique soit désactivée (réglage conservé)."));
|
||||
|
||||
// Échec précédent : même prioritaire, pas de nouvelle tentative automatique (une version qui ne démarre pas
|
||||
// sur ce PC imposerait 60 s d'attente et un retour arrière à chaque lancement). Bandeau + bouton manuel.
|
||||
if (string.Equals(_settings.FailedVersion, check.AvailableDisplay, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
Log.Warn($"Launcher {check.AvailableDisplay} : installation automatique ignorée (échec précédent). " +
|
||||
@@ -167,7 +182,10 @@ public sealed class LauncherUpdateService : ILauncherUpdateService
|
||||
SkippedUpdate = check;
|
||||
return false;
|
||||
}
|
||||
return await InstallAsync(check, automatic: true);
|
||||
if (await InstallAsync(check, automatic: true)) return true;
|
||||
// Version prioritaire non installée (« Plus tard », erreur) : rappel dans le bandeau une fois la fenêtre affichée.
|
||||
if (check.IsPriority) SkippedUpdate = check;
|
||||
return false;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------ Installation
|
||||
@@ -265,10 +283,17 @@ public sealed class LauncherUpdateService : ILauncherUpdateService
|
||||
Log.Error("Mise à jour du launcher impossible", ex);
|
||||
// Au démarrage, une erreur persistante (fichier absent du serveur, antivirus…) ne doit
|
||||
// pas réapparaître à chaque lancement : cette version ne sera plus installée automatiquement.
|
||||
if (automatic) RememberFailure(update);
|
||||
vm.SetError(FriendlyMessage(ex) + (automatic
|
||||
? "\n\nVous pourrez réessayer depuis Paramètres du launcher › Vérifier les mises à jour."
|
||||
: ""));
|
||||
// Exception : une version PRIORITAIRE (correctif critique) est retentée au prochain démarrage
|
||||
// (erreur de téléchargement le plus souvent passagère). Seul un échec de démarrage de la nouvelle
|
||||
// version (plus haut, après retour arrière) l'écarte des installations automatiques.
|
||||
var retryAtNextStart = automatic && update.IsPriority;
|
||||
if (automatic && !retryAtNextStart) RememberFailure(update);
|
||||
vm.SetError(FriendlyMessage(ex) + (retryAtNextStart
|
||||
? "\n\nCette mise à jour est importante : elle sera retentée au prochain démarrage du launcher " +
|
||||
"(ou dès maintenant depuis Paramètres du launcher › Vérifier les mises à jour)."
|
||||
: automatic
|
||||
? "\n\nVous pourrez réessayer depuis Paramètres du launcher › Vérifier les mises à jour."
|
||||
: ""));
|
||||
}
|
||||
|
||||
// Erreur : la fenêtre reste ouverte jusqu'à ce que l'utilisateur la ferme.
|
||||
|
||||
@@ -116,9 +116,19 @@ public sealed class LauncherSettingsViewModel : SettingsViewModelBase
|
||||
if (check.IsAvailable)
|
||||
{
|
||||
SetAvailable(check);
|
||||
LauncherStatusKind = DialogKind.Info;
|
||||
var title = string.IsNullOrWhiteSpace(check.Release?.Title) ? "" : $" — {check.Release!.Title}";
|
||||
LauncherStatus = $"Nouvelle version {check.AvailableDisplay} disponible{title} (actuelle : {check.CurrentDisplay}).";
|
||||
if (check.IsPriority)
|
||||
{
|
||||
// Correctif critique non installé au démarrage (annulé, échec, dossier protégé…)
|
||||
LauncherStatusKind = DialogKind.Warning;
|
||||
LauncherStatus = $"Mise à jour importante {check.AvailableDisplay} disponible{title} (actuelle : {check.CurrentDisplay}). " +
|
||||
"Installez-la dès que possible.";
|
||||
}
|
||||
else
|
||||
{
|
||||
LauncherStatusKind = DialogKind.Info;
|
||||
LauncherStatus = $"Nouvelle version {check.AvailableDisplay} disponible{title} (actuelle : {check.CurrentDisplay}).";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -21,13 +21,19 @@ public sealed class LauncherUpdateViewModel : ObservableObject
|
||||
public LauncherUpdateViewModel(LauncherUpdateCheck update, bool automatic)
|
||||
{
|
||||
Automatic = automatic;
|
||||
IsPriority = update.IsPriority;
|
||||
FromVersion = update.CurrentDisplay;
|
||||
ToVersion = update.AvailableDisplay;
|
||||
// Notes de toutes les versions publiées depuis la version installée (format enrichi)
|
||||
Changelog = update.Release?.GetChangelogSince(update.CurrentVersion) ?? Array.Empty<ChangelogEntry>();
|
||||
Subtitle = automatic
|
||||
? "Une nouvelle version est disponible. Elle s'installe automatiquement, le launcher redémarrera tout seul."
|
||||
: "Installation de la nouvelle version. Le launcher redémarrera tout seul.";
|
||||
Subtitle = (automatic, IsPriority) switch
|
||||
{
|
||||
(true, true) => "Mise à jour importante : elle s'installe automatiquement, même si la mise à jour automatique " +
|
||||
"du launcher est désactivée (votre réglage ne change pas). Le launcher redémarrera tout seul.",
|
||||
(true, false) => "Une nouvelle version est disponible. Elle s'installe automatiquement, le launcher redémarrera tout seul.",
|
||||
(false, true) => "Installation d'une mise à jour importante. Le launcher redémarrera tout seul.",
|
||||
_ => "Installation de la nouvelle version. Le launcher redémarrera tout seul."
|
||||
};
|
||||
|
||||
Progress = new Progress<OperationProgress>(p =>
|
||||
{
|
||||
@@ -44,6 +50,9 @@ public sealed class LauncherUpdateViewModel : ObservableObject
|
||||
/// <summary>Recherche au démarrage (vrai) ou demandée dans les Paramètres du launcher (faux).</summary>
|
||||
public bool Automatic { get; }
|
||||
|
||||
/// <summary>Mise à jour prioritaire (correctif critique, information signée) : pastille « Prioritaire ».</summary>
|
||||
public bool IsPriority { get; }
|
||||
|
||||
public string FromVersion { get; }
|
||||
public string ToVersion { get; }
|
||||
public string Subtitle { get; }
|
||||
|
||||
@@ -76,6 +76,16 @@
|
||||
<TextBlock Text="{Binding ToVersion}" FontSize="13" FontWeight="Bold" VerticalAlignment="Center" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
<!-- Mise à jour prioritaire (correctif critique) : installée même si l'option automatique est désactivée -->
|
||||
<Border Style="{StaticResource VersionChipStyle}" Margin="10,0,0,0" BorderBrush="{DynamicResource WarningBrush}"
|
||||
ToolTip="Correctif important : installé même si la mise à jour automatique du launcher est désactivée. Votre réglage ne change pas."
|
||||
Visibility="{Binding IsPriority, Converter={StaticResource BoolToVisibility}}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Path Data="{StaticResource Icon.Alert}" Fill="{DynamicResource WarningBrush}"
|
||||
Width="14" Height="14" Stretch="Uniform" VerticalAlignment="Center" Margin="0,0,7,0" />
|
||||
<TextBlock Text="Prioritaire" FontSize="12.5" FontWeight="SemiBold" VerticalAlignment="Center" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Nouveautés : même présentation que les notes du jeu (Themes/Changelog.xaml) -->
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using GameLauncher.Core.SelfUpdate;
|
||||
using GameLauncher.Core.Utils;
|
||||
|
||||
namespace GameLauncher.Tests.Signing;
|
||||
|
||||
/// <summary>
|
||||
/// Version PRIORITAIRE du launcher (correctif critique) : installée au démarrage même si le joueur a désactivé la mise
|
||||
/// à jour automatique. Information signée, reprise d'une publication à l'autre, sans effet sur les launchers à jour.
|
||||
/// </summary>
|
||||
public class LauncherPriorityTests
|
||||
{
|
||||
private static LauncherRelease Release(string version, string? priority = null) => new()
|
||||
{
|
||||
Product = "GameLauncher",
|
||||
Version = version,
|
||||
File = $"GameLauncher-{version}.exe",
|
||||
Size = 1234,
|
||||
Sha256 = new string('a', 64),
|
||||
PublishedAt = DateTimeOffset.UnixEpoch,
|
||||
PriorityVersion = priority
|
||||
};
|
||||
|
||||
private static (string Envelope, UpdateSignature.TrustedKey Key) Sign(LauncherRelease release, ECDsa key)
|
||||
{
|
||||
var payload = JsonSerializer.SerializeToUtf8Bytes(release, JsonDefaults.Options);
|
||||
var envelope = UpdateSignature.CreateEnvelope(payload, key);
|
||||
var spki = key.ExportSubjectPublicKeyInfo();
|
||||
return (envelope, new UpdateSignature.TrustedKey(UpdateSignature.ComputeKeyId(spki), spki));
|
||||
}
|
||||
|
||||
private static LauncherUpdateCheck Check(string current, LauncherRelease? release) =>
|
||||
new(VersionUtil.Parse(current), release, release is null ? null : new Uri("https://jeux.exemple.test/launcher/" + release.File));
|
||||
|
||||
[Fact]
|
||||
public void The_priority_version_is_part_of_the_signed_content()
|
||||
{
|
||||
using var key = ECDsa.Create(ECCurve.NamedCurves.nistP256);
|
||||
var (envelope, trusted) = Sign(Release("3.4.0", priority: "3.4.0"), key);
|
||||
|
||||
var read = UpdateSignature.ReadRelease(envelope, new[] { trusted });
|
||||
Assert.Equal("3.4.0", read.PriorityVersion);
|
||||
Assert.True(read.IsPriorityRelease);
|
||||
|
||||
// Serveur compromis : retirer (ou ajouter) la priorité dans le payload casse la signature.
|
||||
var json = JsonSerializer.Deserialize<SignedEnvelope>(envelope, JsonDefaults.Options)!;
|
||||
var tampered = Encoding.UTF8.GetString(Convert.FromBase64String(json.Payload)).Replace("\"3.4.0\"", "\"3.3.9\"");
|
||||
json.Payload = Convert.ToBase64String(Encoding.UTF8.GetBytes(tampered));
|
||||
var forged = JsonSerializer.Serialize(json, JsonDefaults.Options);
|
||||
Assert.Throws<UpdateSecurityException>(() => UpdateSignature.ReadRelease(forged, new[] { trusted }));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void A_release_without_priority_stays_compatible_with_older_launchers()
|
||||
{
|
||||
using var key = ECDsa.Create(ECCurve.NamedCurves.nistP256);
|
||||
var release = Release("3.4.0");
|
||||
var payload = Encoding.UTF8.GetString(JsonSerializer.SerializeToUtf8Bytes(release, JsonDefaults.Options));
|
||||
Assert.DoesNotContain("priorityVersion", payload, StringComparison.OrdinalIgnoreCase); // champ absent : rien ne change
|
||||
|
||||
var (envelope, trusted) = Sign(release, key);
|
||||
var read = UpdateSignature.ReadRelease(envelope, new[] { trusted });
|
||||
Assert.Null(read.PriorityVersion);
|
||||
Assert.False(read.IsPriorityRelease);
|
||||
Assert.False(Check("3.3.1", read).IsPriority);
|
||||
Assert.True(Check("3.3.1", read).IsAvailable);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("3.3.1", true)] // plus ancien que le correctif : mise à jour imposée
|
||||
[InlineData("3.3.9", true)]
|
||||
[InlineData("3.4.0", false)] // correctif déjà installé : réglage du joueur respecté pour 3.5.0
|
||||
[InlineData("3.4.2", false)]
|
||||
public void Only_launchers_older_than_the_priority_version_are_forced(string current, bool forced)
|
||||
{
|
||||
var release = Release("3.5.0", priority: "3.4.0"); // 3.4.0 prioritaire, puis 3.5.0 normale
|
||||
Assert.Equal(forced, release.IsPriorityFor(VersionUtil.Parse(current)));
|
||||
Assert.Equal(forced, Check(current, release).IsPriority);
|
||||
Assert.False(release.IsPriorityRelease);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void No_update_available_means_no_priority()
|
||||
{
|
||||
Assert.False(Check("3.4.0", null).IsPriority);
|
||||
Assert.False(new LauncherUpdateCheck(VersionUtil.Parse("3.3.0"), Release("3.4.0", "3.4.0"), null).IsPriority);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("3.6.0")] // supérieure à la version publiée
|
||||
[InlineData("pas-une-version")]
|
||||
public void An_inconsistent_priority_version_is_refused(string priority)
|
||||
{
|
||||
var release = Release("3.5.0", priority);
|
||||
Assert.Throws<UpdateSecurityException>(release.Validate);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void A_blank_priority_version_means_none()
|
||||
{
|
||||
var release = Release("3.5.0", " ");
|
||||
release.Validate();
|
||||
Assert.Null(release.PriorityVersion);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void The_publisher_marks_carries_over_or_clears_the_priority()
|
||||
{
|
||||
var v350 = VersionUtil.Parse("3.5.0");
|
||||
var previousPriority = Release("3.4.0", priority: "3.4.0");
|
||||
var previousNormal = Release("3.4.0");
|
||||
|
||||
// --priority : la version publiée elle-même
|
||||
Assert.Equal("3.5.0", LauncherRelease.ResolvePriorityVersion(v350, markPriority: true, clearPriority: false, previousNormal));
|
||||
// version normale après un correctif critique : priorité reprise (les launchers < 3.4.0 restent forcés)
|
||||
Assert.Equal("3.4.0", LauncherRelease.ResolvePriorityVersion(v350, false, false, previousPriority));
|
||||
// --clear-priority : plus aucune version imposée
|
||||
Assert.Null(LauncherRelease.ResolvePriorityVersion(v350, false, true, previousPriority));
|
||||
// rien à reprendre
|
||||
Assert.Null(LauncherRelease.ResolvePriorityVersion(v350, false, false, previousNormal));
|
||||
Assert.Null(LauncherRelease.ResolvePriorityVersion(v350, false, false, null));
|
||||
// priorité précédente supérieure à la version publiée (--force vers une version inférieure) : non reprise
|
||||
Assert.Null(LauncherRelease.ResolvePriorityVersion(VersionUtil.Parse("3.3.5"), false, false, previousPriority));
|
||||
// options contradictoires
|
||||
Assert.Throws<ArgumentException>(() => LauncherRelease.ResolvePriorityVersion(v350, true, true, null));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void The_player_setting_file_has_no_priority_field()
|
||||
{
|
||||
// Effet ponctuel : la priorité vient UNIQUEMENT de la description signée, jamais de self-update.json.
|
||||
var settings = new SelfUpdateSettings { AutoUpdate = false };
|
||||
var json = JsonSerializer.Serialize(settings, JsonDefaults.Options);
|
||||
Assert.DoesNotContain("priority", json, StringComparison.OrdinalIgnoreCase);
|
||||
Assert.False(JsonSerializer.Deserialize<SelfUpdateSettings>(json, JsonDefaults.Options)!.AutoUpdate);
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,7 @@ namespace LauncherPublisher;
|
||||
/// --config src\GameLauncher\launcher.json --output "D:\Publish\launcher"
|
||||
/// [--version 1.2.0] [--notes notes-launcher.txt]
|
||||
/// [--public-key src\GameLauncher\Security\update-signing.pub]
|
||||
/// [--priority | --clear-priority] [--yes]
|
||||
/// LauncherPublisher verify --manifest "D:\Publish\launcher\launcher-update.json"
|
||||
/// --public-key src\GameLauncher\Security\update-signing.pub [--exe fichier.exe]
|
||||
///
|
||||
@@ -49,6 +50,11 @@ public static class Program
|
||||
default: return Usage();
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException ex)
|
||||
{
|
||||
Warn(ex.Message);
|
||||
return 1;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Error($"Erreur : {ex.Message}");
|
||||
@@ -136,6 +142,22 @@ public static class Program
|
||||
@"Augmentez <Version> dans src\GameLauncher\GameLauncher.csproj, recompilez le launcher, puis relancez. (--force pour passer outre)");
|
||||
}
|
||||
|
||||
// Version PRIORITAIRE (correctif critique) : installée au démarrage même chez les joueurs qui ont désactivé la
|
||||
// mise à jour automatique (leur réglage n'est pas modifié). Confirmation demandée : rien n'est encore écrit.
|
||||
var markPriority = o.Has("--priority");
|
||||
var clearPriority = o.Has("--clear-priority");
|
||||
if (markPriority && clearPriority)
|
||||
throw new ArgumentException("--priority et --clear-priority sont incompatibles.");
|
||||
if (markPriority && !o.Has("--yes"))
|
||||
{
|
||||
Warn($"Version PRIORITAIRE : le launcher {display} sera installé au démarrage MÊME chez les joueurs qui ont désactivé " +
|
||||
"la mise à jour automatique (leur réglage n'est pas modifié). À réserver aux correctifs critiques (faille de sécurité…).");
|
||||
Console.Write("Publier cette version comme prioritaire ? (o/N) ");
|
||||
var answer = Console.ReadLine()?.Trim().ToLowerInvariant();
|
||||
if (answer is not ("o" or "oui"))
|
||||
throw new OperationCanceledException("Abandon : rien n'a été publié.");
|
||||
}
|
||||
|
||||
using var key = LoadPrivateKey(keyPath);
|
||||
var keyId = UpdateSignature.ComputeKeyId(key.ExportSubjectPublicKeyInfo());
|
||||
|
||||
@@ -172,10 +194,20 @@ public static class Program
|
||||
ChangelogParser.Parse(await File.ReadAllLinesAsync(notesFile), entry);
|
||||
}
|
||||
|
||||
// Historique : notes des versions déjà publiées (launcher-update.json précédent, signé par cette clé)
|
||||
var history = ReadHistory(previousJson, key, keyId, display);
|
||||
// launcher-update.json précédent, signé par cette clé : historique des notes et version prioritaire repris
|
||||
var previousRelease = ReadPreviousRelease(previousJson, key, keyId);
|
||||
var history = ReadHistory(previousRelease, display);
|
||||
var changelog = ChangelogLimits.Normalize(new[] { entry }.Concat(history));
|
||||
|
||||
// Version prioritaire : celle-ci (--priority), aucune (--clear-priority), sinon celle déjà publiée
|
||||
var priorityVersion = LauncherRelease.ResolvePriorityVersion(version, markPriority, clearPriority, previousRelease);
|
||||
if (previousRelease is null && previousJson is not null && !markPriority && !clearPriority &&
|
||||
TryReadUnverified(previousJson)?.PriorityVersion is { Length: > 0 } unverifiedPriority)
|
||||
Warn($"Version prioritaire {unverifiedPriority} du launcher-update.json précédent NON reprise (signature non vérifiée) : " +
|
||||
"relancez avec --priority si cette version doit rester imposée.");
|
||||
if (clearPriority && previousRelease?.PriorityVersion is { Length: > 0 } removedPriority)
|
||||
Warn($"Version prioritaire {removedPriority} retirée : seuls les joueurs ayant activé la mise à jour automatique recevront cette version.");
|
||||
|
||||
var release = new LauncherRelease
|
||||
{
|
||||
Product = product,
|
||||
@@ -187,7 +219,8 @@ public static class Program
|
||||
// Format historique, lu par les launchers antérieurs à 1.1.3
|
||||
Title = entry.Title,
|
||||
Notes = entry.Changes.ToList(),
|
||||
Changelog = changelog
|
||||
Changelog = changelog,
|
||||
PriorityVersion = priorityVersion
|
||||
};
|
||||
release.Validate();
|
||||
|
||||
@@ -219,6 +252,7 @@ public static class Program
|
||||
? " Notes : aucune (--notes non fourni ou fichier vide)"
|
||||
: $" Notes : « {current.Title ?? "sans titre"} », {current.Sections.Count} section(s), " +
|
||||
$"{current.Sections.Sum(x => x.Items.Count)} élément(s) ; historique : {release.Changelog.Count - 1} version(s)");
|
||||
Console.WriteLine($" Priorité : {DescribePriority(release)}");
|
||||
Console.WriteLine($" Produit : {product}");
|
||||
Console.WriteLine($" Clé : {keyId}");
|
||||
Console.WriteLine($" SHA-256 : {sha256}");
|
||||
@@ -245,6 +279,7 @@ public static class Program
|
||||
Console.WriteLine($" Version : {release.Version} ({release.PublishedAt:yyyy-MM-dd HH:mm})");
|
||||
Console.WriteLine($" Fichier : {release.File} ({Format.Bytes(release.Size)})");
|
||||
Console.WriteLine($" SHA-256 : {release.Sha256}");
|
||||
Console.WriteLine($" Priorité: {DescribePriority(release)}");
|
||||
|
||||
var exe = o.Get("--exe");
|
||||
if (exe is null) return true;
|
||||
@@ -295,25 +330,30 @@ public static class Program
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Notes des versions déjà publiées, reprises du launcher-update.json précédent — uniquement
|
||||
/// si sa signature est valide pour la clé utilisée (un fichier modifié à la main est ignoré).
|
||||
/// launcher-update.json précédent — uniquement si sa signature est valide pour la clé utilisée (un fichier
|
||||
/// modifié à la main est ignoré) : son historique des notes et sa version prioritaire sont repris.
|
||||
/// </summary>
|
||||
private static IReadOnlyList<ChangelogEntry> ReadHistory(string? previousJson, ECDsa key, string keyId, string version)
|
||||
private static LauncherRelease? ReadPreviousRelease(string? previousJson, ECDsa key, string keyId)
|
||||
{
|
||||
if (previousJson is null) return Array.Empty<ChangelogEntry>();
|
||||
LauncherRelease previous;
|
||||
if (previousJson is null) return null;
|
||||
try
|
||||
{
|
||||
previous = UpdateSignature.ReadRelease(previousJson, new[]
|
||||
return UpdateSignature.ReadRelease(previousJson, new[]
|
||||
{
|
||||
new UpdateSignature.TrustedKey(keyId, key.ExportSubjectPublicKeyInfo())
|
||||
});
|
||||
}
|
||||
catch (UpdateSecurityException ex)
|
||||
{
|
||||
Warn($"Historique des notes ignoré ({ex.Message}).");
|
||||
return Array.Empty<ChangelogEntry>();
|
||||
Warn($"launcher-update.json précédent ignoré ({ex.Message}) : historique des notes et version prioritaire non repris.");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Notes des versions déjà publiées (publication précédente vérifiée), sans la version en cours.</summary>
|
||||
private static IReadOnlyList<ChangelogEntry> ReadHistory(LauncherRelease? previous, string version)
|
||||
{
|
||||
if (previous is null) return Array.Empty<ChangelogEntry>();
|
||||
|
||||
// Version publiée avant le format enrichi : son titre et sa liste deviennent une entrée
|
||||
var entries = previous.Changelog.Count > 0
|
||||
@@ -322,6 +362,16 @@ public static class Program
|
||||
return entries.Where(e => !string.Equals(e.Version, version, StringComparison.OrdinalIgnoreCase)).ToList();
|
||||
}
|
||||
|
||||
/// <summary>Ligne « Priorité » affichée après publish et verify.</summary>
|
||||
private static string DescribePriority(LauncherRelease release)
|
||||
{
|
||||
if (!release.TryGetPriorityVersion(out var priority)) return "normale (installée selon le réglage de chaque joueur)";
|
||||
var display = VersionUtil.Display(priority);
|
||||
return release.IsPriorityRelease
|
||||
? "PRIORITAIRE — installée au démarrage même si la mise à jour automatique est désactivée (réglage du joueur conservé)"
|
||||
: $"normale, version prioritaire {display} reprise — les launchers antérieurs à {display} se mettent à jour quel que soit leur réglage";
|
||||
}
|
||||
|
||||
private static LauncherRelease? TryReadUnverified(string envelopeJson)
|
||||
{
|
||||
try
|
||||
@@ -509,7 +559,14 @@ public static class Program
|
||||
(--config <launcher.json> | --product <launcherId>)
|
||||
[--version x.y.z] [--notes <notes.txt>] [--title <texte>]
|
||||
[--public-key <update-signing.pub>] [--force]
|
||||
[--priority | --clear-priority] [--yes]
|
||||
Signe la nouvelle version et prépare le dossier à envoyer sur le serveur.
|
||||
--priority : version PRIORITAIRE (correctif critique), installée au démarrage même
|
||||
chez les joueurs qui ont désactivé la mise à jour automatique (leur
|
||||
réglage n'est pas modifié). Confirmation demandée, sauf --yes.
|
||||
Reprise par les publications suivantes : un launcher plus ancien que
|
||||
cette version se met à jour quoi qu'il arrive.
|
||||
--clear-priority : ne reprend pas la version prioritaire déjà publiée.
|
||||
|
||||
verify --manifest <launcher-update.json | https://…> --public-key <update-signing.pub> [--exe <fichier>]
|
||||
Vérifie la signature (et l'exécutable).
|
||||
|
||||
@@ -36,7 +36,8 @@ Les positions du rapport sont en unités WPF (multiplier par `--scale` pour les
|
||||
`main-maintenance-plain` sans précision, jeu non installé) ; `main-multi` : plusieurs jeux (exemple
|
||||
`launcher.multi-jeux.example.json`).
|
||||
* **Fenêtres** : paramètres du jeu et du launcher, notes de version agrandies, choix du dossier
|
||||
d'installation, mise à jour du launcher (téléchargement, version vérifiée, erreur), boîtes de dialogue
|
||||
d'installation, mise à jour du launcher (téléchargement, version vérifiée, version prioritaire
|
||||
`launcher-update-priority`, erreur), boîtes de dialogue
|
||||
(information, succès, question, avertissement, erreur, action irréversible avec option), menu de la zone
|
||||
de notification.
|
||||
* **Fenêtre « Aide »** (`feedback-*`, bouton de la barre de titre) : choix du type de demande, avis noté,
|
||||
|
||||
@@ -137,6 +137,7 @@ internal sealed class PlayerScenes : ISceneSet
|
||||
yield return new SceneInfo("install-dialog" + suffix, "Installation : choix du dossier");
|
||||
yield return new SceneInfo("launcher-update" + suffix, "Mise à jour du launcher · téléchargement");
|
||||
yield return new SceneInfo("launcher-update-verified" + suffix, "Mise à jour du launcher · version vérifiée");
|
||||
yield return new SceneInfo("launcher-update-priority" + suffix, "Mise à jour du launcher · version prioritaire");
|
||||
yield return new SceneInfo("launcher-update-error" + suffix, "Mise à jour du launcher · erreur");
|
||||
foreach (var (id, title, _) in Dialogs) yield return new SceneInfo(id + suffix, title);
|
||||
yield return new SceneInfo("tray-menu" + suffix, "Menu de la zone de notification");
|
||||
@@ -290,6 +291,21 @@ internal sealed class PlayerScenes : ISceneSet
|
||||
return Task.FromResult<Window>(new LauncherUpdateWindow { DataContext = viewModel });
|
||||
}, CloseUpdateWindowAsync);
|
||||
|
||||
// Version prioritaire (correctif critique) : pastille « Prioritaire » et texte dédié
|
||||
var priorityRelease = SampleData.CreateLauncherRelease(_fixture.CurrentVersion);
|
||||
priorityRelease.PriorityVersion = priorityRelease.Version;
|
||||
var priorityCheck = new LauncherUpdateCheck(_fixture.CurrentVersion, priorityRelease,
|
||||
new Uri("https://jeux.exemple.test/launcher/GameLauncher.exe"));
|
||||
await capture.ShootAsync("launcher-update-priority" + suffix, "Mise à jour du launcher · version prioritaire", () =>
|
||||
{
|
||||
_fixture.ApplyTheme(theme);
|
||||
var viewModel = new LauncherUpdateViewModel(priorityCheck, automatic: true);
|
||||
viewModel.Progress.Report(new OperationProgress("Téléchargement de la nouvelle version",
|
||||
$"{Format.Bytes(25_800_000)} / {Format.Bytes(priorityRelease.Size)} • {Format.Speed(6_300_000)} • " +
|
||||
$"{Format.Duration(TimeSpan.FromSeconds(2))} restantes", 67.5));
|
||||
return Task.FromResult<Window>(new LauncherUpdateWindow { DataContext = viewModel });
|
||||
}, CloseUpdateWindowAsync);
|
||||
|
||||
await capture.ShootAsync("launcher-update-error" + suffix, "Mise à jour du launcher · erreur", () =>
|
||||
{
|
||||
_fixture.ApplyTheme(theme);
|
||||
|
||||
+38
-52
@@ -1,9 +1,9 @@
|
||||
# Launcher 3.3.1
|
||||
# Launcher 3.4.0
|
||||
LAUNCHER - NOTES DE MISE À JOUR
|
||||
Correctif du mode maintenance - Mise à jour du 10 novembre 2026
|
||||
==================================================================
|
||||
Mises à jour prioritaires du launcher - Mise à jour du 10 novembre 2026
|
||||
=========================================================================
|
||||
|
||||
Un launcher resté ouvert pendant la re-signature du trousseau ne voyait pas la maintenance activée ensuite : chaque relecture (toutes les 2 minutes) la refusait jusqu'au redémarrage. Il prend désormais le nouveau trousseau en compte tout seul, et le relit aussi régulièrement quand il reste ouvert longtemps.
|
||||
Une version du launcher peut désormais être publiée comme prioritaire, pour un correctif critique (faille de sécurité…) : elle s'installe au démarrage même si vous avez désactivé la mise à jour automatique du launcher. Votre réglage n'est pas modifié : les versions normales suivantes ne s'installeront toujours pas toutes seules.
|
||||
|
||||
Aucune action sur le serveur. Le launcher se met à jour automatiquement.
|
||||
|
||||
@@ -11,58 +11,40 @@ Aucune action sur le serveur. Le launcher se met à jour automatiquement.
|
||||
------------------------------------------------------------------
|
||||
EN BREF
|
||||
------------------------------------------------------------------
|
||||
- CORRIGÉ : mode maintenance activé après la re-signature du
|
||||
trousseau non vu par un launcher déjà ouvert (redémarrage
|
||||
nécessaire en 3.3.0).
|
||||
- AMÉLIORÉ : trousseau retéléchargé quand un contenu est signé par
|
||||
une clé connue mais pas encore autorisée pour ce contenu.
|
||||
- SÉCURITÉ : trousseau relu au plus toutes les 30 minutes : un
|
||||
appareil retiré n'est plus accepté par un launcher laissé ouvert.
|
||||
- QUALITÉ : 3 nouveaux tests automatisés (370 au total).
|
||||
- NOUVEAU : version prioritaire, installée au démarrage même avec
|
||||
la mise à jour automatique du launcher désactivée.
|
||||
- NOUVEAU : pastille « Prioritaire » dans la fenêtre de mise à
|
||||
jour, avertissement dans le bandeau et les paramètres tant
|
||||
qu'elle n'est pas installée.
|
||||
- SÉCURITÉ : l'indication « prioritaire » est signée, comme le
|
||||
reste de la mise à jour.
|
||||
- QUALITÉ : nouveaux tests automatisés.
|
||||
|
||||
|
||||
------------------------------------------------------------------
|
||||
CORRECTIF
|
||||
VERSION PRIORITAIRE
|
||||
------------------------------------------------------------------
|
||||
- Cause : le launcher gardait en mémoire le trousseau lu au
|
||||
démarrage. Il ne le retéléchargeait que pour une clé inconnue,
|
||||
pas pour une clé connue qui vient de recevoir une autorisation
|
||||
(« mode maintenance » ajoutée à l'étape 3 decies).
|
||||
- Effet en 3.3.0 : « la clé … n'est pas autorisée à signer ce
|
||||
contenu » dans le journal à chaque relecture, maintenance
|
||||
ignorée jusqu'au redémarrage.
|
||||
- Désormais : nouveau téléchargement du trousseau, au plus une
|
||||
fois par minute ; la maintenance s'affiche à la relecture
|
||||
suivante (2 minutes au plus).
|
||||
- Concerne aussi les formulaires « Aide », le catalogue et les
|
||||
versions des jeux signés par un appareil qui vient d'être
|
||||
autorisé.
|
||||
|
||||
|
||||
------------------------------------------------------------------
|
||||
VÉRIFICATIONS PÉRIODIQUES DU LAUNCHER
|
||||
------------------------------------------------------------------
|
||||
- Mode maintenance : au démarrage (4 s au plus), puis toutes les
|
||||
2 minutes (1 minute pendant une maintenance), et aussitôt après
|
||||
une publication depuis le menu de ce PC.
|
||||
- Versions des jeux (manifest) : au démarrage, puis toutes les
|
||||
30 minutes (updateCheckIntervalMinutes).
|
||||
- Trousseau : au démarrage ; après un échec réseau (1 minute) ;
|
||||
clé inconnue ou non autorisée (1 minute au plus) ; au plus
|
||||
toutes les 30 minutes (nouveau).
|
||||
- Catalogue : au démarrage seulement.
|
||||
- Mise à jour du launcher : au démarrage, et bouton « Rechercher »
|
||||
des paramètres.
|
||||
- Formulaires « Aide » : à l'ouverture de la fenêtre « Aide ».
|
||||
- Votre réglage « Mettre à jour le launcher automatiquement »
|
||||
n'est jamais modifié : effet ponctuel, le temps d'installer le
|
||||
correctif.
|
||||
- « Plus tard » reste possible : nouvelle tentative au prochain
|
||||
démarrage du launcher.
|
||||
- Une erreur de téléchargement est retentée au démarrage suivant.
|
||||
- Si la nouvelle version ne démarre pas, l'ancienne est remise en
|
||||
place et le bouton « Vérifier les mises à jour » permet de
|
||||
réessayer.
|
||||
- Mise à jour automatique désactivée : le launcher vérifie
|
||||
désormais au démarrage si une version prioritaire existe.
|
||||
|
||||
|
||||
------------------------------------------------------------------
|
||||
SÉCURITÉ
|
||||
------------------------------------------------------------------
|
||||
- Chaque trousseau retéléchargé passe par les mêmes contrôles :
|
||||
signature de la clé racine, numéro jamais en baisse, minimum
|
||||
keyringMinSequence ; un ancien trousseau resservi est refusé.
|
||||
- Serveur injoignable : dernier trousseau vérifié conservé.
|
||||
- L'indication « prioritaire » fait partie du contenu signé : un
|
||||
serveur compromis ne peut ni imposer une mise à jour, ni
|
||||
retirer la priorité d'un correctif.
|
||||
- Seule une version signée et strictement supérieure peut être
|
||||
installée, comme avant.
|
||||
|
||||
|
||||
------------------------------------------------------------------
|
||||
@@ -74,11 +56,15 @@ SERVEUR
|
||||
------------------------------------------------------------------
|
||||
DÉVELOPPEMENT
|
||||
------------------------------------------------------------------
|
||||
- Core : ContentTrustService (KeyChangeRetryDelay, KeyringMaxAge,
|
||||
KeyMayBeNewer).
|
||||
- Tests : ContentTrustTests (2), MaintenanceServiceTests (1).
|
||||
- Documentation : README, docs/ADMIN-ARCHITECTURE.md,
|
||||
server/deploy/LISEZMOI.md (étapes 3 quinquies et 3 decies).
|
||||
- publish-launcher-update.bat --priority (version prioritaire,
|
||||
confirmation demandée) et --clear-priority (ne reprend pas la
|
||||
priorité déjà publiée).
|
||||
- LauncherPublisher : options --priority, --clear-priority, --yes ;
|
||||
ligne « Priorité » dans publish et verify.
|
||||
- Core : LauncherRelease.PriorityVersion (reprise d'une
|
||||
publication à l'autre), LauncherUpdateCheck.IsPriority.
|
||||
- UiSnapshot : scène launcher-update-priority.
|
||||
- Tests : LauncherPriorityTests.
|
||||
|
||||
|
||||
La sécurité des mises à jour du launcher ne change pas : la signature numérique et l'empreinte SHA-256 de chaque nouvelle version sont vérifiées avant toute installation.
|
||||
@@ -7,8 +7,41 @@ REM 3. (optionnel) l'envoie sur le VPS avec WinSCP
|
||||
REM
|
||||
REM Avant de lancer : augmentez <Version> dans src\GameLauncher\GameLauncher.csproj
|
||||
REM et ecrivez les nouveautes dans tools\notes-launcher.txt.
|
||||
REM
|
||||
REM Parametres (facultatifs, depuis une invite de commandes) :
|
||||
REM publish-launcher-update.bat version normale (double-clic)
|
||||
REM publish-launcher-update.bat --priority version PRIORITAIRE (correctif critique) :
|
||||
REM installee au demarrage MEME chez les joueurs qui ont desactive la mise a jour
|
||||
REM automatique du launcher. Leur reglage (self-update.json) n'est pas modifie.
|
||||
REM Reprise par les publications suivantes : un launcher plus ancien que cette
|
||||
REM version se met a jour quoi qu'il arrive. Confirmation demandee.
|
||||
REM publish-launcher-update.bat --clear-priority ne reprend pas la version prioritaire
|
||||
REM deja publiee (priorite publiee par erreur).
|
||||
REM ---------------------------------------------------------------------------
|
||||
setlocal
|
||||
set PRIORITY=
|
||||
|
||||
:args
|
||||
if "%~1"=="" goto :argsdone
|
||||
if /i "%~1"=="--priority" goto :opt_priority
|
||||
if /i "%~1"=="--clear-priority" goto :opt_clear
|
||||
if /i "%~1"=="--help" goto :usage
|
||||
if "%~1"=="/?" goto :usage
|
||||
echo Parametre inconnu : %~1
|
||||
goto :usage
|
||||
:opt_priority
|
||||
if defined PRIORITY goto :conflict
|
||||
set PRIORITY=--priority
|
||||
REM shift /1 : %0 (donc %~dp0) reste inchange
|
||||
shift /1
|
||||
goto :args
|
||||
:opt_clear
|
||||
if defined PRIORITY goto :conflict
|
||||
set PRIORITY=--clear-priority
|
||||
shift /1
|
||||
goto :args
|
||||
:argsdone
|
||||
|
||||
set ROOT=%~dp0..
|
||||
set KEY=%USERPROFILE%\.launcher-keys\launcher-signing.key
|
||||
set OUTPUT=C:\Users\solac\Documents\Game Laucher\GameLauncher_V2\Publish\Updates
|
||||
@@ -16,6 +49,18 @@ REM Script WinSCP (laisser vide pour envoyer les fichiers a la main) :
|
||||
set WINSCP_SCRIPT=C:\Users\solac\Documents\Game Laucher\GameLauncher_V2\Publish\publier-launcher-update.txt
|
||||
set WINSCP="C:\Program Files (x86)\WinSCP\WinSCP.com"
|
||||
|
||||
if /i "%PRIORITY%"=="--priority" (
|
||||
echo ***************************************************************************
|
||||
echo Version PRIORITAIRE : installee au demarrage meme chez les joueurs qui ont
|
||||
echo desactive la mise a jour automatique du launcher. Leur reglage ne change pas.
|
||||
echo ***************************************************************************
|
||||
echo.
|
||||
)
|
||||
if /i "%PRIORITY%"=="--clear-priority" (
|
||||
echo Version normale : la version prioritaire deja publiee n'est PAS reprise.
|
||||
echo.
|
||||
)
|
||||
|
||||
echo === 1/3 Compilation du launcher ===
|
||||
dotnet publish "%ROOT%\src\GameLauncher\GameLauncher.csproj" -c Release -p:PublishProfile=win-x64
|
||||
if errorlevel 1 goto :error
|
||||
@@ -28,6 +73,7 @@ dotnet run --project "%ROOT%\tools\LauncherPublisher\LauncherPublisher.csproj" -
|
||||
--config "%ROOT%\src\GameLauncher\launcher.json" ^
|
||||
--public-key "%ROOT%\src\GameLauncher\Security\update-signing.pub" ^
|
||||
--notes "%~dp0notes-launcher.txt" ^
|
||||
%PRIORITY% ^
|
||||
--output "%OUTPUT%"
|
||||
if errorlevel 1 goto :error
|
||||
|
||||
@@ -52,3 +98,15 @@ echo.
|
||||
echo La publication a echoue.
|
||||
pause
|
||||
exit /b 1
|
||||
|
||||
:conflict
|
||||
echo --priority et --clear-priority sont incompatibles.
|
||||
exit /b 1
|
||||
|
||||
:usage
|
||||
echo.
|
||||
echo Utilisation : publish-launcher-update.bat [--priority ^| --clear-priority]
|
||||
echo --priority version prioritaire (correctif critique) : installee meme si le
|
||||
echo joueur a desactive la mise a jour automatique (reglage conserve)
|
||||
echo --clear-priority ne reprend pas la version prioritaire deja publiee
|
||||
exit /b 1
|
||||
Reference in new issue
Block a user