readInstalledVersionNorm(); $currentRaw = $this->readInstalledVersionRaw() ?? ($currentNorm ? 'v'.$currentNorm : null); $appPath = base_path(); // Fallback-Kette für Remote-Tags (erste funktionierende Methode gewinnt): // 1. mailwolt-update --check-only (sudoers: mailwolt-update) // 2. mailwolt-fetch-tags (sudoers: mailwolt-fetch-tags) // 3. git fetch --tags als App-User direkt $updateBin = '/usr/local/sbin/mailwolt-update'; $fetchHelper = '/usr/local/sbin/mailwolt-fetch-tags'; $fetched = false; if (file_exists($updateBin) && str_contains((string) @file_get_contents($updateBin), '--check-only')) { @exec('sudo -n ' . escapeshellarg($updateBin) . ' --check-only 2>/dev/null', $_, $rc); $fetched = ($rc === 0); } if (!$fetched && file_exists($fetchHelper)) { @exec('sudo -n ' . escapeshellarg($fetchHelper) . ' 2>/dev/null', $_, $rc); $fetched = ($rc === 0); } // Direkter git fetch als Fallback (klappt wenn Credentials im Repo hinterlegt sind) if (!$fetched) { @exec('git -C ' . escapeshellarg($appPath) . ' fetch --tags origin 2>/dev/null', $_, $rc); } // version_remote von Helfer geschrieben; als frisch 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)); } // Lokale Tags (nach fetch hoffentlich aktuell) $out = []; @exec("git -C " . escapeshellarg($appPath) . " tag -l 'v*' --sort=-v:refname 2>/dev/null", $out); $latestTagRaw = $remoteRaw !== '' ? $remoteRaw : trim($out[0] ?? ''); $latestNorm = $this->normalizeVersion($latestTagRaw); if (!$latestNorm) { cache()->forget('updates:latest'); cache()->forget('updates:latest_raw'); cache()->forget('mailwolt.update_available'); $this->warn('Keine Release-Tags gefunden.'); return self::SUCCESS; } 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); $this->info("Update verfügbar: {$latestTagRaw} (installiert: ".($currentRaw ?? $currentNorm).")"); } else { cache()->forget('updates:latest'); cache()->forget('updates:latest_raw'); cache()->forget('mailwolt.update_available'); $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 { // version_raw ist die zuverlässigste Quelle – wird vom Update-Script geschrieben $rawVer = $this->normalizeVersion($this->readInstalledVersionRaw() ?? ''); $fileVer = null; foreach (['/var/lib/mailwolt/version', base_path('VERSION')] as $p) { $raw = @trim(@file_get_contents($p) ?: ''); if ($raw !== '') { $fileVer = $this->normalizeVersion($raw); break; } } // version_raw bevorzugen (echter installierter Stand) // Nur wenn version_raw fehlt: version-Datei oder git-Tag als Fallback if ($rawVer) { return $rawVer; } // git-Tag als letzter Fallback (funktioniert auf Dev-Servern) $out = []; @exec('git -C ' . escapeshellarg(base_path()) . ' describe --tags --abbrev=0 2>/dev/null', $out); $gitVer = $this->normalizeVersion(trim($out[0] ?? '')); if ($gitVer && (!$fileVer || version_compare($gitVer, $fileVer, '>'))) { return $gitVer; } return $fileVer ?: null; } private function readInstalledVersionRaw(): ?string { $p = '/var/lib/mailwolt/version_raw'; $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"); $v = preg_replace('/-.*$/', '', $v); return $v !== '' ? $v : null; } }