cache->hosts()); $hosts = array_map('strval', $hosts); $master = (string)$c->cache->master(); usort($hosts, fn ($a, $b) => [$b === $master, $c->cache->name($a)] <=> [$a === $master, $c->cache->name($b)]); $rows = []; $total = 0; $reboots = []; foreach ($hosts as $host) { $state = Updates::of($c, $host); if ($state->packages === null && !$state->checking && $state->error === '' && $c->isConnected()) { Updates::check($c, $host); // never asked: ask now } $packages = $state->packages ?? []; $total += count($packages); $reboot = $state->rebootRequired($c, $host); if ($reboot) { $reboots[] = $host; } $summary = match (true) { $state->installing => 'Installing updates…', $state->checking && $state->packages === null => 'Checking for updates…', $state->error !== '' => 'Check failed: ' . $state->error, $packages === [] && $reboot => 'Up to date · reboot required', $packages === [] => $state->packages === null ? 'Not checked yet' : 'Up to date', default => count($packages) . (count($packages) === 1 ? ' update' : ' updates') . ($reboot ? ' · reboot required' : '') . (Updates::needsReboot($packages) ? ' (a reboot will be needed)' : ''), }; $h = $c->cache->get($host) ?? []; $rows[] = ['id' => $host, 'icon' => Icons::of('host', $h, $c->cache), 'cells' => [(string)($h['name_label'] ?? $host) . ($host === $master ? ' (pool master)' : ''), $summary, $reboot ? 'Updated: ' . implode(', ', $state->rebootFor) : '', ''], 'color' => $state->error !== '' ? '#b42318' : ($reboot ? '#9a6700' : null), 'sort' => [sprintf('%s%04d', $host === $master ? '0' : '1', count($rows))]]; foreach ($packages as $p) { $from = Updates::version($p, 'current_'); $to = Updates::version($p); $note = trim((string)($p['description'] ?? '')); $log = is_array($p['changelog'] ?? null) ? trim((string)($p['changelog']['description'] ?? '')) : trim((string)($p['changelog'] ?? '')); $rows[] = ['id' => 'update:' . $host . ':' . $p['name'], 'icon' => preg_match(Updates::REBOOT_PACKAGES, $p['name']) ? 'warning' : 'info', 'cells' => [' ' . $p['name'], $from !== '' ? "$from → $to" : "→ $to", $note . ($log !== '' && $log !== $note ? ' — ' . $log : ''), isset($p['size']) ? Units::bytes((float)$p['size']) : ''], 'muted' => false, 'sort' => [sprintf('%s%04d', $host === $master ? '0' : '1', count($rows))]]; } } $actions = [['id' => 'updates.check', 'text' => 'Check for Updates', 'style' => 'primary']]; if ($kind === 'host') { $actions[] = ['id' => 'updates.install', 'text' => 'Install Updates…', 'enabled' => $total > 0 && !Updates::of($c, $ref)->installing]; if ($reboots !== []) { $actions[] = ['id' => 'host.reboot', 'text' => 'Reboot Now', 'enabled' => $c->cache->vmsOn($ref) === [], 'style' => 'danger']; } } else { $actions[] = ['id' => 'updates.rolling', 'text' => 'Rolling Pool Update…', 'enabled' => $total > 0]; } return Page::grid([['title' => 'Server / package', 'width' => 2.0], ['title' => 'Version (installed → new)', 'width' => 3.0], ['title' => 'Description', 'width' => 3.2], ['title' => 'Size', 'width' => 0.8]], $rows, $actions, 'No server: connect first.'); } }