Fix : SignManifest et listing ZIPs supportent les sous-dossiers de channel

v0.26.0 générait des URLs builds/{channel}/proserve-X.Y.Z.zip dans le
manifest pour les channels non-default, mais SignManifest faisait juste
basename(url) → cherchait builds/proserve-X.Y.Z.zip à plat → ZIP introuvable
au moment du Sync.

Fixes :

1. SignManifest::resolveZipPath() : extrait tout le chemin après /builds/
   dans l'URL et le mappe sur {$buildsDir}/. Strip ../ pour anti-traversal
   même si le manifest est de toute façon signé Ed25519.

2. SignManifest fallback fuzzy étendu à 1 niveau de sous-dossier (glob */*.zip)
   pour le cas "admin a renommé le ZIP".

3. admin/versions.php $zips inclut maintenant builds/ + builds/*/ et stocke
   les chemins relatifs (asterion-vr/proserve-X.Y.Z.zip) au lieu du basename.
   Le check "référencé ?" matche aussi sur le chemin relatif.

4. Workflow text en haut de la page indique le bon chemin SFTP selon le
   channel actif (avec rappel "crée le sous-dossier si absent").

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-05 08:14:02 +02:00
parent 95c70903e7
commit 7ff5e56102
2 changed files with 75 additions and 11 deletions

View File

@@ -283,9 +283,23 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
}
$manifest = loadManifest($manifestPath);
$zips = is_dir($buildsDir) ? array_map('basename', glob("$buildsDir/*.zip") ?: []) : [];
// Liste des ZIPs : on inclut builds/ + builds/{channel}/ (1 niveau de
// sous-dossier) pour lister aussi les ZIPs d'un channel spécifique. Les
// chemins affichés sont relatifs à $buildsDir pour distinguer les ZIPs
// "racine" (default) des ZIPs en sous-dossier (asterion-vr/proserve-X.zip).
$zips = [];
$zipSizes = [];
foreach ($zips as $z) $zipSizes[$z] = filesize("$buildsDir/$z");
if (is_dir($buildsDir)) {
foreach (array_merge(
glob("$buildsDir/*.zip") ?: [],
glob("$buildsDir/*/*.zip") ?: []
) as $z) {
$rel = ltrim(str_replace($buildsDir, '', $z), '/\\');
$rel = str_replace('\\', '/', $rel);
$zips[] = $rel;
$zipSizes[$rel] = filesize($z);
}
}
// Pour l'édition : pré-charge les release notes existantes
$existingNotes = [];
@@ -342,7 +356,15 @@ Layout::header('Versions', 'versions');
<h2>Workflow d'une nouvelle release</h2>
<ol class="muted">
<li>Ajoute l'entrée du manifest avec le formulaire ci-dessous (version, date min de license, release notes).</li>
<li>Upload le ZIP correspondant via SFTP dans <code>www/PS_Launcher/builds/</code> en respectant le nom <code>proserve-{version}.zip</code>.</li>
<li>Upload le ZIP correspondant via SFTP en respectant le nom <code>proserve-{version}.zip</code>, dans le dossier qui correspond au channel actif :
<ul>
<?php if ($channel === ''): ?>
<li>Channel <strong>default</strong> → <code>www/PS_Launcher/builds/proserve-{version}.zip</code></li>
<?php else: ?>
<li>Channel <strong><?= htmlspecialchars($channel) ?></strong> → <code>www/PS_Launcher/builds/<?= htmlspecialchars($channel) ?>/proserve-{version}.zip</code> <span class="muted">(crée le sous-dossier en SFTP s'il n'existe pas)</span></li>
<?php endif; ?>
</ul>
</li>
<li>Clique <strong>🔁 Sync (sign-manifest)</strong> pour calculer le SHA-256, mettre à jour <code>sizeBytes</code>, bumper <code>latest</code>, et signer le manifest avec Ed25519.</li>
<li>Les clients PS_Launcher détecteront la nouvelle version au prochain « Vérifier les MAJ ».</li>
</ol>
@@ -581,12 +603,18 @@ Layout::header('Versions', 'versions');
<thead><tr><th>Fichier</th><th>Taille</th><th>Référencé ?</th></tr></thead>
<tbody>
<?php
$referencedNames = [];
// On référence avec le chemin relatif après /builds/ pour matcher
// les ZIPs en sous-dossier (channels). Ex. asterion-vr/proserve-1.4.6.zip.
$referencedRel = [];
foreach ($manifest['versions'] ?? [] as $v) {
$referencedNames[] = basename(parse_url($v['download']['url'], PHP_URL_PATH) ?? '');
$urlPath = parse_url($v['download']['url'] ?? '', PHP_URL_PATH) ?: '';
$pos = strpos($urlPath, '/builds/');
if ($pos !== false) {
$referencedRel[] = ltrim(substr($urlPath, $pos + strlen('/builds/')), '/\\');
}
}
foreach ($zips as $z):
$referenced = in_array($z, $referencedNames, true);
$referenced = in_array($z, $referencedRel, true);
?>
<tr>
<td><code><?= htmlspecialchars($z) ?></code></td>