Don't trust manifest.latest, no-cache the manifest fetch

Two robustness fixes after a real-world miss where v1.4.7 was uploaded but
the launcher kept reporting v1.4.6 as latest:

1. UpdateChecker: ignore the `latest` field of the manifest entirely.
   Always pick the highest SemVer in the versions[] array (filtered by
   availableForDownload). Removes a class of "I forgot to bump latest"
   bugs at the server.

2. ManifestService: send Cache-Control: no-cache, no-store + Pragma:
   no-cache when fetching. The user explicitly clicked "Check for
   updates", they want fresh data — bypass any intermediate cache
   (browser-style HTTP cache, OVH static handler default 2-day expires).

3. sign-manifest.php: after hashing the uploaded ZIPs, auto-update
   `manifest.latest` to the highest version that actually has a ZIP
   on the server. Prevents the same drift the client now ignores, but
   keeps the field meaningful for any consumer that reads it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-01 09:26:06 +02:00
parent b8ac2488fe
commit 3a00b0e677
3 changed files with 31 additions and 5 deletions

View File

@@ -26,10 +26,13 @@ public sealed class UpdateChecker : IUpdateChecker
try
{
var manifest = await _manifestService.FetchAsync(ct).ConfigureAwait(false);
var latest = manifest.Versions.FirstOrDefault(v => v.Version == manifest.Latest)
?? manifest.Versions
.OrderByDescending(v => SemVer.Parse(v.Version))
.FirstOrDefault();
// On ignore le champ `manifest.Latest` (trop facile à oublier au serveur).
// On prend toujours la plus haute version SemVer disponible et téléchargeable.
var latest = manifest.Versions
.Where(v => v.AvailableForDownload)
.OrderByDescending(v => SemVer.Parse(v.Version))
.FirstOrDefault();
var installed = _registry.Scan();
var latestInstalled = installed