2026-04-13 19:44:29 +02:00
|
|
|
import subprocess
|
|
|
|
|
|
|
|
|
|
|
2026-04-28 12:09:19 +02:00
|
|
|
def _boot_disk_name():
|
|
|
|
|
"""Return the parent disk name of the live-ISO boot media (e.g. "sdb"), or None.
|
|
|
|
|
|
|
|
|
|
On a normal box `/run/archiso/bootmnt` does not exist and we return None,
|
|
|
|
|
leaving the device list untouched. On bare metal booted from USB this is
|
|
|
|
|
the stick we booted from — we want to filter it out so the user can't
|
|
|
|
|
accidentally pick it as the install target.
|
|
|
|
|
"""
|
|
|
|
|
try:
|
|
|
|
|
result = subprocess.run(
|
|
|
|
|
["findmnt", "-no", "SOURCE", "/run/archiso/bootmnt"],
|
|
|
|
|
capture_output=True,
|
|
|
|
|
text=True,
|
|
|
|
|
)
|
|
|
|
|
except FileNotFoundError:
|
|
|
|
|
return None
|
|
|
|
|
if result.returncode != 0:
|
|
|
|
|
return None
|
|
|
|
|
partition = result.stdout.strip()
|
|
|
|
|
if not partition:
|
|
|
|
|
return None
|
|
|
|
|
try:
|
|
|
|
|
parent = subprocess.run(
|
|
|
|
|
["lsblk", "-no", "PKNAME", partition],
|
|
|
|
|
capture_output=True,
|
|
|
|
|
text=True,
|
|
|
|
|
)
|
|
|
|
|
except FileNotFoundError:
|
|
|
|
|
return None
|
|
|
|
|
if parent.returncode != 0:
|
|
|
|
|
return None
|
|
|
|
|
name = parent.stdout.strip().splitlines()[0] if parent.stdout.strip() else ""
|
|
|
|
|
return name or None
|
|
|
|
|
|
|
|
|
|
|
2026-04-16 12:01:57 +02:00
|
|
|
def _smart_status(device):
|
2026-04-13 19:44:29 +02:00
|
|
|
try:
|
|
|
|
|
result = subprocess.run(
|
|
|
|
|
["smartctl", "-H", device],
|
ci: add Forgejo Actions workflow with ruff, pytest, JSON + link checks
- .forgejo/workflows/ci.yml: four jobs (lint, test, validate-json,
markdown-links) running on push to main and on pull requests
- pyproject.toml: project metadata, flask dep, dev extras (ruff, pytest),
ruff config (E/F/I/W/B/UP rulesets, 100-char lines, py311 target),
pytest config (pythonpath=webinstaller so tests can import drives)
- tests/test_drives.py: 11 unit tests covering parse_size_gb (TB/GB/MB,
European comma decimal, empty input, unknown units), drive type
scoring (nvme/ssd/hdd), size scoring bands, and score_device summing
- .gitignore: ignore .pytest_cache, *.egg-info, .ruff_cache
- webinstaller/drives.py: refactor subprocess.run to capture_output
kwarg (ruff UP022) — drops four lines, same behavior
- webinstaller/app.py: ruff-sorted imports (isort)
All checks pass locally: ruff check + format, pytest 11/11, JSON valid.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 20:24:05 +02:00
|
|
|
capture_output=True,
|
2026-04-13 19:44:29 +02:00
|
|
|
)
|
|
|
|
|
output = result.stdout.decode()
|
|
|
|
|
if "PASSED" in output:
|
2026-04-16 12:01:57 +02:00
|
|
|
return "passed"
|
2026-04-13 19:44:29 +02:00
|
|
|
elif "FAILED" in output:
|
2026-04-16 12:01:57 +02:00
|
|
|
return "failed"
|
|
|
|
|
return "unknown"
|
2026-04-13 19:44:29 +02:00
|
|
|
except Exception as e:
|
|
|
|
|
print(f"Error checking SMART status for {device}: {e}")
|
2026-04-16 12:01:57 +02:00
|
|
|
return "unknown"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
_HEALTH_SCORE = {"passed": 10, "failed": 0, "unknown": 5}
|
|
|
|
|
_HEALTH_LABEL = {
|
|
|
|
|
"passed": "Healthy",
|
|
|
|
|
"failed": "SMART warning",
|
|
|
|
|
"unknown": "Status unknown",
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_drive_health(device):
|
|
|
|
|
return _HEALTH_SCORE[_smart_status(device)]
|
2026-04-13 19:44:29 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_drive_type_score(device):
|
|
|
|
|
name = device.lower()
|
|
|
|
|
if "nvme" in name:
|
|
|
|
|
return 15
|
|
|
|
|
if "ssd" in name:
|
|
|
|
|
return 10
|
|
|
|
|
return 5
|
|
|
|
|
|
|
|
|
|
|
2026-04-16 12:01:57 +02:00
|
|
|
def get_drive_type_label(device):
|
|
|
|
|
name = device.lower()
|
|
|
|
|
if "nvme" in name:
|
|
|
|
|
return "NVMe"
|
|
|
|
|
if "ssd" in name:
|
|
|
|
|
return "SSD"
|
|
|
|
|
return "HDD"
|
|
|
|
|
|
|
|
|
|
|
2026-04-13 19:44:29 +02:00
|
|
|
def parse_size_gb(size_str):
|
|
|
|
|
size_str = size_str.strip().upper().replace(",", ".")
|
|
|
|
|
if not size_str:
|
|
|
|
|
return None
|
|
|
|
|
if size_str.endswith("T"):
|
|
|
|
|
return float(size_str[:-1]) * 1024
|
|
|
|
|
if size_str.endswith("G"):
|
|
|
|
|
return float(size_str[:-1])
|
|
|
|
|
if size_str.endswith("M"):
|
|
|
|
|
return float(size_str[:-1]) / 1024
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_size_score(size_gb):
|
|
|
|
|
if size_gb is None:
|
|
|
|
|
return 5
|
|
|
|
|
if size_gb < 128:
|
|
|
|
|
return 5
|
|
|
|
|
if size_gb < 512:
|
|
|
|
|
return 7
|
|
|
|
|
return 10
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def score_device(device, size_gb):
|
|
|
|
|
return get_drive_type_score(device) + get_drive_health(device) + get_size_score(size_gb)
|
|
|
|
|
|
|
|
|
|
|
2026-04-28 12:09:19 +02:00
|
|
|
def parse_lsblk_output(output, boot_disk=None):
|
feat: webinstaller writes archinstall config + execs install, styled
Wires the live-ISO wizard from "shows three screens" to "actually invokes
archinstall on the chosen disk", plus first-pass styling so it stops looking
like raw <h1>/<form>.
Webinstaller flow:
- S1 form gains username/password/password2/language with server-side
validation (hostname/username regex, ≥8 char password, match check).
- /install/run writes user_configuration.json + user_credentials.json
(creds 0600) to FURTKA_STATE_DIR (default /tmp/furtka), then execs
`archinstall --config … --creds … --silent` as a backgrounded subprocess.
- /install/log renders the subprocess output via meta-refresh polling.
- FURTKA_DRY_RUN=1 short-circuits the exec for testing.
- archinstall flag names verified against `archinstall --help` in an
archlinux container before committing.
Drive list:
- drives.py now filters via `lsblk … -o NAME,SIZE,TYPE` keeping TYPE=disk,
so the live ISO's own squashfs (loop) and CD-ROM (rom) stop appearing
as install targets.
Boot menu:
- iso/build.sh sed-rebrands "Arch Linux install medium" →
"Furtka Live Installer" across grub/, syslinux/, and efiboot/loader/
entries. Verified zero leftovers against the current releng profile.
Styling:
- static/style.css adopts the website's design tokens (palette,
typography, gate-mark accent), with light + dark via prefers-color-scheme.
- New base.html with header (gate SVG + FURTKA·INSTALLER wordmark + step
indicator) and footer; all install templates extend it.
- Drive picker uses radio cards with score chip; overview uses a summary
table and a destructive "wipe drive" button.
Tests: 17 pass (4 new in test_app.py covering validation + config builders,
2 new in test_drives.py covering the lsblk filter). Ruff clean.
README roadmap updated to mark these done and explicitly defer the
26.0-alpha release until archinstall actually completes end-to-end on a VM.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 10:54:49 +02:00
|
|
|
"""Parse `lsblk -dn -o NAME,SIZE,TYPE` output into scored device dicts.
|
|
|
|
|
|
|
|
|
|
Keeps only TYPE=disk so the live ISO's own squashfs (loop) and the boot
|
2026-04-28 12:09:19 +02:00
|
|
|
CD-ROM (rom) don't show up as install targets. If `boot_disk` is given,
|
|
|
|
|
that disk is also dropped — it's the USB stick the live ISO booted from
|
|
|
|
|
on bare metal, where it appears as TYPE=disk and would otherwise be a
|
|
|
|
|
valid-looking install target.
|
feat: webinstaller writes archinstall config + execs install, styled
Wires the live-ISO wizard from "shows three screens" to "actually invokes
archinstall on the chosen disk", plus first-pass styling so it stops looking
like raw <h1>/<form>.
Webinstaller flow:
- S1 form gains username/password/password2/language with server-side
validation (hostname/username regex, ≥8 char password, match check).
- /install/run writes user_configuration.json + user_credentials.json
(creds 0600) to FURTKA_STATE_DIR (default /tmp/furtka), then execs
`archinstall --config … --creds … --silent` as a backgrounded subprocess.
- /install/log renders the subprocess output via meta-refresh polling.
- FURTKA_DRY_RUN=1 short-circuits the exec for testing.
- archinstall flag names verified against `archinstall --help` in an
archlinux container before committing.
Drive list:
- drives.py now filters via `lsblk … -o NAME,SIZE,TYPE` keeping TYPE=disk,
so the live ISO's own squashfs (loop) and CD-ROM (rom) stop appearing
as install targets.
Boot menu:
- iso/build.sh sed-rebrands "Arch Linux install medium" →
"Furtka Live Installer" across grub/, syslinux/, and efiboot/loader/
entries. Verified zero leftovers against the current releng profile.
Styling:
- static/style.css adopts the website's design tokens (palette,
typography, gate-mark accent), with light + dark via prefers-color-scheme.
- New base.html with header (gate SVG + FURTKA·INSTALLER wordmark + step
indicator) and footer; all install templates extend it.
- Drive picker uses radio cards with score chip; overview uses a summary
table and a destructive "wipe drive" button.
Tests: 17 pass (4 new in test_app.py covering validation + config builders,
2 new in test_drives.py covering the lsblk filter). Ruff clean.
README roadmap updated to mark these done and explicitly defer the
26.0-alpha release until archinstall actually completes end-to-end on a VM.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 10:54:49 +02:00
|
|
|
"""
|
|
|
|
|
devices = []
|
|
|
|
|
for line in output.strip().split("\n"):
|
|
|
|
|
if not line:
|
|
|
|
|
continue
|
|
|
|
|
parts = line.split()
|
|
|
|
|
if len(parts) < 3:
|
|
|
|
|
continue
|
|
|
|
|
name, size, dev_type = parts[0], parts[1], parts[2]
|
|
|
|
|
if dev_type != "disk":
|
|
|
|
|
continue
|
2026-04-28 12:09:19 +02:00
|
|
|
if boot_disk and name == boot_disk:
|
|
|
|
|
continue
|
feat: webinstaller writes archinstall config + execs install, styled
Wires the live-ISO wizard from "shows three screens" to "actually invokes
archinstall on the chosen disk", plus first-pass styling so it stops looking
like raw <h1>/<form>.
Webinstaller flow:
- S1 form gains username/password/password2/language with server-side
validation (hostname/username regex, ≥8 char password, match check).
- /install/run writes user_configuration.json + user_credentials.json
(creds 0600) to FURTKA_STATE_DIR (default /tmp/furtka), then execs
`archinstall --config … --creds … --silent` as a backgrounded subprocess.
- /install/log renders the subprocess output via meta-refresh polling.
- FURTKA_DRY_RUN=1 short-circuits the exec for testing.
- archinstall flag names verified against `archinstall --help` in an
archlinux container before committing.
Drive list:
- drives.py now filters via `lsblk … -o NAME,SIZE,TYPE` keeping TYPE=disk,
so the live ISO's own squashfs (loop) and CD-ROM (rom) stop appearing
as install targets.
Boot menu:
- iso/build.sh sed-rebrands "Arch Linux install medium" →
"Furtka Live Installer" across grub/, syslinux/, and efiboot/loader/
entries. Verified zero leftovers against the current releng profile.
Styling:
- static/style.css adopts the website's design tokens (palette,
typography, gate-mark accent), with light + dark via prefers-color-scheme.
- New base.html with header (gate SVG + FURTKA·INSTALLER wordmark + step
indicator) and footer; all install templates extend it.
- Drive picker uses radio cards with score chip; overview uses a summary
table and a destructive "wipe drive" button.
Tests: 17 pass (4 new in test_app.py covering validation + config builders,
2 new in test_drives.py covering the lsblk filter). Ruff clean.
README roadmap updated to mark these done and explicitly defer the
26.0-alpha release until archinstall actually completes end-to-end on a VM.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 10:54:49 +02:00
|
|
|
device = f"/dev/{name}"
|
2026-04-16 12:01:57 +02:00
|
|
|
size_gb = parse_size_gb(size)
|
|
|
|
|
status = _smart_status(device)
|
feat(ui): shared /style.css + top nav across landing and /apps
Slice 1 of the on-box UI uplevel. Consolidates the two duplicated
stylesheets (landing's webinstaller/app.py and /apps's inline block
in furtka/api.py) into one sheet served by Caddy at /style.css.
Expands the token set (spacing, radii, shadows, focus ring, warn-fg,
accent-soft, card-hover), adds a prefers-color-scheme light theme,
and introduces shared primitives for later slices: .nav, .chip,
.card, .kv, .coming, .grid-apps, .app-tile, .app-icon.
Also adds a persistent top nav (Home / Apps) to both pages — Jakob's
Law, so users always have a way back — and collapses the /apps "Last
action" log behind a details disclosure so it stops dominating the
page. Format fallout on drives.py picked up by ruff.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 12:19:54 +02:00
|
|
|
score = get_drive_type_score(device) + _HEALTH_SCORE[status] + get_size_score(size_gb)
|
feat: webinstaller writes archinstall config + execs install, styled
Wires the live-ISO wizard from "shows three screens" to "actually invokes
archinstall on the chosen disk", plus first-pass styling so it stops looking
like raw <h1>/<form>.
Webinstaller flow:
- S1 form gains username/password/password2/language with server-side
validation (hostname/username regex, ≥8 char password, match check).
- /install/run writes user_configuration.json + user_credentials.json
(creds 0600) to FURTKA_STATE_DIR (default /tmp/furtka), then execs
`archinstall --config … --creds … --silent` as a backgrounded subprocess.
- /install/log renders the subprocess output via meta-refresh polling.
- FURTKA_DRY_RUN=1 short-circuits the exec for testing.
- archinstall flag names verified against `archinstall --help` in an
archlinux container before committing.
Drive list:
- drives.py now filters via `lsblk … -o NAME,SIZE,TYPE` keeping TYPE=disk,
so the live ISO's own squashfs (loop) and CD-ROM (rom) stop appearing
as install targets.
Boot menu:
- iso/build.sh sed-rebrands "Arch Linux install medium" →
"Furtka Live Installer" across grub/, syslinux/, and efiboot/loader/
entries. Verified zero leftovers against the current releng profile.
Styling:
- static/style.css adopts the website's design tokens (palette,
typography, gate-mark accent), with light + dark via prefers-color-scheme.
- New base.html with header (gate SVG + FURTKA·INSTALLER wordmark + step
indicator) and footer; all install templates extend it.
- Drive picker uses radio cards with score chip; overview uses a summary
table and a destructive "wipe drive" button.
Tests: 17 pass (4 new in test_app.py covering validation + config builders,
2 new in test_drives.py covering the lsblk filter). Ruff clean.
README roadmap updated to mark these done and explicitly defer the
26.0-alpha release until archinstall actually completes end-to-end on a VM.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 10:54:49 +02:00
|
|
|
devices.append(
|
|
|
|
|
{
|
|
|
|
|
"name": device,
|
|
|
|
|
"size": size,
|
2026-04-16 12:01:57 +02:00
|
|
|
"type_label": get_drive_type_label(device),
|
|
|
|
|
"health_label": _HEALTH_LABEL[status],
|
|
|
|
|
"score": score,
|
feat: webinstaller writes archinstall config + execs install, styled
Wires the live-ISO wizard from "shows three screens" to "actually invokes
archinstall on the chosen disk", plus first-pass styling so it stops looking
like raw <h1>/<form>.
Webinstaller flow:
- S1 form gains username/password/password2/language with server-side
validation (hostname/username regex, ≥8 char password, match check).
- /install/run writes user_configuration.json + user_credentials.json
(creds 0600) to FURTKA_STATE_DIR (default /tmp/furtka), then execs
`archinstall --config … --creds … --silent` as a backgrounded subprocess.
- /install/log renders the subprocess output via meta-refresh polling.
- FURTKA_DRY_RUN=1 short-circuits the exec for testing.
- archinstall flag names verified against `archinstall --help` in an
archlinux container before committing.
Drive list:
- drives.py now filters via `lsblk … -o NAME,SIZE,TYPE` keeping TYPE=disk,
so the live ISO's own squashfs (loop) and CD-ROM (rom) stop appearing
as install targets.
Boot menu:
- iso/build.sh sed-rebrands "Arch Linux install medium" →
"Furtka Live Installer" across grub/, syslinux/, and efiboot/loader/
entries. Verified zero leftovers against the current releng profile.
Styling:
- static/style.css adopts the website's design tokens (palette,
typography, gate-mark accent), with light + dark via prefers-color-scheme.
- New base.html with header (gate SVG + FURTKA·INSTALLER wordmark + step
indicator) and footer; all install templates extend it.
- Drive picker uses radio cards with score chip; overview uses a summary
table and a destructive "wipe drive" button.
Tests: 17 pass (4 new in test_app.py covering validation + config builders,
2 new in test_drives.py covering the lsblk filter). Ruff clean.
README roadmap updated to mark these done and explicitly defer the
26.0-alpha release until archinstall actually completes end-to-end on a VM.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 10:54:49 +02:00
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
devices.sort(key=lambda d: d["score"], reverse=True)
|
|
|
|
|
return devices
|
|
|
|
|
|
|
|
|
|
|
2026-04-13 19:44:29 +02:00
|
|
|
def list_scored_devices():
|
|
|
|
|
"""Return [{name, size, score}, ...] for all physical disks, highest score first."""
|
|
|
|
|
try:
|
|
|
|
|
result = subprocess.run(
|
feat: webinstaller writes archinstall config + execs install, styled
Wires the live-ISO wizard from "shows three screens" to "actually invokes
archinstall on the chosen disk", plus first-pass styling so it stops looking
like raw <h1>/<form>.
Webinstaller flow:
- S1 form gains username/password/password2/language with server-side
validation (hostname/username regex, ≥8 char password, match check).
- /install/run writes user_configuration.json + user_credentials.json
(creds 0600) to FURTKA_STATE_DIR (default /tmp/furtka), then execs
`archinstall --config … --creds … --silent` as a backgrounded subprocess.
- /install/log renders the subprocess output via meta-refresh polling.
- FURTKA_DRY_RUN=1 short-circuits the exec for testing.
- archinstall flag names verified against `archinstall --help` in an
archlinux container before committing.
Drive list:
- drives.py now filters via `lsblk … -o NAME,SIZE,TYPE` keeping TYPE=disk,
so the live ISO's own squashfs (loop) and CD-ROM (rom) stop appearing
as install targets.
Boot menu:
- iso/build.sh sed-rebrands "Arch Linux install medium" →
"Furtka Live Installer" across grub/, syslinux/, and efiboot/loader/
entries. Verified zero leftovers against the current releng profile.
Styling:
- static/style.css adopts the website's design tokens (palette,
typography, gate-mark accent), with light + dark via prefers-color-scheme.
- New base.html with header (gate SVG + FURTKA·INSTALLER wordmark + step
indicator) and footer; all install templates extend it.
- Drive picker uses radio cards with score chip; overview uses a summary
table and a destructive "wipe drive" button.
Tests: 17 pass (4 new in test_app.py covering validation + config builders,
2 new in test_drives.py covering the lsblk filter). Ruff clean.
README roadmap updated to mark these done and explicitly defer the
26.0-alpha release until archinstall actually completes end-to-end on a VM.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 10:54:49 +02:00
|
|
|
["lsblk", "-dn", "-o", "NAME,SIZE,TYPE"],
|
ci: add Forgejo Actions workflow with ruff, pytest, JSON + link checks
- .forgejo/workflows/ci.yml: four jobs (lint, test, validate-json,
markdown-links) running on push to main and on pull requests
- pyproject.toml: project metadata, flask dep, dev extras (ruff, pytest),
ruff config (E/F/I/W/B/UP rulesets, 100-char lines, py311 target),
pytest config (pythonpath=webinstaller so tests can import drives)
- tests/test_drives.py: 11 unit tests covering parse_size_gb (TB/GB/MB,
European comma decimal, empty input, unknown units), drive type
scoring (nvme/ssd/hdd), size scoring bands, and score_device summing
- .gitignore: ignore .pytest_cache, *.egg-info, .ruff_cache
- webinstaller/drives.py: refactor subprocess.run to capture_output
kwarg (ruff UP022) — drops four lines, same behavior
- webinstaller/app.py: ruff-sorted imports (isort)
All checks pass locally: ruff check + format, pytest 11/11, JSON valid.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 20:24:05 +02:00
|
|
|
capture_output=True,
|
2026-04-13 19:44:29 +02:00
|
|
|
text=True,
|
|
|
|
|
check=True,
|
|
|
|
|
)
|
|
|
|
|
except subprocess.CalledProcessError as e:
|
|
|
|
|
print(f"Error listing devices: {e}")
|
feat: webinstaller writes archinstall config + execs install, styled
Wires the live-ISO wizard from "shows three screens" to "actually invokes
archinstall on the chosen disk", plus first-pass styling so it stops looking
like raw <h1>/<form>.
Webinstaller flow:
- S1 form gains username/password/password2/language with server-side
validation (hostname/username regex, ≥8 char password, match check).
- /install/run writes user_configuration.json + user_credentials.json
(creds 0600) to FURTKA_STATE_DIR (default /tmp/furtka), then execs
`archinstall --config … --creds … --silent` as a backgrounded subprocess.
- /install/log renders the subprocess output via meta-refresh polling.
- FURTKA_DRY_RUN=1 short-circuits the exec for testing.
- archinstall flag names verified against `archinstall --help` in an
archlinux container before committing.
Drive list:
- drives.py now filters via `lsblk … -o NAME,SIZE,TYPE` keeping TYPE=disk,
so the live ISO's own squashfs (loop) and CD-ROM (rom) stop appearing
as install targets.
Boot menu:
- iso/build.sh sed-rebrands "Arch Linux install medium" →
"Furtka Live Installer" across grub/, syslinux/, and efiboot/loader/
entries. Verified zero leftovers against the current releng profile.
Styling:
- static/style.css adopts the website's design tokens (palette,
typography, gate-mark accent), with light + dark via prefers-color-scheme.
- New base.html with header (gate SVG + FURTKA·INSTALLER wordmark + step
indicator) and footer; all install templates extend it.
- Drive picker uses radio cards with score chip; overview uses a summary
table and a destructive "wipe drive" button.
Tests: 17 pass (4 new in test_app.py covering validation + config builders,
2 new in test_drives.py covering the lsblk filter). Ruff clean.
README roadmap updated to mark these done and explicitly defer the
26.0-alpha release until archinstall actually completes end-to-end on a VM.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 10:54:49 +02:00
|
|
|
return []
|
2026-04-28 12:09:19 +02:00
|
|
|
return parse_lsblk_output(result.stdout, boot_disk=_boot_disk_name())
|
2026-04-13 19:44:29 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def main():
|
|
|
|
|
devices = list_scored_devices()
|
|
|
|
|
if not devices:
|
|
|
|
|
print("No storage devices found.")
|
|
|
|
|
return
|
|
|
|
|
print(f"\n{'Device':<20} {'Size':<10} {'Score'}")
|
|
|
|
|
print("-" * 40)
|
|
|
|
|
for d in devices:
|
|
|
|
|
print(f"{d['name']:<20} {d['size']:<10} {d['score']}")
|
|
|
|
|
print(f"\nBest drive for boot: {devices[0]['name']} (score {devices[0]['score']})")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
main()
|