Skip to content

feat: list pending package updates on the system page - #2427

Merged
henrygd merged 3 commits into
henrygd:mainfrom
anandghegde:feat/package-updates-list
Sep 27, 2026
Merged

henrygd merged 3 commits into
henrygd:mainfrom
anandghegde:feat/package-updates-list

Conversation

@anandghegde

Copy link
Copy Markdown
Contributor

📃 Description

Follow-up to #2357, which added pending update counts (pu: [total, security]). This PR adds the list behind those counts, the rest of what #617 asked for.

Agent

  • The background check keeps the per-package results, not just the counts. It uses the same commands as before, and the counts come from the same parse, so the badge and the table always agree.

  • A new GetPackageUpdates action returns the result of the last check. It never starts a check, runs a package manager or refreshes metadata. That still happens only on the PACKAGE_UPDATES_INTERVAL schedule.

  • What each package manager provides:

    Manager Installed version Available version Security flag per package
    apt apt-get -s dist-upgrade same yes (-security origin)
    dnf rpm -q --qf for the listed packages check-update yes (check-update --security, matched by name.arch)
    zypper list-updates same no, security stays a count from list-patches
    pacman checkupdates same no
    apk apk -u list same no
  • dnf's check-update does not print installed versions. The agent makes one extra rpm -q call with the listed name.arch values. It is local only, with no network or metadata access.

  • The dnf security count is now the number of listed packages flagged by --security, not the line count of the --security output. With the captured Rocky 9 sample these agree (53).

  • PACKAGE_UPDATES_INTERVAL parsing is moved into its own function and logs the effective value, like SMART_INTERVAL does:

    • It accepts a Go duration (30m, 6h); the default is 1h.
    • 0 disables the checks.
    • Invalid, negative or unitless values keep the default and log a warning.
    • BESZEL_AGENT_PACKAGE_UPDATES_INTERVAL takes precedence, as for other agent variables.

Hub

  • GET /api/beszel/package-updates?system=<id> follows the same pattern as /systemd/info:
    • The system is fetched live from the agent, over WebSocket or SSH.
    • Nothing is stored in the database.
    • The route uses the same HasUser check.

Site

  • A "Package Updates" table on the system page, in both the default and tabbed layouts. It shows package, current version, available version, change type and, when the manager reports it, a security flag.
  • The table appears only when the system is up and info.pu[0] > 0. It is lazy-loaded and refetches only when the counts change, so no request is made per realtime tick.
  • The change type is parsed on the frontend (src/lib/package-updates.ts) and set to major, minor, patch or revision:
    • Any epoch is stripped. The revision is the text after the last -.
    • The first differing numeric component of the upstream version decides the type.
    • If only the revision differs, the type is "revision", e.g. 5.15.0-91.101 → 5.15.0-92.102 and 2.35-0ubuntu3.4 → 2.35-0ubuntu3.15.
  • It falls back to "other" instead of guessing for:
    • a missing current version
    • epoch changes
    • calendar-style versions (leading component ≥ 1000, e.g. tzdata or ca-certificates)
    • ~/+ suffix-only changes
    • non-numeric versions
    • downgrades

📖 Documentation

PACKAGE_UPDATES_INTERVAL needs a line in the agent environment variable docs. I can open that PR against beszel-docs once this is settled.

🪵 Changelog

➕ Added

  • Pending package updates table on the system page (package, current and available version, change type, security flag where available)
  • GetPackageUpdates agent action and GET /api/beszel/package-updates hub route

✏️ Changed

  • PACKAGE_UPDATES_INTERVAL parsing is tested, logs the effective value, and 0 disables the checks
  • dnf security count is the number of listed packages flagged by --security

Testing

  • Go parser tests use real command output captured in containers (Ubuntu 22.04, Debian 12, Rocky 9 / dnf4, Fedora 42 / dnf5, openSUSE Leap, Alpine 3.20, plus a pacman checkupdates sample). New rpm -q captures come from the same Rocky and Fedora images. The tests cover:
    • dnf4 wrapped long names
    • the Obsoleting section
    • rpm "is not installed" lines
    • zypper column lookup by header
    • apk names that contain dashes
  • checkDnf and checkZypper are tested end to end against fake dnf/rpm/zypper binaries on PATH.
  • Other Go tests cover:
    • interval parsing (unset, valid, compound, zero, 0s, negative, unitless, invalid, empty, prefixed precedence)
    • the manager's caching
    • the new handler
    • hub API auth and validation cases for the new route
  • bun test covers the version classifier with the formats listed above.
  • go test -tags='testing no_ui' passes for the agent, hub and entities packages. The exceptions are 6 GPU/sensor tests in agent/ that fail the same way on main on macOS.
  • golangci-lint --new-from-rev reports 0 issues, biome check is clean, and bun run build succeeds.

Compatibility note

An agent without this change ignores the new action, so the request would wait for the 5 s timeout. The table only renders when pu is present, and pu is new in #2357, which isn't in a release yet. So as long as both ship in the same release, an old agent never gets the request. If they end up in different releases, I can add a MinVersion gate like the ZFS one.

Keep the per-package results of the background package manager check
and return them through a new GetPackageUpdates action, exposed by the
hub as GET /api/beszel/package-updates. The handler only returns the
cached result of the last check, so the request never runs a package
manager command or refreshes repository metadata.

Each package has its name, installed version, available version and,
where the package manager reports it, a security flag:

- apt: versions and security repo from `apt-get -s dist-upgrade`
- dnf: installed versions from `rpm -q` for the listed packages, security
  flags from `check-update --security` matched by name.arch
- zypper: versions from `list-updates`; security stays a count because
  patches are not mapped to packages
- pacman: versions from checkupdates
- apk: versions from `apk -u list`

Move the PACKAGE_UPDATES_INTERVAL parsing into its own function, log the
effective value like SMART_INTERVAL does, and cover it with tests.
Show the package list on the system page when the agent reports pending
updates. The table refetches only when the update counts change, and
classifies each update as major, minor, patch or revision from the
installed and available versions. Epoch changes, calendar versions,
pre-release suffixes and anything else that can't be compared safely
fall back to "other".

@henrygd henrygd left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! There are only a few small changes I would make, which I can do after merge. Thanks very much!

@henrygd
henrygd merged commit 8613cfe into henrygd:main Sep 27, 2026
ivanch added a commit to ivanch/haven that referenced this pull request Oct 4, 2026
This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
| [henrygd/beszel-agent](https://github.com/henrygd/beszel) | minor | `0.20.0` → `0.21.0` |

---

### Release Notes

<details>
<summary>henrygd/beszel (henrygd/beszel-agent)</summary>

### [`v0.21.0`](https://github.com/henrygd/beszel/releases/tag/v0.21.0)

[Compare Source](henrygd/beszel@v0.20.0...v0.21.0)

#### What's Changed

- Add pending package updates list to the system page ([#&#8203;2357](henrygd/beszel#2357), [#&#8203;2427](henrygd/beszel#2427))
- Add Wi-Fi signal strength monitoring ([#&#8203;2367](henrygd/beszel#2367))
- Add TLS certificate expiry check to HTTPS network monitors ([#&#8203;2401](henrygd/beszel#2401))
- Add systemd service logs to service details ([#&#8203;2440](henrygd/beszel#2440))
- Add option to set the DNS server queried by DNS monitors ([#&#8203;2389](henrygd/beszel#2389))
- Add network monitor comparison across systems and within the same system ([#&#8203;2442](henrygd/beszel#2442), [#&#8203;2477](henrygd/beszel#2477))
- Add `SYNC_SYSTEM_NAMES` environment variable to sync system names with hostnames ([#&#8203;1917](henrygd/beszel#1917))
- Add `DISABLE_SSH` option to the hub to skip SSH fallback ([#&#8203;2469](henrygd/beszel#2469))
- Add environment variable to disable container image update checks ([#&#8203;2371](henrygd/beszel#2371))
- Add MB/s and GB/s unit selection to bandwidth alerts
- Add log timestamp toggle ([#&#8203;2471](henrygd/beszel#2471))
- Add view settings to the network monitors table ([#&#8203;2396](henrygd/beszel#2396))
- Add Twemoji Country Flags font so flags always render ([#&#8203;2363](henrygd/beszel#2363))
- Change active alerts grid to a banner and sheet ([#&#8203;2445](henrygd/beszel#2445))
- Change network packet loss to a red line ([#&#8203;2377](henrygd/beszel#2377))
- Improve Intel GPU monitoring by parsing `intel_gpu_top` JSON output ([#&#8203;2387](henrygd/beszel#2387))
- Improve LXC guest CPU usage reporting using cgroup accounting ([#&#8203;2341](henrygd/beszel#2341))
- Improve WebSocket and SSH fallback handling ([#&#8203;2441](henrygd/beszel#2441))
- Fix LVM volumes not mapping to disk I/O by resolving device symlinks ([#&#8203;2486](henrygd/beszel#2486))
- Fix ZFS pool I/O stats to include snapshot reads ([#&#8203;2474](henrygd/beszel#2474))
- Fix inaccurate disk I/O on the first sample of an interval ([#&#8203;2420](henrygd/beszel#2420))
- Fix Windows volume names being altered when matching I/O devices ([#&#8203;2419](henrygd/beszel#2419))
- Fix 32-bit wrap of disk I/O time counters ([#&#8203;2407](henrygd/beszel#2407))
- Fix partial NVMe SMART data handling ([#&#8203;2340](henrygd/beszel#2340))
- Fix NVMe power-cycle SMART fields overflowing ([#&#8203;2484](henrygd/beszel#2484))
- Fix `nvethernet` network counters ([#&#8203;2090](henrygd/beszel#2090))
- Fix agent not reconnecting after an async WebSocket handshake failure ([#&#8203;2329](henrygd/beszel#2329), [#&#8203;2326](henrygd/beszel#2326))
- Fix WebSocket reconnect loops on slow agent collections ([#&#8203;2294](henrygd/beszel#2294))
- Fix `SKIP_GPU` not excluding GPU hwmon temperatures and fans ([#&#8203;2313](henrygd/beszel#2313))
- Fix Docker image update check across all image repository digests ([#&#8203;2393](henrygd/beszel#2393))
- Fix status alerts being lost during pending recovery ([#&#8203;2375](henrygd/beszel#2375))
- Fix hub requiring system access for alert subscriptions and delivery ([#&#8203;2455](henrygd/beszel#2455))
- Fix hub not honoring SSH request cancellation ([#&#8203;2450](henrygd/beszel#2450))
- Fix hub not restarting `beszel-hub.service` after update ([#&#8203;2410](henrygd/beszel#2410))
- Fix IP targets being accepted for DNS monitors ([#&#8203;2483](henrygd/beszel#2483))
- Fix short monitor IDs failing schema validation ([#&#8203;2454](henrygd/beszel#2454))
- Fix network monitor charts to show gaps for failed probes and disconnects ([#&#8203;2428](henrygd/beszel#2428))
- Fix duplicate y-axis labels on network monitor response charts ([#&#8203;2426](henrygd/beszel#2426))
- Fix translations not refreshing on locale change ([#&#8203;2405](henrygd/beszel#2405))
- Fix scrollbar layout shifts ([#&#8203;2406](henrygd/beszel#2406))
- Fix hub returning 200 for unknown frontend routes ([#&#8203;2414](henrygd/beszel#2414))
- Fix proxy caching of systemd details ([#&#8203;2456](henrygd/beszel#2456))
- Fix agent not exiting with code 0 when run without a key on Windows ([#&#8203;2376](henrygd/beszel#2376), [#&#8203;2247](henrygd/beszel#2247))
- Fix OpenWrt user account handling in the install script ([#&#8203;2370](henrygd/beszel#2370))
- Update Go dependencies

#### New Contributors

- [@&#8203;adrienboulle](https://github.com/adrienboulle) made their first contribution in [#&#8203;2397](henrygd/beszel#2397)
- [@&#8203;Mario-Mohar](https://github.com/Mario-Mohar) made their first contribution in [#&#8203;2410](henrygd/beszel#2410)
- [@&#8203;LudwigJMarx](https://github.com/LudwigJMarx) made their first contribution in [#&#8203;2407](henrygd/beszel#2407)
- [@&#8203;anandghegde](https://github.com/anandghegde) made their first contribution in [#&#8203;2357](henrygd/beszel#2357)
- [@&#8203;Yi-111-a](https://github.com/Yi-111-a) made their first contribution in [#&#8203;2419](henrygd/beszel#2419)
- [@&#8203;lukyjay](https://github.com/lukyjay) made their first contribution in [#&#8203;2387](henrygd/beszel#2387)
- [@&#8203;user01010111](https://github.com/user01010111) made their first contribution in [#&#8203;2461](henrygd/beszel#2461)
- [@&#8203;Juankers](https://github.com/Juankers) made their first contribution in [#&#8203;2470](henrygd/beszel#2470)
- [@&#8203;Bartuzen](https://github.com/Bartuzen) made their first contribution in [#&#8203;2484](henrygd/beszel#2484)
- [@&#8203;jeroendewilde](https://github.com/jeroendewilde) made their first contribution in [#&#8203;2471](henrygd/beszel#2471)

**Full Changelog**: <henrygd/beszel@v0.20.0...v0.21.0>

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate CLI](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC4xMTUuMTIiLCJ1cGRhdGVkSW5WZXIiOiI0NC4xMTUuMTIiLCJ0YXJnZXRCcmFuY2giOiJtYWluIiwibGFiZWxzIjpbXX0=-->

---------

Co-authored-by: Renovate Bot <[email protected]>
Reviewed-on: https://git.ivanch.me/ivanch/haven/pulls/21
ivanch added a commit to ivanch/haven that referenced this pull request Oct 4, 2026
This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
| [ghcr.io/henrygd/beszel/beszel](https://github.com/henrygd/beszel) | minor | `0.20.0` → `0.21.0` |

---

### Release Notes

<details>
<summary>henrygd/beszel (ghcr.io/henrygd/beszel/beszel)</summary>

### [`v0.21.0`](https://github.com/henrygd/beszel/releases/tag/v0.21.0)

[Compare Source](henrygd/beszel@v0.20.0...v0.21.0)

#### What's Changed

- Add pending package updates list to the system page ([#&#8203;2357](henrygd/beszel#2357), [#&#8203;2427](henrygd/beszel#2427))
- Add Wi-Fi signal strength monitoring ([#&#8203;2367](henrygd/beszel#2367))
- Add TLS certificate expiry check to HTTPS network monitors ([#&#8203;2401](henrygd/beszel#2401))
- Add systemd service logs to service details ([#&#8203;2440](henrygd/beszel#2440))
- Add option to set the DNS server queried by DNS monitors ([#&#8203;2389](henrygd/beszel#2389))
- Add network monitor comparison across systems and within the same system ([#&#8203;2442](henrygd/beszel#2442), [#&#8203;2477](henrygd/beszel#2477))
- Add `SYNC_SYSTEM_NAMES` environment variable to sync system names with hostnames ([#&#8203;1917](henrygd/beszel#1917))
- Add `DISABLE_SSH` option to the hub to skip SSH fallback ([#&#8203;2469](henrygd/beszel#2469))
- Add environment variable to disable container image update checks ([#&#8203;2371](henrygd/beszel#2371))
- Add MB/s and GB/s unit selection to bandwidth alerts
- Add log timestamp toggle ([#&#8203;2471](henrygd/beszel#2471))
- Add view settings to the network monitors table ([#&#8203;2396](henrygd/beszel#2396))
- Add Twemoji Country Flags font so flags always render ([#&#8203;2363](henrygd/beszel#2363))
- Change active alerts grid to a banner and sheet ([#&#8203;2445](henrygd/beszel#2445))
- Change network packet loss to a red line ([#&#8203;2377](henrygd/beszel#2377))
- Improve Intel GPU monitoring by parsing `intel_gpu_top` JSON output ([#&#8203;2387](henrygd/beszel#2387))
- Improve LXC guest CPU usage reporting using cgroup accounting ([#&#8203;2341](henrygd/beszel#2341))
- Improve WebSocket and SSH fallback handling ([#&#8203;2441](henrygd/beszel#2441))
- Fix LVM volumes not mapping to disk I/O by resolving device symlinks ([#&#8203;2486](henrygd/beszel#2486))
- Fix ZFS pool I/O stats to include snapshot reads ([#&#8203;2474](henrygd/beszel#2474))
- Fix inaccurate disk I/O on the first sample of an interval ([#&#8203;2420](henrygd/beszel#2420))
- Fix Windows volume names being altered when matching I/O devices ([#&#8203;2419](henrygd/beszel#2419))
- Fix 32-bit wrap of disk I/O time counters ([#&#8203;2407](henrygd/beszel#2407))
- Fix partial NVMe SMART data handling ([#&#8203;2340](henrygd/beszel#2340))
- Fix NVMe power-cycle SMART fields overflowing ([#&#8203;2484](henrygd/beszel#2484))
- Fix `nvethernet` network counters ([#&#8203;2090](henrygd/beszel#2090))
- Fix agent not reconnecting after an async WebSocket handshake failure ([#&#8203;2329](henrygd/beszel#2329), [#&#8203;2326](henrygd/beszel#2326))
- Fix WebSocket reconnect loops on slow agent collections ([#&#8203;2294](henrygd/beszel#2294))
- Fix `SKIP_GPU` not excluding GPU hwmon temperatures and fans ([#&#8203;2313](henrygd/beszel#2313))
- Fix Docker image update check across all image repository digests ([#&#8203;2393](henrygd/beszel#2393))
- Fix status alerts being lost during pending recovery ([#&#8203;2375](henrygd/beszel#2375))
- Fix hub requiring system access for alert subscriptions and delivery ([#&#8203;2455](henrygd/beszel#2455))
- Fix hub not honoring SSH request cancellation ([#&#8203;2450](henrygd/beszel#2450))
- Fix hub not restarting `beszel-hub.service` after update ([#&#8203;2410](henrygd/beszel#2410))
- Fix IP targets being accepted for DNS monitors ([#&#8203;2483](henrygd/beszel#2483))
- Fix short monitor IDs failing schema validation ([#&#8203;2454](henrygd/beszel#2454))
- Fix network monitor charts to show gaps for failed probes and disconnects ([#&#8203;2428](henrygd/beszel#2428))
- Fix duplicate y-axis labels on network monitor response charts ([#&#8203;2426](henrygd/beszel#2426))
- Fix translations not refreshing on locale change ([#&#8203;2405](henrygd/beszel#2405))
- Fix scrollbar layout shifts ([#&#8203;2406](henrygd/beszel#2406))
- Fix hub returning 200 for unknown frontend routes ([#&#8203;2414](henrygd/beszel#2414))
- Fix proxy caching of systemd details ([#&#8203;2456](henrygd/beszel#2456))
- Fix agent not exiting with code 0 when run without a key on Windows ([#&#8203;2376](henrygd/beszel#2376), [#&#8203;2247](henrygd/beszel#2247))
- Fix OpenWrt user account handling in the install script ([#&#8203;2370](henrygd/beszel#2370))
- Update Go dependencies

#### New Contributors

- [@&#8203;adrienboulle](https://github.com/adrienboulle) made their first contribution in [#&#8203;2397](henrygd/beszel#2397)
- [@&#8203;Mario-Mohar](https://github.com/Mario-Mohar) made their first contribution in [#&#8203;2410](henrygd/beszel#2410)
- [@&#8203;LudwigJMarx](https://github.com/LudwigJMarx) made their first contribution in [#&#8203;2407](henrygd/beszel#2407)
- [@&#8203;anandghegde](https://github.com/anandghegde) made their first contribution in [#&#8203;2357](henrygd/beszel#2357)
- [@&#8203;Yi-111-a](https://github.com/Yi-111-a) made their first contribution in [#&#8203;2419](henrygd/beszel#2419)
- [@&#8203;lukyjay](https://github.com/lukyjay) made their first contribution in [#&#8203;2387](henrygd/beszel#2387)
- [@&#8203;user01010111](https://github.com/user01010111) made their first contribution in [#&#8203;2461](henrygd/beszel#2461)
- [@&#8203;Juankers](https://github.com/Juankers) made their first contribution in [#&#8203;2470](henrygd/beszel#2470)
- [@&#8203;Bartuzen](https://github.com/Bartuzen) made their first contribution in [#&#8203;2484](henrygd/beszel#2484)
- [@&#8203;jeroendewilde](https://github.com/jeroendewilde) made their first contribution in [#&#8203;2471](henrygd/beszel#2471)

**Full Changelog**: <henrygd/beszel@v0.20.0...v0.21.0>

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate CLI](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC4xMTUuMTIiLCJ1cGRhdGVkSW5WZXIiOiI0NC4xMTUuMTIiLCJ0YXJnZXRCcmFuY2giOiJtYWluIiwibGFiZWxzIjpbXX0=-->

---------

Co-authored-by: Renovate Bot <[email protected]>
Reviewed-on: https://git.ivanch.me/ivanch/haven/pulls/20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants