setup: stop calling the app-data drive "System disk" when it is not

The wizard's Storage step builds its first entry from primaryRoot() — the
app-data root — and labelled it "System disk". On a default install those are
the same drive and the name is honest. Installed with --containers-dir on its
own disk they are not, and the step then showed the DATA drive's size under the
system disk's name while the actual system disk never appeared in the list.

Seen on a matrix case-2 install (apps on a 29.4G test disk, system on a 912G
root): "System disk — 26.7G free of 29.4G".

The generator now reports whether that root is really on the OS disk
(is_os_disk, by st_dev against /), and the wizard labels it from that: "System
disk" when they coincide, otherwise the mount point. The "system" badge stays —
it marks the default location, which is still what it is.

Also add lp-shot --token / --cookie-js. A screenshot answers "does it render";
"does this wizard step work" needs clicking, which needs a real browser, which
needs the session lp-shot already knows how to mint from the stored jwtSecret.
This bug was found that way.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
librelad 2026-08-28 06:35:16 +01:00
parent 864059ab83
commit 7ed8539af7
3 changed files with 40 additions and 4 deletions

View File

@ -527,6 +527,16 @@ class SetupWizard {
//
// The badge carries severity and Details carries the explanation, so the card
// stays a single row.
// What to call the default app-data location. It is the OS disk on a normal
// install, but --containers-dir can put it on its own drive, and calling that
// "System disk" showed the data drive's size under the system disk's name
// while the actual system disk was absent from the list.
_primaryLabel() {
const s = this.storageSystem;
if (!s || s.is_os_disk !== false) return 'System disk';
return s.mount && s.mount !== '/' ? s.mount : (s.path || 'Default location');
}
_storageCard(c, key, opts) {
const o = opts || {};
const refused = c.verdict === 'refuse';
@ -535,7 +545,7 @@ class SetupWizard {
: (refused ? '<span class="setup-storage-badge setup-storage-badge-bad">can\u2019t be used</span>'
: (c.verdict === 'warn' ? '<span class="setup-storage-badge setup-storage-badge-warn">needs care</span>' : ''));
const title = o.system ? 'System disk' : c.path;
const title = o.system ? this._primaryLabel() : c.path;
return `
<div class="setup-app setup-storage-card${refused ? ' setup-storage-disabled' : ''} setup-storage-locked">
@ -553,7 +563,7 @@ class SetupWizard {
// Every place a root could go: the system disk, plus each usable candidate.
_storageChoices() {
const opts = [{ value: 'primary', label: 'System disk' }];
const opts = [{ value: 'primary', label: this._primaryLabel() }];
this.storageCandidates
.filter(c => c.verdict !== 'refuse')
.forEach(c => opts.push({ value: c.path, label: c.path }));
@ -681,7 +691,7 @@ class SetupWizard {
this.storageCandidates
.filter(c => c.verdict !== 'refuse')
.forEach(c => opts.push({ value: c.path, label: c.path, shared: c.path === appTarget }));
opts.push({ value: 'primary', label: 'System disk', shared: this.storageDefault === 'primary' });
opts.push({ value: 'primary', label: this._primaryLabel(), shared: this.storageDefault === 'primary' });
box.innerHTML = `
<div class="setup-storage-choice">
@ -890,7 +900,7 @@ class SetupWizard {
const m = window.openEoModal({
id: 'lp-storage-details',
size: 'md',
title: isSystem ? 'System disk' : c.path,
title: isSystem ? this._primaryLabel() : c.path,
desc: `${c.size} drive \u00b7 ${c.free} free \u00b7 ${c.fstype}${c.removable ? ' \u00b7 removable' : ''}`,
body: parts.join(''),
actions: [{ label: 'Close', variant: 'secondary' }],

View File

@ -358,6 +358,24 @@ class CDP:
def main():
# Print a session cookie and exit. A screenshot is enough for "does it
# render", but not for "does this wizard step work" — that needs clicking,
# which means driving a real browser, which needs the same session lp-shot
# already knows how to mint. Without this the only way in is typing the
# admin password into the login form.
#
# lp-shot --token -> the raw cookie VALUE
# lp-shot --cookie-js -> a document.cookie assignment to paste/eval
if len(sys.argv) > 1 and sys.argv[1] in ("--token", "--cookie-js"):
token, src = mint_token()
if os.environ.get("LP_SHOT_VERBOSE"):
print(f"signed from {src}", file=sys.stderr)
if sys.argv[1] == "--token":
print(token)
else:
print(f'document.cookie = "{COOKIE}={token}; path=/"')
sys.exit(0)
if len(sys.argv) < 2 or sys.argv[1] in ("-h", "--help"):
print(__doc__.strip())
sys.exit(0 if len(sys.argv) > 1 else 2)

View File

@ -73,6 +73,14 @@ webuiGenerateStorageCandidates()
local sys_pct; sys_pct=$(df -Pk "$sys_root" 2>/dev/null | awk 'NR==2 {gsub("%","",$5); print $5}')
system_json+=",\"used_pct\":${sys_pct:-0}"
system_json+=",\"removable\":false"
# Is this entry actually the OS disk? On a default install the app-data root
# sits on /, and "System disk" is the honest name for it. Installed with
# --containers-dir on another drive it does not, and calling that the system
# disk reports the WRONG size (the data drive's) while the real system disk
# never appears in the list at all.
local sys_is_os=false
[[ "$(stat -c '%d' -- "$sys_root" 2>/dev/null)" == "$(stat -c '%d' -- / 2>/dev/null)" ]] && sys_is_os=true
system_json+=",\"is_os_disk\":$sys_is_os"
system_json+=",\"apps\":\"$(_lpJsonEsc "$(storageAppsOnRoot "$sys_root" 2>/dev/null | paste -sd, -)")\""
system_json+=",\"checks\":$sys_checks}"