v1.5.0-rc.26
Full Changelog: v1.5.0-rc.25...v1.5.0-rc.26
[1.5.0-rc.26] — 2026-05-22
Fixed
-
Image reference construction — unanchored
/v2strip could silently corrupt references when the image name contained a/v2path segment.Registry.getImageFullNameand the controller-mode fallback inresolveContainerImageFullNameboth applied.replace(/\/v2/, '')to the fully concatenatedregistryUrl/imageName:tagstring. Because the regex was unanchored and non-global, if the image name contained a/v2segment (e.g.library/v2/tool) the strip would remove it from the image name rather than the registry URL — producing a silently wrong reference handed to Trivy. The fix extracts a shared pure helperbuildImageReference(app/registries/image-reference.ts) that cleans the registry URL before concatenation using anchored regexes (^https?:\/\/and/v2\/?$) so the URL scheme and trailing/v2API path are removed without touching anything in the image name. BothRegistry.getImageFullNameand the fallback branch ofresolveContainerImageFullNamenow delegate to this helper, eliminating the duplicate logic. -
#386 — Agents intermittently showing 0 running containers in the controller UI — a second recurrence the rc.25 fix did not close. The rc.25 fix suppressed the authoritative watcher snapshot whenever container enumeration failed or per-container enrichment errors dropped containers, but the recurrence reported on rc.25 is a different failure mode: a cold-start race between the controller's handshake and the agent's first watch cycle. When the controller's
AgentClient(re)connects to an agent's SSE stream it handshakes immediately viaGET /api/containers; if the agent'swatchatstartcron has not yet finished its first run, the agent's in-memory store is still empty and the handshake legitimately receives 0 containers (the agent log showsHandshake successful. Received 0 containers.~5 s beforeCron finished (4 containers watched, 0 errors)). The handshake then firesemitAgentConnected, the UI re-fetches/api/v1/agents, and the agent's running-container count renders 0. When the agent's cron completes moments later it pushes add:watcher-snapshot, andAgentClient.handleWatcherSnapshotEventingests the four containers into the controller store correctly — but nothing told the UI to refresh, becauseAgentsViewonly re-fetches the agent summary onagent-status-changed/connected/resync-requiredevents, not oncontainer-added/container-updated. The stale 0 therefore persisted until an unrelated reconnect event (such as an agent restart) fired. The fix adds a dedicatedAgentStatsChangedevent:app/event/index.tsgainsemitAgentStatsChanged/registerAgentStatsChanged(mirroring the existingAgentConnectedpair);AgentClient.handleWatcherSnapshotEventnow emitsemitAgentStatsChanged({ agentName })after every completed watcher snapshot;app/api/sse.tsbroadcasts it to UI SSE clients asdd:agent-stats-changed; andui/src/stores/eventStream.tsmaps that to the existingagent-status-changedbus event. A completed agent watch cycle therefore always refreshes the controller's agent-summary count, even when the handshake raced ahead of the agent's first cron. -
#342 — A container is no longer shown as "update available" with a blank target version after a transient registry error.
hasRawUpdateinapp/model/container.tscomparedtransformTag(image.tag.value)againsttransformTag(result.tag)without guarding an undefinedresult.tag. When a registry scan failed mid-flight (for example a Docker Hub or GHCR429) and left a containerresultpresent but itstagunset,transformTag(undefined)returnedundefined, thelocalTag !== remoteTagcomparison evaluated true, and the container was flaggedupdateAvailablewith anunknownupdate kind — which the UI renders as an update with no target version (the reporter saw this onimmich_redis).hasRawUpdatenow performs the tag comparison only when bothimage.tag.valueandresult.tagare defined, matching the existing guard ingetRawTagUpdate. Digest-only updates are unaffected: a container with an undefinedresult.tagbut a genuine digest change still reports the digest update. -
#386 follow-through — the controller's agent-summary container count now also refreshes on docker-event-driven container changes, not only completed cron cycles. The initial #386 fix emitted
emitAgentStatsChangedfromAgentClient.handleWatcherSnapshotEvent, the cron-watch path. An agent also ingests individual container add/remove/update events from the Docker event stream between cron cycles (handleContainerChangeEvent,handleContainerRemovedEvent), via the controller-initiatedwatch()path, and via the per-container controller-initiatedwatchContainer()path — none of which emitted the stats-changed signal, so a container started or stopped on an agent host could leave theAgentsViewrunning-container count stale until the next 6-hourly cron. All four paths now emitemitAgentStatsChangedafter mutating the controller store, keeping the count current in real time. -
#342 — GitHub release-notes lookups now survive GitHub's secondary rate limit instead of giving up on the first burst. Drydock authenticates its api.github.com release-notes requests by reusing the configured GHCR token, but a watch cycle still fans out a lookup for every watched container at once and trips GitHub's secondary rate limit — a
403GitHub returns to authenticated callers who burst too many requests. The shared retry helper (app/registries/http-retry.ts) only retried429/503, so the secondary-limit403was never retried: the provider loggedGitHub release notes lookup is rate-limitedand returned nothing.withRetrygains two optional, opt-in hooks —retryPredicate(retry a status outsideretryableStatuses) andretryDelayMs(per-attempt delay override) — leaving every existing caller unchanged.GithubProviderclassifies a403as a secondary rate limit only when it carries aretry-afterheader orx-ratelimit-remaining: 0, retries those (honouringretry-after/x-ratelimit-resetfor the delay), and leaves a genuine403authorization failure failing fast as before. Once retries are exhausted the provider arms a short module-level cooldown — driven by GitHub's own retry hint, floored at the 60 s default so aretry-after: 0hint cannot produce an already-expired cooldown — during which further release-notes lookups are skipped, so a single cron cycle no longer hammers an already-tripped limit container after container. The rate-limit warning now also records whether the request was authenticated. -
#342 — the registry-error tooltip on the Containers view now names the registry that failed. When a registry tag lookup errors (for example a
429rate limit) the container shows a registry-error badge whose tooltip previously rendered only the raw transport message —Registry error: Request failed with status code 429— with no indication of which registry was queried.registryErrorTooltipinui/src/views/ContainersView.vuenow derives the registry hostname from the container'sregistryUrland renders it through a newregistryError.detailWithRegistryi18n string ({registryHost} — {error}), e.g.ghcr.io — Request failed with status code 429. Containers whoseregistryUrlis absent or unparseable fall back to the original message unchanged.