af0a0bb9d5
Le bouton "Mettre à jour" appelle désormais `sudo /usr/local/bin/folio-upgrade.sh` via exec() plutôt que d'exécuter git pull + composer + migrations directement en PHP. Le script shell (template dans scripts/server/) gère la séquence complète : clone fresh, permissions www-data, restauration .env, composer install, migrations SQL, .sessions, safe.directory. Le journal de la dernière mise à jour est conservé dans DATA_PATH/.upgrade-log et affiché en <details> dans l'admin. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
172 lines
5.2 KiB
PHP
172 lines
5.2 KiB
PHP
<?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 = folioRepoUrl();
|
|
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;
|
|
}
|
|
|
|
public function getBranch(): string
|
|
{
|
|
return folioUpdateBranch();
|
|
}
|
|
|
|
public function getLastChecked(): ?int
|
|
{
|
|
$cacheFile = $this->dataDir . '/.version_check_cache.json';
|
|
if (!file_exists($cacheFile)) {
|
|
return null;
|
|
}
|
|
$cache = json_decode((string) file_get_contents($cacheFile), true) ?? [];
|
|
return isset($cache['fetched_at']) ? (int) $cache['fetched_at'] : null;
|
|
}
|
|
|
|
public function clearCache(): void
|
|
{
|
|
$cacheFile = $this->dataDir . '/.version_check_cache.json';
|
|
if (file_exists($cacheFile)) {
|
|
unlink($cacheFile);
|
|
}
|
|
}
|
|
|
|
public function getLastUpgradeLog(): ?string
|
|
{
|
|
$logFile = $this->dataDir . '/.upgrade-log';
|
|
if (!file_exists($logFile)) {
|
|
return null;
|
|
}
|
|
return (string) file_get_contents($logFile);
|
|
}
|
|
|
|
/**
|
|
* Récupère `public/version.txt` depuis le dépôt Gitea.
|
|
* 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'];
|
|
}
|
|
}
|
|
|
|
$branch = $this->getBranch();
|
|
// URL du fichier brut : {repo}/raw/branch/{branch}/public/version.txt
|
|
$rawUrl = $repoUrl . '/raw/branch/' . $branch . '/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;
|
|
}
|
|
}
|