diff --git a/containers/libreportal/frontend/components/apps/overview/js/overview-manager.js b/containers/libreportal/frontend/components/apps/overview/js/overview-manager.js index 77a4531..c2e0c41 100644 --- a/containers/libreportal/frontend/components/apps/overview/js/overview-manager.js +++ b/containers/libreportal/frontend/components/apps/overview/js/overview-manager.js @@ -607,9 +607,21 @@ class OverviewManager { const name = esc((window.getAppDisplayName ? window.getAppDisplayName(a.name) : null) || a.displayName || a.name); const cur = esc(a.current_version || a.current_image || '—'); const avail = a.update_available ? esc(a.available_version || a.available_image || 'newer') : null; + // "✓ Up to date" is the whole truth only when nothing newer exists at all. + // Once a newer release line is published, a green all-clear sitting beside + // the "1.159.0 available" chip reads as a flat contradiction. The two ARE + // reconcilable — you are current on the line you track, and moving lines is + // a deliberate act — but a fleet row is a glance, not a place to reconcile + // anything. So drop the reassurance and let the chip carry the specifics. + // (The per-app detail keeps "up to date" on purpose: there it comes with a + // sentence explaining the distinction and an Upgrade button.) const status = a.update_available ? `↑ Update available` - : (a.scanned ? `✓ Up to date` : `• Unscanned`); + : (!a.scanned + ? `• Unscanned` + : (a.newer_version + ? `• Newer version` + : `✓ Up to date`)); const sev = a.worstSeverity ? `${a.worstSeverity}` : ''; // Automatic is the default, so only the apps that opt OUT carry a marker — // 30-odd "auto" chips would be wallpaper, one "manual" chip is information. diff --git a/scripts/webui/data/generators/updater/webui_updater_scan.sh b/scripts/webui/data/generators/updater/webui_updater_scan.sh index ea3928d..e8c7662 100644 --- a/scripts/webui/data/generators/updater/webui_updater_scan.sh +++ b/scripts/webui/data/generators/updater/webui_updater_scan.sh @@ -290,6 +290,28 @@ updaterNewerVersionTag() { # Human-readable version for display: OCI label → tag (versioned) → tag·shortdigest. updaterDisplayVersion() { local oci="$1" channel="$2" vtype="$3" digest="$4" + # The OCI version label is usually the app's own version and reads far + # better than a bare tag ("0.63.2" beats "latest"), which is why it wins. + # But that label is INHERITED from whichever base image the vendor built on + # unless they overwrite it, so it can describe the OS instead of the app: + # mongo:8.0 carries org.opencontainers.image.version=24.04 — its Ubuntu + # base — and the row then called MongoDB "24.04" and offered "24.04 → 8.0" + # as an upgrade, which is not a version transition at all. + # + # When we track a VERSIONED tag we already hold an authoritative statement + # of the app's version, so the label only wins if the two agree on their + # leading number. That keeps the richer label where it genuinely is the + # app's (nextcloud 34 → 34.0.1) and rejects it where it plainly is not + # (mongo 8.0 vs 24.04). Rolling tags are unaffected: there is no version in + # "latest" to check against, and the label is the whole point there. + if [ -n "$oci" ] && [ "$vtype" = "versioned" ] && [ -n "$channel" ]; then + local _ln _cn + _ln="$(printf '%s' "$oci" | grep -oE '[0-9]+' | head -1)" + _cn="$(printf '%s' "$channel" | grep -oE '[0-9]+' | head -1)" + if [ -n "$_ln" ] && [ -n "$_cn" ] && [ "$((10#$_ln))" != "$((10#$_cn))" ]; then + oci="" + fi + fi if [ -n "$oci" ]; then printf '%s' "$oci"; return; fi if [ "$vtype" = "versioned" ] && [ -n "$channel" ]; then printf '%s' "$channel"; return; fi local short="${digest#sha256:}"; short="${short:0:7}"