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

@@ -24,6 +24,7 @@ if (!is_file($manifestPath)) {
$manifest = json_decode(file_get_contents($manifestPath), true, 512, JSON_THROW_ON_ERROR);
$updated = 0;
$hashedVersions = [];
foreach ($manifest['versions'] as &$v) {
$version = $v['version'] ?? '?';
$url = $v['download']['url'] ?? '';
@@ -60,9 +61,23 @@ foreach ($manifest['versions'] as &$v) {
$v['download']['sizeBytes'] = $size;
$v['download']['sha256'] = $sha;
$updated++;
$hashedVersions[] = $version;
}
unset($v);
// Met à jour automatiquement le champ `latest` avec la plus haute version
// effectivement uploadée (celle pour laquelle on a calculé un hash).
if (!empty($hashedVersions)) {
usort($hashedVersions, function ($a, $b) {
return version_compare($a, $b);
});
$newLatest = end($hashedVersions);
if (($manifest['latest'] ?? null) !== $newLatest) {
echo " [latest] {$manifest['latest']} -> {$newLatest}\n";
$manifest['latest'] = $newLatest;
}
}
// (v0.4) Signature Ed25519 — pour l'instant on laisse 'signature' = null.
// $config = require __DIR__ . '/../api/config.php';
// $sk = sodium_hex2bin($config['ed25519']['private_key_hex']);