feat: show Ubuntu phased updates apart and leave them out of pending counts
apt lists phased updates as upgradable while an upgrade defers them until Ubuntu selects the host, so a freshly patched server kept reporting pending updates. The agent now flags them; the server stores the flag and leaves them out of patch run counts, and the server page shows them in their own section.
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
import { useState } from "react";
|
||||
import Link from "next/link";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { api, ServerWithKeys } from "@/lib/api";
|
||||
import { api, installableUpdates, ServerWithKeys } from "@/lib/api";
|
||||
import { Badge, Button, Card, ConfirmDialog, Table, Tbody, Td, Th, Thead, Tr } from "@/components/ui";
|
||||
import { matchesTags } from "@/lib/targets";
|
||||
import { RUN_STATUS } from "@/components/patching/status";
|
||||
@@ -41,7 +41,10 @@ export function MaintenanceTab({
|
||||
const [copied, setCopied] = useState(false);
|
||||
const [confirmDelete, setConfirmDelete] = useState(false);
|
||||
|
||||
const updates = server.available_updates ?? [];
|
||||
// Phased updates are listed by apt but deferred until Ubuntu selects this
|
||||
// host, so they are shown apart and never count as pending.
|
||||
const installable = installableUpdates(server.available_updates);
|
||||
const phased = (server.available_updates ?? []).filter((u) => u.phased);
|
||||
const command = api.getUpdateCommand(server.os_info);
|
||||
const isWindows = server.os_info?.toLowerCase().includes("windows");
|
||||
const agentCurrent = !!latestVersion && !!server.agent_version && server.agent_version === latestVersion;
|
||||
@@ -63,7 +66,8 @@ export function MaintenanceTab({
|
||||
<div className="flex flex-wrap items-center justify-between gap-3 border-b border-border px-6 py-4">
|
||||
<h2 className="text-lg font-semibold text-text-primary">OS updates</h2>
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
{updates.length > 0 ? <Badge variant="warning">{updates.length} pending</Badge> : <Badge variant="success">up to date</Badge>}
|
||||
{installable.length > 0 ? <Badge variant="warning">{installable.length} pending</Badge> : <Badge variant="success">up to date</Badge>}
|
||||
{phased.length > 0 && <Badge variant="neutral">{phased.length} phased</Badge>}
|
||||
{/* Sits with the updates panel because that is what
|
||||
caused it. The agent never reboots a host itself. */}
|
||||
{server.inventory?.reboot_required && <Badge variant="warning">reboot required</Badge>}
|
||||
@@ -88,7 +92,7 @@ export function MaintenanceTab({
|
||||
)}
|
||||
</div>
|
||||
|
||||
{updates.length === 0 ? (
|
||||
{installable.length === 0 ? (
|
||||
<p className="px-6 py-10 text-center text-sm text-text-secondary">
|
||||
{isWindows ? "No pending Windows updates. The agent checks hourly." : "No pending package updates. The agent checks hourly."}
|
||||
</p>
|
||||
@@ -106,7 +110,7 @@ export function MaintenanceTab({
|
||||
</Tr>
|
||||
</Thead>
|
||||
<Tbody>
|
||||
{updates.map((u) => (
|
||||
{installable.map((u) => (
|
||||
<Tr key={u.name}>
|
||||
<Td label={isWindows ? "Update" : "Package"}>
|
||||
<span className="font-mono text-sm font-medium">{u.name}</span>
|
||||
@@ -131,6 +135,16 @@ export function MaintenanceTab({
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{phased.length > 0 && (
|
||||
<div className="border-t border-border px-6 py-4">
|
||||
<p className="text-sm text-text-secondary">
|
||||
<span className="text-text-primary">{phased.length} deferred by Ubuntu phasing.</span> Ubuntu releases these to a share of machines at a time and
|
||||
apt holds them back until this server is selected, usually within a few days. They install on a later run; nothing needs doing.
|
||||
</p>
|
||||
<p className="mt-2 font-mono text-xs text-text-tertiary">{phased.map((u) => u.name).join(", ")}</p>
|
||||
</div>
|
||||
)}
|
||||
</Card>
|
||||
|
||||
<div className="space-y-6">
|
||||
|
||||
Reference in New Issue
Block a user