feat: DE/WM selection with cross-distro package mappings

- 12 DE/WMs: sway, i3, Hyprland, GNOME, KDE, XFCE, Cinnamon, MATE, LXQt, bspwm, openbox, Headless
- 5 distro families: void, arch, debian, alpine, fedora
- Correct package names per distro (researched from archinstall + distro repos)
- Shows availability table with package counts
- Warns if DE/WM unavailable on selected distro
This commit is contained in:
2026-08-27 20:46:21 +02:00
parent dc5898aed0
commit d3bc5ba321
+169 -2
View File
@@ -35,12 +35,124 @@ COMMON_PACKAGES = {
"Base": ["base-system", "sudo", "bash"],
"Networking": ["NetworkManager", "dhcpcd", "openssh", "iw", "wpa_supplicant"],
"Development": ["gcc", "make", "git", "python3", "cmake"],
"Desktop": ["firefox", "foot", "dmenu", "sway", "xorg-server-xwayland"],
"Fonts": ["font-noto", "font-dejavu"],
"Audio": ["pipewire", "wireplumber", "alsa-utils"],
"Firmware": ["linux-firmware", "linux-firmware-network", "linux-firmware-amd"],
}
# Cross-distro DE/WM package mapping
# Keys: distro families (void, arch, debian, alpine, fedora)
DE_WM_PACKAGES = {
"Sway": {
"desc": "Wayland tiling WM — minimal, keyboard-driven",
"server": "wayland",
"void": ["sway", "foot", "waybar", "swaybg", "swaylock", "swayidle", "xorg-server-xwayland", "wmenu", "grim", "slurp", "wl-clipboard", "dinit"],
"arch": ["sway", "foot", "waybar", "swaybg", "swaylock", "swayidle", "xorg-xwayland", "wmenu", "grim", "slurp", "wl-clipboard"],
"debian": ["sway", "foot", "waybar", "swaybg", "swaylock", "swayidle", "xwayland", "wmenu", "grim", "slurp", "wl-clipboard"],
"alpine": ["sway", "foot", "waybar", "swaybg", "swaylockd", "swayidle", "xwayland", "wmenu", "grim", "wl-clipboard", "brightnessctl"],
"fedora": ["sway", "foot", "waybar", "swaybg", "swaylock", "swayidle", "xorg-x11-server-Xwayland", "wofi", "grim", "slurp", "wl-clipboard"],
},
"i3": {
"desc": "X11 tiling WM — keyboard-driven, highly configurable",
"server": "x11",
"void": ["i3", "i3status", "dmenu", "rofi", "dunst", "xorg", "lightdm"],
"arch": ["i3-wm", "i3status", "i3blocks", "dmenu", "rofi", "dunst", "xorg-server", "lightdm", "lightdm-gtk-greeter"],
"debian": ["i3", "i3status", "dmenu", "rofi", "dunst", "xorg", "lightdm"],
"alpine": ["i3wm", "i3status", "dmenu", "rofi", "xorg-server", "lightdm"],
"fedora": ["i3", "i3status", "dmenu", "rofi", "dunst", "xorg-x11-server-Xorg", "lightdm"],
},
"Hyprland": {
"desc": "Wayland dynamic tiling — animations, eye candy",
"server": "wayland",
"void": [], # not in official repos
"arch": ["hyprland", "kitty", "waybar", "wofi", "dunst", "xdg-desktop-portal-hyprland", "hypridle", "hyprlock", "qt5-wayland", "qt6-wayland", "polkit"],
"debian": ["hyprland", "kitty", "waybar", "wofi", "dunst", "xdg-desktop-portal-hyprland", "xwayland", "polkit"],
"alpine": [], # not in official repos
"fedora": ["hyprland", "kitty", "waybar", "wofi", "dunst", "xdg-desktop-portal-hyprland", "hypridle", "hyprlock", "polkit"],
},
"GNOME": {
"desc": "Full desktop — polished, Wayland by default",
"server": "wayland",
"void": ["gnome", "gnome-terminal", "gdm", "gnome-tweaks", "xorg"],
"arch": ["gnome", "gnome-extra", "gdm", "gnome-terminal", "xdg-desktop-portal-gnome"],
"debian": ["gnome", "gdm3", "gnome-terminal", "xdg-desktop-portal-gnome", "xorg"],
"alpine": ["gnome", "gdm", "gnome-terminal", "xorg-server"],
"fedora": ["gnome", "gdm", "gnome-terminal", "xdg-desktop-portal-gnome", "gnome-tweaks"],
},
"KDE Plasma": {
"desc": "Full desktop — customizable, feature-rich",
"server": "wayland",
"void": ["plasma-desktop", "konsole", "sddm", "dolphin", "kate", "xorg"],
"arch": ["plasma-meta", "konsole", "sddm", "dolphin", "kate", "xdg-desktop-portal-kde", "xorg-server"],
"debian": ["kde-plasma-desktop", "konsole", "sddm", "dolphin", "kate", "xdg-desktop-portal-kde", "xorg"],
"alpine": ["plasma-desktop-meta", "konsole", "sddm", "oxygen", "xorg-server"],
"fedora": ["plasma-desktop", "konsole", "sddm", "dolphin", "kate", "xdg-desktop-portal-kde", "qt6-wayland"],
},
"XFCE": {
"desc": "Lightweight desktop — traditional, fast",
"server": "x11",
"void": ["xfce4", "xfce4-terminal", "lightdm", "gvfs", "xorg"],
"arch": ["xfce4", "xfce4-goodies", "xfce4-terminal", "lightdm", "lightdm-gtk-greeter", "xorg-server"],
"debian": ["xfce4", "xfce4-goodies", "xfce4-terminal", "lightdm", "xorg", "gvfs"],
"alpine": ["xfce4", "xfce4-terminal", "lightdm", "xorg-server", "elogind"],
"fedora": ["xfce4-session", "xfce4-terminal", "lightdm", "xorg-x11-server-Xorg"],
},
"Cinnamon": {
"desc": "Modern traditional desktop — Windows-like",
"server": "x11",
"void": ["cinnamon", "gnome-terminal", "lightdm", "nemo", "xorg"],
"arch": ["cinnamon", "gnome-terminal", "lightdm", "lightdm-gtk-greeter", "nemo", "xorg-server"],
"debian": ["cinnamon", "gnome-terminal", "lightdm", "nemo", "xorg"],
"alpine": [], # limited support
"fedora": ["cinnamon", "gnome-terminal", "lightdm", "nemo", "xorg-x11-server-Xorg"],
},
"MATE": {
"desc": "Traditional desktop — GNOME 2 fork, lightweight",
"server": "x11",
"void": ["mate", "mate-terminal", "lightdm", "caja", "xorg"],
"arch": ["mate", "mate-extra", "mate-terminal", "lightdm", "lightdm-gtk-greeter", "caja", "xorg-server"],
"debian": ["mate-desktop-environment", "mate-terminal", "lightdm", "caja", "xorg"],
"alpine": ["mate-desktop-environment", "mate-terminal", "lightdm", "xorg-server"],
"fedora": ["mate-desktop", "mate-terminal", "lightdm", "caja", "xorg-x11-server-Xorg"],
},
"LXQt": {
"desc": "Ultra-lightweight desktop — Qt-based",
"server": "x11",
"void": ["lxqt", "qterminal", "sddm", "openbox", "xorg"],
"arch": ["lxqt", "qterminal", "sddm", "openbox", "xorg-server"],
"debian": ["lxqt", "qterminal", "sddm", "openbox", "xorg"],
"alpine": ["lxqt-desktop", "qterminal", "sddm", "openbox", "xorg-server"],
"fedora": ["lxqt", "qterminal", "sddm", "openbox", "xorg-x11-server-Xorg"],
},
"bspwm": {
"desc": "X11 tiling WM — binary space partitioning",
"server": "x11",
"void": ["bspwm", "sxhkd", "alacritty", "polybar", "dmenu", "rofi", "picom", "dunst", "xorg"],
"arch": ["bspwm", "sxhkd", "alacritty", "polybar", "dmenu", "rofi", "picom", "dunst", "xorg-server"],
"debian": ["bspwm", "sxhkd", "alacritty", "polybar", "dmenu", "rofi", "picom", "dunst", "xorg"],
"alpine": ["bspwm", "sxhkd", "alacritty", "polybar", "dmenu", "rofi", "xorg-server"],
"fedora": ["bspwm", "sxhkd", "alacritty", "polybar", "dmenu", "rofi", "picom", "dunst", "xorg-x11-server-Xorg"],
},
"openbox": {
"desc": "X11 stacking WM — minimalist, scriptable",
"server": "x11",
"void": ["openbox", "alacritty", "obconf", "tint2", "rofi", "picom", "dunst", "xorg"],
"arch": ["openbox", "alacritty", "obconf", "tint2", "rofi", "picom", "dunst", "xorg-server"],
"debian": ["openbox", "alacritty", "obconf", "tint2", "rofi", "picom", "dunst", "xorg"],
"alpine": ["openbox", "alacritty", "obconf", "tint2", "rofi", "xorg-server"],
"fedora": ["openbox", "alacritty", "obconf", "tint2", "rofi", "picom", "dunst", "xorg-x11-server-Xorg"],
},
"Headless (no DE)": {
"desc": "Server / minimal — no graphical environment",
"server": "none",
"void": [],
"arch": [],
"debian": [],
"alpine": [],
"fedora": [],
},
}
def _check_fakeroot() -> bool:
"""Check if fakeroot is available."""
@@ -296,6 +408,7 @@ def confirm_build(cfg: dict) -> bool:
table.add_row("Arch", cfg["arch"])
table.add_row("Init", cfg["init"])
table.add_row("Libc", cfg["libc"])
table.add_row("DE/WM", cfg.get("de_wm", "none"))
table.add_row("Packages", str(len(cfg.get("packages", []))))
table.add_row("ISO name", cfg["iso"]["name"])
table.add_row("Version", cfg["iso"]["version"])
@@ -319,6 +432,50 @@ def confirm_build(cfg: dict) -> bool:
return questionary.confirm("Start build?", default=True).ask() or sys.exit(0)
def select_de_wm(distro: str) -> tuple[str, list[str]]:
"""Pick a DE or window manager — returns (name, distro-specific packages)."""
console.print("[bold]Step 5b: Desktop Environment / Window Manager[/]\n")
# map distro to family
distro_family = {
"void": "void", "arch": "arch", "artix": "arch",
"debian": "debian", "ubuntu": "debian",
"alpine": "alpine",
"fedora": "fedora", "rocky": "fedora", "almalinux": "fedora",
}.get(distro, "arch")
# show table
de_table = Table(show_header=True, border_style="dim", pad_edge=False)
de_table.add_column("#", style="cyan", width=3)
de_table.add_column("DE/WM", style="bold")
de_table.add_column("Type")
de_table.add_column("Description")
for i, (name, info) in enumerate(DE_WM_PACKAGES.items(), 1):
pkgs = info.get(distro_family, [])
status = f"[green]{len(pkgs)} pkgs[/]" if pkgs else "[red]unavailable[/]"
server = info.get("server", "")
de_table.add_row(str(i), name, server, f"{info['desc']} ({status})")
console.print(de_table)
console.print()
names = list(DE_WM_PACKAGES.keys())
choice = questionary.select(
"Pick a DE/WM:",
choices=names,
).ask()
if choice is None:
sys.exit(0)
pkgs = DE_WM_PACKAGES.get(choice, {}).get(distro_family, [])
if not pkgs:
console.print(f" [yellow]Warning:[/] {choice} is not available in {distro} repos.")
console.print(f" [dim]Continuing without DE/WM packages.[/]\n")
else:
console.print(f" [green]{len(pkgs)} packages[/] for {choice} on {distro}\n")
return choice, pkgs
def build_progress(cfg: dict, func, *args, **kwargs):
"""Run a function with a rich progress spinner."""
from rich.progress import Progress, SpinnerColumn, TextColumn
@@ -347,15 +504,25 @@ def run_interactive() -> dict:
libc = select_libc(distro)
arch = select_arch()
pkgs = select_packages()
de_name, de_pkgs = select_de_wm(distro)
iso = configure_iso()
# merge packages: base groups + DE/WM
seen = set()
merged = []
for p in pkgs + de_pkgs:
if p not in seen:
seen.add(p)
merged.append(p)
cfg = {
"distro": distro,
"arch": arch,
"init": init,
"libc": libc,
"hostname": iso["hostname"],
"packages": pkgs,
"packages": merged,
"de_wm": de_name,
"services": [],
"users": [],
"iso": {