fix(install): constrain keyboard selection to XKB catalog
All checks were successful
Check / eval (push) Successful in 3m17s
All checks were successful
Check / eval (push) Successful in 3m17s
Make keyboard typing filter-only with Gum strict mode, validate interactive and unattended choices against the pinned XKB catalog before any disk mutation, and show resolved human-readable choices in review. Verified: V1 flake eval, focused installer builds, shell/Python/diff checks; current-ISO KVM boot proved the packaged new flow while the pre-destructive interaction pass continued.
This commit is contained in:
@@ -11,14 +11,15 @@ silent regression:
|
||||
3. HM pre-activate failure drops NOMARCHY-DESKTOP-NOT-THEMED.txt.
|
||||
4. Review panel Source line tracks the same OFFLINE probe as the install path.
|
||||
|
||||
Also re-checks the password ≥8 interactive floors (V0-complete) and, when
|
||||
given the patcher + template, the BACKLOG #91 no-keyboard-variant path.
|
||||
Pure — no VM, no ISO.
|
||||
Also re-checks the password ≥8 interactive floors (V0-complete), the
|
||||
BACKLOG #92 catalog-only keyboard boundary, and, when given the patcher +
|
||||
template, the BACKLOG #91 no-keyboard-variant path. Pure — no VM, no ISO.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
@@ -199,6 +200,95 @@ if len(sys.argv) == 4:
|
||||
"keyboard variant normalization is not on the shared input boundary",
|
||||
)
|
||||
|
||||
# ── 6. Catalog-only keyboard input (BACKLOG #92) ──────────────────
|
||||
# gum input is search-only: selections are parsed from rows generated by
|
||||
# the installed layout/variant catalogs, then both interactive and
|
||||
# unattended paths meet the same exact-membership validator.
|
||||
check(
|
||||
"keyboard_pick=$(keyboard_layout_choices" in script
|
||||
and 'gum filter --strict' in script
|
||||
and 'KB_LAYOUT="${keyboard_pick%%$\'\\t\'*}"' in script,
|
||||
"layout picker is not strict/catalog-constrained",
|
||||
)
|
||||
check(
|
||||
'keyboard_variant_choices "$KB_LAYOUT"' in script
|
||||
and script.count("gum filter --strict") == 2
|
||||
and 'KB_VARIANT="${keyboard_pick%%$\'\\t\'*}"' in script,
|
||||
"variant picker is not strict/catalog-constrained",
|
||||
)
|
||||
check(
|
||||
not re.search(r"KB_(?:LAYOUT|VARIANT)=\$\(gum input", script),
|
||||
"keyboard choice must not come from a free-form gum input",
|
||||
)
|
||||
validation_at = script.find(
|
||||
'keyboard_error=$(validate_keyboard_choice "$KB_LAYOUT" "$KB_VARIANT"'
|
||||
)
|
||||
prewipe_at = script.find('prewipe "$TARGET_DISK"')
|
||||
patch_at = script.find('python3 "$SHARE/patch-template.py"')
|
||||
check(
|
||||
normalize_at < validation_at < prewipe_at < patch_at,
|
||||
"shared keyboard validation must fail before disk changes/generated Nix",
|
||||
)
|
||||
check(
|
||||
'"Keyboard: $KEYBOARD_SUMMARY"' in script
|
||||
and "standard keys (no special variant)" in script,
|
||||
"review does not show a plain-language resolved keyboard choice",
|
||||
)
|
||||
|
||||
# Exercise the real shell validator with a tiny deterministic XKB rules
|
||||
# catalog. Exact boundaries matter: a prefix or a made-up unattended
|
||||
# value must fail here, not become an XKB/Nix build error later.
|
||||
with tempfile.TemporaryDirectory(prefix="nomarchy-keyboard-catalog-") as tmp:
|
||||
rules = Path(tmp) / "base.lst"
|
||||
rules.write_text(
|
||||
"""! layout
|
||||
us English (US)
|
||||
gb English (UK)
|
||||
! variant
|
||||
intl us: English (US, intl., with dead keys)
|
||||
dvorak us: English (Dvorak)
|
||||
extd gb: English (UK, extended, with Win keys)
|
||||
! option
|
||||
""",
|
||||
encoding="utf-8",
|
||||
)
|
||||
env = os.environ.copy()
|
||||
env["NOMARCHY_XKB_RULES"] = str(rules)
|
||||
|
||||
def keyboard_validate(layout: str, variant: str) -> subprocess.CompletedProcess[str]:
|
||||
return subprocess.run(
|
||||
["bash", str(sys.argv[1]), "--validate-keyboard", layout, variant],
|
||||
text=True,
|
||||
capture_output=True,
|
||||
check=False,
|
||||
env=env,
|
||||
)
|
||||
|
||||
for layout, variant in (("us", ""), ("us", "intl"), ("gb", "extd")):
|
||||
result = keyboard_validate(layout, variant)
|
||||
check(
|
||||
result.returncode == 0,
|
||||
f"valid keyboard choice {layout!r}/{variant!r} rejected: {result.stderr.strip()}",
|
||||
)
|
||||
|
||||
invalid = (
|
||||
("u", "", "Unknown keyboard layout 'u'"),
|
||||
("made-up", "", "Unknown keyboard layout 'made-up'"),
|
||||
("us", "int", "Unknown keyboard variant 'int' for layout 'us'"),
|
||||
("gb", "intl", "Unknown keyboard variant 'intl' for layout 'gb'"),
|
||||
)
|
||||
for layout, variant, message in invalid:
|
||||
result = keyboard_validate(layout, variant)
|
||||
check(
|
||||
result.returncode == 2 and message in result.stderr,
|
||||
f"invalid keyboard choice {layout!r}/{variant!r} did not fail friendly/early: "
|
||||
f"rc={result.returncode}, stderr={result.stderr.strip()!r}",
|
||||
)
|
||||
check(
|
||||
"NOMARCHY_INSTALL_SHARE" not in result.stderr,
|
||||
"invalid keyboard choice reached normal installer setup",
|
||||
)
|
||||
|
||||
values = {
|
||||
"hostname": "keyboard-test",
|
||||
"username": "nomarchy",
|
||||
|
||||
Reference in New Issue
Block a user