feat : versionnage semver, migrations contenu, bandeau mise à jour admin
- CHANGELOG.md : structure semver (1.0.0 / 1.1.0 / 1.2.0) remplace le journal non versionné - public/version.txt : généré à chaque push depuis la première entrée CHANGELOG - scripts/push.sh : extrait la version CHANGELOG avant git add - src/UpdateChecker.php : compare version déployée vs version Gitea (raw file), cache 1 h - templates/layout.php : bandeau alerte admin (nouvelle version / migrations en attente) - templates/admin.php : dashboard moteur Folio (version déployée / disponible) - scripts/migrate_content.php + migration_001 : ajout # titre dans les articles existants - templates/maintenance.php : page HTTP 503 pendant une migration - src/helpers.php : extractMarkdownTitle(), normalisation \r\n dans lineDiff() - templates/wizard/step1.php : suppression champ titre, plan TOC dynamique - public/assets/js/wizard.js : scope titleEl, scrollToCursor, buildToc, handlers externalisés Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,138 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* Vérifie si une mise à jour de Folio est disponible sur le dépôt Git,
|
||||
* et si des migrations de contenu sont en attente.
|
||||
*
|
||||
* Aucune dépendance externe : utilise file_get_contents() + cache JSON.
|
||||
*/
|
||||
class UpdateChecker
|
||||
{
|
||||
private string $dataDir;
|
||||
private string $baseDir;
|
||||
|
||||
public function __construct(string $dataDir, string $baseDir)
|
||||
{
|
||||
$this->dataDir = $dataDir;
|
||||
$this->baseDir = $baseDir;
|
||||
}
|
||||
|
||||
/** Retourne la liste des alertes à afficher aux administrateurs. */
|
||||
public function adminNotices(): array
|
||||
{
|
||||
$notices = [];
|
||||
|
||||
if ($this->hasPendingContentMigrations()) {
|
||||
$notices[] = [
|
||||
'type' => 'warning',
|
||||
'message' => 'Des migrations de contenu sont en attente.',
|
||||
];
|
||||
}
|
||||
|
||||
$update = $this->checkRemoteVersion();
|
||||
if ($update !== null) {
|
||||
$notices[] = [
|
||||
'type' => 'info',
|
||||
'message' => 'Une nouvelle version de Folio est disponible : <strong>v' . htmlspecialchars($update) . '</strong>.',
|
||||
];
|
||||
}
|
||||
|
||||
return $notices;
|
||||
}
|
||||
|
||||
// ─── Migrations de contenu en attente ────────────────────────────────────
|
||||
|
||||
private function hasPendingContentMigrations(): bool
|
||||
{
|
||||
$trackFile = $this->dataDir . '/.content_migrations.json';
|
||||
$applied = [];
|
||||
if (file_exists($trackFile)) {
|
||||
$applied = json_decode((string) file_get_contents($trackFile), true) ?? [];
|
||||
}
|
||||
foreach (glob($this->baseDir . '/scripts/content/migration_*.php') ?: [] as $f) {
|
||||
if (!isset($applied[basename($f)])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// ─── Vérification version distante (Gitea) ───────────────────────────────
|
||||
|
||||
/**
|
||||
* Retourne le numéro de la version disponible si elle est supérieure
|
||||
* à la version déployée, null sinon.
|
||||
*/
|
||||
private function checkRemoteVersion(): ?string
|
||||
{
|
||||
$repoUrl = rtrim((string) ($_ENV['FOLIO_REPO_URL'] ?? getenv('FOLIO_REPO_URL') ?: ''), '/');
|
||||
if ($repoUrl === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
$deployedFile = $this->baseDir . '/public/version.txt';
|
||||
if (!file_exists($deployedFile)) {
|
||||
return null;
|
||||
}
|
||||
$deployedVer = trim((string) file_get_contents($deployedFile));
|
||||
if ($deployedVer === '' || !preg_match('/^\d+\.\d+\.\d+/', $deployedVer)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$remoteVer = $this->fetchRemoteVersion($repoUrl);
|
||||
if ($remoteVer === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return version_compare($remoteVer, $deployedVer, '>') ? $remoteVer : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère `public/version.txt` depuis le dépôt Gitea (branche main).
|
||||
* Résultat mis en cache 1 h dans `data/.version_check_cache.json`.
|
||||
*/
|
||||
private function fetchRemoteVersion(string $repoUrl): ?string
|
||||
{
|
||||
$cacheFile = $this->dataDir . '/.version_check_cache.json';
|
||||
$ttl = 3600;
|
||||
|
||||
if (file_exists($cacheFile)) {
|
||||
$cache = json_decode((string) file_get_contents($cacheFile), true) ?? [];
|
||||
if (isset($cache['fetched_at'], $cache['version'])
|
||||
&& (time() - (int) $cache['fetched_at']) < $ttl
|
||||
) {
|
||||
return (string) $cache['version'];
|
||||
}
|
||||
}
|
||||
|
||||
// URL du fichier brut : {repo}/raw/branch/main/public/version.txt
|
||||
$rawUrl = $repoUrl . '/raw/branch/main/public/version.txt';
|
||||
|
||||
$token = (string) ($_ENV['GITEA_TOKEN'] ?? getenv('GITEA_TOKEN') ?: '');
|
||||
$opts = [
|
||||
'http' => [
|
||||
'timeout' => 5,
|
||||
'header' => $token !== '' ? "Authorization: token $token" : '',
|
||||
],
|
||||
];
|
||||
|
||||
$body = @file_get_contents($rawUrl, false, stream_context_create($opts));
|
||||
if ($body === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$version = trim($body);
|
||||
if (!preg_match('/^\d+\.\d+\.\d+/', $version)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
file_put_contents(
|
||||
$cacheFile,
|
||||
json_encode(['fetched_at' => time(), 'version' => $version]) . "\n"
|
||||
);
|
||||
|
||||
return $version;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user