#!/usr/bin/env python3 import subprocess import shutil from argparse import ArgumentParser from pathlib import Path BASE_PKGS = [ "clang", # C/C++ development "scripts", # scripts & snippets "stow", # --target=~ ] # for TUI only setups # e.g. servers that are accessed via SSH only TUI_PKGS = [ *BASE_PKGS, "fastfetch", # sys info, "helix", # editor "nvim", # editor "shell", # fish & .bash_profile & shell prompt "yazi", # terminal file manager ] # for all WMs and DEs GUI_BASE_PKGS = [ *TUI_PKGS, "cava", "kitty", # terminal emulator "ghostty", # alternative terminal emulator "misc", # miscellaneous GUI configs (e.g. *-flags) # "mpv", # media player "wallpaper", # wallpapers & manager "kvantum", # qt theming "nwg-look", # gtk theming "wezterm", # alternative terminal emulator ] # for Hyprland setup # HYPRLAND_PKGS = [ # *GUI_BASE_PKGS, # "eww", # widgets # "hypr", # hypr family # "hyprland", # wm config # "mako", # notifications # "rofi", # application launcher # "waybar", # status bar # "wlogout", # logout menu # ] # for Niri setup NIRI_PKGS = [ *GUI_BASE_PKGS, "hypr", # for hyprlock & hypridle "niri", # wm config "quickshell", # widgets & status bar & notifications & ... "rofi", # application launcher "wlogout", # logout menu ] PKGS = { "base": BASE_PKGS, "tui": TUI_PKGS, "gui": GUI_BASE_PKGS, # "hyprland": HYPRLAND_PKGS, "niri": NIRI_PKGS, } SESSION_NAME = { "hyprland": "Hyprland", "niri": "niri", "default": "default", } PKGS_PATH = Path(__file__).resolve().parent.resolve() / "config" DEST_PATH = Path.home().expanduser() def _log(level: str, message: str): color = ( "\033[92m" if level == "INFO" else "\033[91m" if level == "ERROR" else "\033[0m" ) reset = "\033[0m" print(f"{color}[{level}] {message}{reset}") def check_deps() -> bool: required_deps = ["stow"] for dep in required_deps: if not shutil.which(dep): _log("ERROR", f"Required dependency '{dep}' is not installed.") return False return True def stow(pkg: str): subprocess.run( ["stow", "-v", "-d", str(PKGS_PATH), "-t", str(DEST_PATH), pkg], check=True ) def unstow(pkg: str): subprocess.run( ["stow", "-v", "-d", str(PKGS_PATH), "-t", str(DEST_PATH), "-D", pkg], check=True, ) def switch(session: str): subprocess.run( [str(Path("~/.local/scripts/config-switch").expanduser()), session], check=True ) def main(): if not check_deps(): exit(1) parser = ArgumentParser( description="Stow configuration packages in this repository." ) parser.add_argument( "package", choices=PKGS.keys(), help="The configuration package group to stow." ) parser.add_argument( "--unstow", action="store_true", help="Unstow the specified package group instead of stowing it.", ) args = parser.parse_args() selected_pkgs = PKGS[args.package] is_unstow = args.unstow for pkg in selected_pkgs: try: if is_unstow: unstow(pkg) _log("INFO", f"Successfully unstowed package '{pkg}'.") else: stow(pkg) _log("INFO", f"Successfully stowed package '{pkg}'.") except subprocess.CalledProcessError as e: _log("ERROR", f"Failed to stow package '{pkg}': {e}") if is_unstow: return # No need to switch session if we're just unstowing if args.package in SESSION_NAME: session = SESSION_NAME[args.package] else: session = SESSION_NAME["default"] try: switch(session) _log("INFO", f"Switched to session profile '{session}'.") except subprocess.CalledProcessError as e: _log("ERROR", f"Failed to switch session profile '{session}': {e}") if __name__ == "__main__": main()