Merge pull request #361 from naturallaw777/arena/019fae31-sovran-systemsos
Fix Tile Versioning with Build-Time Reference File
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
"""Load the Nix-generated config for Sovran_SystemsOS_Hub."""
|
"""Load the Nix-generated config and build-time versions for Sovran_SystemsOS_Hub."""
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
@@ -18,3 +18,23 @@ def load_config() -> dict:
|
|||||||
return json.load(fh)
|
return json.load(fh)
|
||||||
except (FileNotFoundError, json.JSONDecodeError):
|
except (FileNotFoundError, json.JSONDecodeError):
|
||||||
return {"refresh_interval": 5, "command_method": "systemctl", "services": []}
|
return {"refresh_interval": 5, "command_method": "systemctl", "services": []}
|
||||||
|
|
||||||
|
|
||||||
|
def load_versions() -> dict:
|
||||||
|
"""Read the Nix-generated or fallback build-time package versions."""
|
||||||
|
path = os.environ.get("SOVRAN_HUB_VERSIONS")
|
||||||
|
if not path:
|
||||||
|
# Candidate 1: parent folder (same as config.json, typical for Nix layout)
|
||||||
|
parent_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||||
|
candidate1 = os.path.join(parent_dir, "versions.json")
|
||||||
|
if os.path.exists(candidate1):
|
||||||
|
path = candidate1
|
||||||
|
else:
|
||||||
|
# Candidate 2: local package folder (dev folder structure)
|
||||||
|
path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "versions.json")
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open(path, "r") as fh:
|
||||||
|
return json.load(fh)
|
||||||
|
except (FileNotFoundError, json.JSONDecodeError):
|
||||||
|
return {}
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ from html import escape as _html_escape
|
|||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
from starlette.middleware.base import BaseHTTPMiddleware
|
from starlette.middleware.base import BaseHTTPMiddleware
|
||||||
|
|
||||||
from .config import load_config
|
from .config import load_config, load_versions
|
||||||
from . import systemctl as sysctl
|
from . import systemctl as sysctl
|
||||||
from . import nwc_hub_manager as _nwc_mgr
|
from . import nwc_hub_manager as _nwc_mgr
|
||||||
|
|
||||||
@@ -2550,15 +2550,20 @@ _SVC_VERSION_CACHE_TTL = 300 # 5 minutes — versions only change on system upd
|
|||||||
|
|
||||||
|
|
||||||
def _get_service_version(unit: str) -> str | None:
|
def _get_service_version(unit: str) -> str | None:
|
||||||
"""Extract the version of a service from its Nix store ExecStart path.
|
"""Extract the version of a service.
|
||||||
|
|
||||||
Runs ``systemctl show <unit> --property=ExecStart --value`` and parses
|
Checks the Nix-generated build-time versions reference file (loaded via load_versions()) first.
|
||||||
the Nix store path embedded in the output to obtain the package version.
|
Falls back to running ``systemctl show <unit> --property=ExecStart --value`` and parsing
|
||||||
|
the Nix store path embedded in the output if the reference file is missing or incomplete.
|
||||||
Results are cached for _SVC_VERSION_CACHE_TTL seconds so that repeated
|
|
||||||
/api/services polls don't spawn extra processes on every request.
|
|
||||||
Returns None if the version cannot be determined.
|
|
||||||
"""
|
"""
|
||||||
|
try:
|
||||||
|
versions_ref = load_versions()
|
||||||
|
ver = versions_ref.get(unit)
|
||||||
|
if ver and ver != "unknown":
|
||||||
|
return ver if ver.startswith("v") else f"v{ver}"
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
now = time.monotonic()
|
now = time.monotonic()
|
||||||
cached = _svc_version_cache.get(unit)
|
cached = _svc_version_cache.get(unit)
|
||||||
if cached is not None:
|
if cached is not None:
|
||||||
|
|||||||
@@ -200,6 +200,19 @@ button.btn-reboot:hover:not(:disabled) {
|
|||||||
gap: 10px;
|
gap: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.creds-title-version-badge {
|
||||||
|
background-color: rgba(255, 255, 255, 0.06);
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-size: 0.72rem;
|
||||||
|
font-weight: 600;
|
||||||
|
padding: 2px 10px;
|
||||||
|
border-radius: 12px;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||||
|
letter-spacing: 0.02em;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
.creds-title-icon {
|
.creds-title-icon {
|
||||||
width: 28px;
|
width: 28px;
|
||||||
height: 28px;
|
height: 28px;
|
||||||
|
|||||||
@@ -623,6 +623,20 @@ async function openServiceDetailModal(unit, name, icon) {
|
|||||||
if (icon) url += "?icon=" + encodeURIComponent(icon);
|
if (icon) url += "?icon=" + encodeURIComponent(icon);
|
||||||
var data = await apiFetch(url);
|
var data = await apiFetch(url);
|
||||||
|
|
||||||
|
// Append version badge next to title if version is detected
|
||||||
|
var serviceVersion = data.version || data.bitcoin_version || '';
|
||||||
|
if (serviceVersion && $credsTitle) {
|
||||||
|
var existingBadge = $credsTitle.querySelector(".creds-title-version-badge");
|
||||||
|
if (existingBadge) {
|
||||||
|
existingBadge.textContent = serviceVersion;
|
||||||
|
} else {
|
||||||
|
var badge = document.createElement("span");
|
||||||
|
badge.className = "creds-title-version-badge";
|
||||||
|
badge.textContent = serviceVersion;
|
||||||
|
$credsTitle.appendChild(badge);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Two content buckets. For most services everything lands in `html` and is
|
// Two content buckets. For most services everything lands in `html` and is
|
||||||
// rendered as one column, exactly as before. For Lightning Wallet
|
// rendered as one column, exactly as before. For Lightning Wallet
|
||||||
// Connections the day-to-day wallet manager (html) is split away from the
|
// Connections the day-to-day wallet manager (html) is split away from the
|
||||||
@@ -666,8 +680,6 @@ async function openServiceDetailModal(unit, name, icon) {
|
|||||||
// These services are wrappers, setup helpers, or application stacks rather
|
// These services are wrappers, setup helpers, or application stacks rather
|
||||||
// than a single versioned daemon represented by the tile's systemd unit.
|
// than a single versioned daemon represented by the tile's systemd unit.
|
||||||
var versionNotApplicable = [
|
var versionNotApplicable = [
|
||||||
'phpfpm-wordpress.service', 'phpfpm-nextcloud.service',
|
|
||||||
'albyhub.service', 'nwc-wallets.service',
|
|
||||||
'zeus-connect-setup.service', 'sparrow-autoconnect.service'
|
'zeus-connect-setup.service', 'sparrow-autoconnect.service'
|
||||||
].indexOf(unit) !== -1;
|
].indexOf(unit) !== -1;
|
||||||
var versionState = serviceVersion ? 'detected' : (versionNotApplicable ? 'not-applicable' : 'unavailable');
|
var versionState = serviceVersion ? 'detected' : (versionNotApplicable ? 'not-applicable' : 'unavailable');
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"caddy.service": "2.8.4",
|
||||||
|
"tor.service": "0.4.8.12",
|
||||||
|
"gnome-remote-desktop.service": "46.0",
|
||||||
|
"bitcoind.service": "27.1.0",
|
||||||
|
"electrs.service": "0.10.6",
|
||||||
|
"lnd.service": "0.18.0",
|
||||||
|
"rtl.service": "0.15.2",
|
||||||
|
"btcpayserver.service": "2.0.0",
|
||||||
|
"albyhub.service": "1.8.0",
|
||||||
|
"mempool.service": "3.0.0",
|
||||||
|
"matrix-synapse.service": "1.115.0",
|
||||||
|
"livekit.service": "1.5.2",
|
||||||
|
"vaultwarden.service": "1.32.0",
|
||||||
|
"phpfpm-nextcloud.service": "29.0.0",
|
||||||
|
"phpfpm-wordpress.service": "6.5.0",
|
||||||
|
"haven-relay.service": "0.1.0"
|
||||||
|
}
|
||||||
@@ -118,6 +118,25 @@ let
|
|||||||
feature_manager = true;
|
feature_manager = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
generatedVersions = pkgs.writeText "sovran-hub-versions.json" (builtins.toJSON {
|
||||||
|
"caddy.service" = if pkgs ? caddy then pkgs.caddy.version else "2.8.4";
|
||||||
|
"tor.service" = if pkgs ? tor then pkgs.tor.version else "0.4.8.12";
|
||||||
|
"gnome-remote-desktop.service" = if pkgs ? gnome-remote-desktop then pkgs.gnome-remote-desktop.version else "46.0";
|
||||||
|
"bitcoind.service" = if pkgs ? bitcoind-knots then pkgs.bitcoind-knots.version else (if pkgs ? bitcoind then pkgs.bitcoind.version else "27.1.0");
|
||||||
|
"electrs.service" = if pkgs ? electrs then pkgs.electrs.version else "0.10.6";
|
||||||
|
"lnd.service" = if pkgs ? lnd then pkgs.lnd.version else "0.18.0";
|
||||||
|
"rtl.service" = if pkgs ? clightning-rtl then pkgs.clightning-rtl.version else (if pkgs ? rtl then pkgs.rtl.version else "0.15.2");
|
||||||
|
"btcpayserver.service" = if pkgs ? btcpayserver then pkgs.btcpayserver.version else "2.0.0";
|
||||||
|
"albyhub.service" = if pkgs ? albyhub then pkgs.albyhub.version else "1.8.0";
|
||||||
|
"mempool.service" = if pkgs ? mempool then pkgs.mempool.version else "3.0.0";
|
||||||
|
"matrix-synapse.service" = if pkgs ? matrix-synapse then pkgs.matrix-synapse.version else "1.115.0";
|
||||||
|
"livekit.service" = if pkgs ? livekit then pkgs.livekit.version else "1.5.2";
|
||||||
|
"vaultwarden.service" = if pkgs ? vaultwarden then pkgs.vaultwarden.version else "1.32.0";
|
||||||
|
"phpfpm-nextcloud.service" = if pkgs ? nextcloud then pkgs.nextcloud.version else "29.0.0";
|
||||||
|
"phpfpm-wordpress.service" = if pkgs ? wordpress then pkgs.wordpress.version else "6.5.0";
|
||||||
|
"haven-relay.service" = if pkgs ? haven-relay then pkgs.haven-relay.version else (if pkgs ? haven then pkgs.haven.version else "0.1.0");
|
||||||
|
});
|
||||||
|
|
||||||
# ── Update wrapper script ──────────────────────────────────────
|
# ── Update wrapper script ──────────────────────────────────────
|
||||||
update-script = pkgs.writeShellScript "sovran-hub-update.sh" ''
|
update-script = pkgs.writeShellScript "sovran-hub-update.sh" ''
|
||||||
set -uo pipefail
|
set -uo pipefail
|
||||||
@@ -311,6 +330,7 @@ let
|
|||||||
cp -r sovran_systemsos_web $out/lib/sovran-hub-web/
|
cp -r sovran_systemsos_web $out/lib/sovran-hub-web/
|
||||||
|
|
||||||
cp ${generatedConfig} $out/lib/sovran-hub-web/config.json
|
cp ${generatedConfig} $out/lib/sovran-hub-web/config.json
|
||||||
|
cp ${generatedVersions} $out/lib/sovran-hub-web/versions.json
|
||||||
|
|
||||||
install -d $out/share/sovran-hub/icons
|
install -d $out/share/sovran-hub/icons
|
||||||
cp icons/* $out/share/sovran-hub/icons/ 2>/dev/null || true
|
cp icons/* $out/share/sovran-hub/icons/ 2>/dev/null || true
|
||||||
@@ -345,6 +365,7 @@ import os, sys
|
|||||||
base = os.path.join("$out", "lib", "sovran-hub-web")
|
base = os.path.join("$out", "lib", "sovran-hub-web")
|
||||||
sys.path.insert(0, base)
|
sys.path.insert(0, base)
|
||||||
os.environ["SOVRAN_HUB_CONFIG"] = os.path.join(base, "config.json")
|
os.environ["SOVRAN_HUB_CONFIG"] = os.path.join(base, "config.json")
|
||||||
|
os.environ["SOVRAN_HUB_VERSIONS"] = os.path.join(base, "versions.json")
|
||||||
os.environ["SOVRAN_HUB_ICONS"] = os.path.join("$out", "share", "sovran-hub", "icons")
|
os.environ["SOVRAN_HUB_ICONS"] = os.path.join("$out", "share", "sovran-hub", "icons")
|
||||||
import uvicorn
|
import uvicorn
|
||||||
uvicorn.run(
|
uvicorn.run(
|
||||||
|
|||||||
Reference in New Issue
Block a user