readInstalledVersionNorm(); $currentRaw = $this->readInstalledVersionRaw() ?? ($currentNorm ? 'v'.$currentNorm : null); $appPath = base_path(); // www-data hat keine git-Credentials für private Repos. // Fallback-Kette (erste funktionierende Methode gewinnt): // 1. mailwolt-update --check-only (verfügbar wenn update.sh neu genug, sudoers: mailwolt-update) // 2. mailwolt-fetch-tags (verfügbar ab v1.1.268) // Sicherheitscheck: --check-only nur aufrufen wenn das Skript das Flag auch kennt. $updateBin = '/usr/local/sbin/mailwolt-update'; $fetchHelper = '/usr/local/sbin/mailwolt-fetch-tags'; // Beide versuchen — --check-only schreibt version_remote, fetch-tags als Fallback $fetched = false; if (file_exists($updateBin) && str_contains((string) @file_get_contents($updateBin), '--check-only')) { $result = @shell_exec('sudo -n ' . escapeshellarg($updateBin) . ' --check-only 2>/dev/null'); $fetched = ($result !== null); } if (!$fetched && file_exists($fetchHelper)) { @shell_exec('sudo -n ' . escapeshellarg($fetchHelper) . ' 2>/dev/null'); } // Version_remote wurde von einem der Helfer geschrieben; frisch genug wenn < 2h alt $remoteFile = '/var/lib/mailwolt/version_remote'; $remoteRaw = ''; if (file_exists($remoteFile) && (time() - filemtime($remoteFile)) < 7200) { $remoteRaw = trim((string) file_get_contents($remoteFile)); } $latestTagRaw = $remoteRaw !== '' ? $remoteRaw : trim((string) shell_exec("git -C " . escapeshellarg($appPath) . " tag -l 'v*' --sort=-v:refname 2>/dev/null | head -n1")); $latestNorm = $this->normalizeVersion($latestTagRaw); // Nichts gefunden -> alles leeren if (!$latestNorm) { cache()->forget('updates:latest'); cache()->forget('updates:latest_raw'); cache()->forget('mailwolt.update_available'); // legacy $this->warn('Keine Release-Tags gefunden.'); return self::SUCCESS; } // Nur wenn wirklich neuer als installiert -> Keys setzen if ($currentNorm && version_compare($latestNorm, $currentNorm, '>')) { cache()->forever('updates:latest', $latestNorm); cache()->forever('updates:latest_raw', $latestTagRaw ?: ('v'.$latestNorm)); cache()->forever('mailwolt.update_available', $latestNorm); // legacy-kompat $this->info("Update verfügbar: {$latestTagRaw} (installiert: ".($currentRaw ?? $currentNorm).")"); } else { // Kein Update -> Keys löschen cache()->forget('updates:latest'); cache()->forget('updates:latest_raw'); cache()->forget('mailwolt.update_available'); // legacy $this->info("Aktuell (installiert: ".($currentRaw ?? $currentNorm ?? 'unbekannt').")."); } cache()->put('updates:last_checked_at', now(), now()->addMinutes(10)); return self::SUCCESS; } /* ===== Helpers ===== */ private function readInstalledVersionNorm(): ?string { // Lokal: git describe gibt immer den aktuellen Stand if (app()->isLocal()) { $tag = @trim((string) shell_exec('git -C ' . escapeshellarg(base_path()) . ' describe --tags --abbrev=0 2>/dev/null')); $v = $this->normalizeVersion($tag); if ($v) return $v; } $paths = [ '/var/lib/mailwolt/version', base_path('VERSION'), ]; foreach ($paths as $p) { $raw = @trim(@file_get_contents($p) ?: ''); if ($raw !== '') return $this->normalizeVersion($raw); } $raw = $this->readInstalledVersionRaw(); return $raw ? $this->normalizeVersion($raw) : null; } private function readInstalledVersionRaw(): ?string { $p = '/var/lib/mailwolt/version_raw'; // vom Wrapper (z.B. "v1.0.25" oder "v1.0.25-3-gabcd") $raw = @trim(@file_get_contents($p) ?: ''); return $raw !== '' ? $raw : null; } private function normalizeVersion(?string $v): ?string { if (!$v) return null; $v = trim($v); if ($v === '') return null; $v = ltrim($v, "vV \t\n\r\0\x0B"); // führendes v entfernen $v = preg_replace('/-.*$/', '', $v); // Build-/dirty-Suffix abschneiden return $v !== '' ? $v : null; } }