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
|
|
|
from drives import (
|
|
|
|
|
get_drive_type_score,
|
|
|
|
|
get_size_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
|
|
|
parse_lsblk_output,
|
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
|
|
|
parse_size_gb,
|
|
|
|
|
score_device,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_parse_size_gb_terabytes():
|
|
|
|
|
assert parse_size_gb("1T") == 1024.0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_parse_size_gb_gigabytes():
|
|
|
|
|
assert parse_size_gb("500G") == 500.0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_parse_size_gb_megabytes():
|
|
|
|
|
assert parse_size_gb("2048M") == 2.0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_parse_size_gb_european_comma_decimal():
|
|
|
|
|
assert parse_size_gb("1,5T") == 1.5 * 1024
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_parse_size_gb_empty_returns_none():
|
|
|
|
|
assert parse_size_gb("") is None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_parse_size_gb_unknown_unit_returns_none():
|
|
|
|
|
assert parse_size_gb("500K") is None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_drive_type_score_nvme():
|
|
|
|
|
assert get_drive_type_score("/dev/nvme0n1") == 15
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_drive_type_score_ssd():
|
|
|
|
|
assert get_drive_type_score("/dev/ssd0") == 10
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_drive_type_score_hdd_fallback():
|
|
|
|
|
assert get_drive_type_score("/dev/sda") == 5
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_size_score_bands():
|
|
|
|
|
assert get_size_score(None) == 5
|
|
|
|
|
assert get_size_score(64) == 5
|
|
|
|
|
assert get_size_score(256) == 7
|
|
|
|
|
assert get_size_score(1024) == 10
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_score_device_sums_type_and_size(monkeypatch):
|
|
|
|
|
import drives
|
|
|
|
|
|
|
|
|
|
monkeypatch.setattr(drives, "get_drive_health", lambda _: 10)
|
|
|
|
|
assert score_device("/dev/nvme0n1", 1024) == 15 + 10 + 10
|
|
|
|
|
assert score_device("/dev/sda", 64) == 5 + 10 + 5
|
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
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_parse_lsblk_drops_loop_and_rom(monkeypatch):
|
|
|
|
|
import drives
|
|
|
|
|
|
|
|
|
|
monkeypatch.setattr(drives, "get_drive_health", lambda _: 10)
|
2026-04-14 18:29:42 +02:00
|
|
|
output = "loop0 2.5G loop\nsr0 1024M rom\nsda 500G disk\nnvme0n1 1T disk\n"
|
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 = parse_lsblk_output(output)
|
|
|
|
|
names = [d["name"] for d in devices]
|
|
|
|
|
assert names == ["/dev/nvme0n1", "/dev/sda"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_parse_lsblk_handles_empty_output():
|
|
|
|
|
assert parse_lsblk_output("") == []
|