backoffice: dedicated Launcher page, scope-aware Sync

The launcher and the game versions are different release cadences
managed by the same operator. Mixing them on one page muddied the
workflow. Split them into two top-level nav entries.

SignManifest::run(string $scope)
--------------------------------
- 'all' (default, used by CLI tools/sign-manifest.php) — unchanged.
- 'versions' — only re-hashes the Proserve ZIPs and bumps `latest`.
- 'launcher' — only re-hashes the launcher EXE.
The Ed25519 sign step always runs at the end so the manifest stays
verifiable. Selectively hashing avoids unrelated noise (e.g. mass-hash
14 GB ZIPs when all you wanted was to update the launcher exe).

admin/launcher.php (new page)
-----------------------------
Self-contained page with the launcher state, Set/Remove forms, the
blue "🔁 Hasher le launcher + signer" button, and a list of the .exe
files present in builds/launcher/. Workflow doc inline.

admin/versions.php
------------------
Cleaned up: launcher card and its set_launcher / remove_launcher /
sync_launcher actions removed. The remaining global Sync button is
relabeled and now triggers scope='versions' (only Proserve ZIPs).

Layout::navHtml gains a "Launcher" item between Versions and Audit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-02 10:53:14 +02:00
parent 55ce5318ae
commit 56069f606d
4 changed files with 207 additions and 117 deletions

View File

@@ -26,13 +26,12 @@ final class SignManifest
private function out(string $line): void { $this->log[] = $line; }
/**
* Recompute sizes / sha256 for every version that has its ZIP uploaded,
* bump 'latest' to the highest signed version, then sign the manifest
* with the Ed25519 private key from config.php (if available).
* Recompute sizes / sha256 selon le scope, puis re-signe le manifest avec Ed25519.
*
* @param string $scope 'all' (défaut) | 'versions' (ne touche que les Proserve) | 'launcher'
* @return array{ok:bool, log:string[]}
*/
public function run(): array
public function run(string $scope = 'all'): array
{
if (!is_file($this->manifestPath)) {
$this->out("Manifest introuvable : {$this->manifestPath}");
@@ -45,8 +44,11 @@ final class SignManifest
return ['ok' => false, 'log' => $this->log];
}
$doVersions = ($scope === 'all' || $scope === 'versions');
$doLauncher = ($scope === 'all' || $scope === 'launcher');
$hashedVersions = [];
foreach ($manifest['versions'] as &$v) {
if ($doVersions) foreach ($manifest['versions'] as &$v) {
$version = $v['version'] ?? '?';
$url = $v['download']['url'] ?? '';
if ($url === '') {
@@ -84,7 +86,7 @@ final class SignManifest
// Section launcher : si présente, on tente de hasher le PSLauncher-{ver}.exe
// dans builds/launcher/. On ignore proprement si l'exe n'est pas là.
if (isset($manifest['launcher']) && is_array($manifest['launcher'])) {
if ($doLauncher && isset($manifest['launcher']) && is_array($manifest['launcher'])) {
$launcher = &$manifest['launcher'];
$lver = $launcher['version'] ?? '';
$lurl = $launcher['download']['url'] ?? '';
@@ -110,7 +112,8 @@ final class SignManifest
}
// Bump auto de `latest` sur la plus haute version effectivement uploadée
if (!empty($hashedVersions)) {
// (uniquement si on a touché aux versions)
if ($doVersions && !empty($hashedVersions)) {
usort($hashedVersions, 'version_compare');
$newLatest = end($hashedVersions);
if (($manifest['latest'] ?? null) !== $newLatest) {