feat: track Decman configuration
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
# Modules
|
||||
|
||||
Only two adapters belong here:
|
||||
|
||||
- `packages.py` turns package concern lists into Decman package declarations.
|
||||
- `host_system.py` deploys reviewed system policy, user configuration, and enabled units for a host.
|
||||
|
||||
Keep policy in `packages/`, `config/`, and `manifests/`; keep this directory
|
||||
thin and reusable. User configuration belongs under `config/user/{common,laptop,desktop}/`
|
||||
and is installed with owner `alex`.
|
||||
@@ -0,0 +1,238 @@
|
||||
"""System files and concern-scoped unit enablement for machine profiles."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
import decman
|
||||
from decman.plugins import systemd
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
MANIFESTS = ROOT / "manifests"
|
||||
|
||||
|
||||
def _units(*names: str) -> set[str]:
|
||||
units: set[str] = set()
|
||||
for name in names:
|
||||
units |= {
|
||||
line.strip()
|
||||
for line in (MANIFESTS / name).read_text(encoding="utf-8").splitlines()
|
||||
if line.strip()
|
||||
}
|
||||
return units
|
||||
|
||||
|
||||
class UserConfigConcern(decman.Module):
|
||||
"""Home-directory configuration owned by a named local user."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
name: str,
|
||||
files: dict[str, str] | None = None,
|
||||
directories: dict[str, str] | None = None,
|
||||
user: str = "alex",
|
||||
) -> None:
|
||||
super().__init__(f"user:{name}")
|
||||
self._files = files or {}
|
||||
self._directories = directories or {}
|
||||
self.user = user
|
||||
|
||||
def files(self) -> dict[str, decman.File]:
|
||||
return {
|
||||
target: decman.File(
|
||||
source_file=str(ROOT / source), owner=self.user, permissions=0o644
|
||||
)
|
||||
for target, source in self._files.items()
|
||||
}
|
||||
|
||||
def directories(self) -> dict[str, decman.Directory]:
|
||||
return {
|
||||
target: decman.Directory(
|
||||
source_directory=str(ROOT / source),
|
||||
bin_files=True,
|
||||
owner=self.user,
|
||||
permissions=0o644,
|
||||
)
|
||||
for target, source in self._directories.items()
|
||||
}
|
||||
|
||||
|
||||
class SystemConcern(decman.Module):
|
||||
"""One host/system concern with explicit files and unit manifests."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
name: str,
|
||||
files: dict[str, str | tuple[str, int]] | None = None,
|
||||
system_manifests: tuple[str, ...] = (),
|
||||
user_manifests: tuple[str, ...] = (),
|
||||
user: str = "alex",
|
||||
) -> None:
|
||||
super().__init__(f"system:{name}")
|
||||
self._files = files or {}
|
||||
self._system_manifests = system_manifests
|
||||
self._user_manifests = user_manifests
|
||||
self.user = user
|
||||
|
||||
def files(self) -> dict[str, decman.File]:
|
||||
return {
|
||||
target: decman.File(
|
||||
source_file=str(
|
||||
ROOT / (source[0] if isinstance(source, tuple) else source)
|
||||
),
|
||||
permissions=source[1] if isinstance(source, tuple) else 0o644,
|
||||
)
|
||||
for target, source in self._files.items()
|
||||
}
|
||||
|
||||
def on_change(self, store: decman.Store) -> None:
|
||||
if self.name == "system:locale":
|
||||
decman.prg(["locale-gen"])
|
||||
if self.name == "system:boot":
|
||||
decman.prg(["mkinitcpio", "-P"])
|
||||
|
||||
@systemd.units
|
||||
def system_units(self) -> set[str]:
|
||||
return _units(*self._system_manifests)
|
||||
|
||||
@systemd.user_units
|
||||
def user_units(self) -> dict[str, set[str]]:
|
||||
return {self.user: _units(*self._user_manifests)}
|
||||
|
||||
|
||||
def common_user_config() -> list[UserConfigConcern]:
|
||||
return [
|
||||
UserConfigConcern(
|
||||
"common-config",
|
||||
{
|
||||
"/home/alex/.config/environment.d/pi-status-bridge.conf": (
|
||||
"config/user/common/environment.d/pi-status-bridge.conf"
|
||||
),
|
||||
"/home/alex/.config/fish/config.fish": "config/user/common/fish/config.fish",
|
||||
"/home/alex/.config/hypr/hypridle.conf": "config/user/common/hypr/hypridle.conf",
|
||||
"/home/alex/.config/hypr/hyprland.lua": "config/user/common/hypr/hyprland.lua",
|
||||
"/home/alex/.config/hypr/hyprlock.conf": "config/user/common/hypr/hyprlock.conf",
|
||||
"/home/alex/.config/kitty/kitty.conf": "config/user/common/kitty/kitty.conf",
|
||||
"/home/alex/.config/noctalia/10-base.toml": "config/user/common/noctalia/10-base.toml",
|
||||
"/home/alex/.config/noctalia/palettes/Ember OLED.json": (
|
||||
"config/user/common/noctalia/palettes/Ember OLED.json"
|
||||
),
|
||||
"/home/alex/.config/systemd/user/idle-inhibitor-monitor.service": (
|
||||
"config/user/common/systemd/user/idle-inhibitor-monitor.service"
|
||||
),
|
||||
"/home/alex/.config/systemd/user/pi-status-bridge.service": (
|
||||
"config/user/common/systemd/user/pi-status-bridge.service"
|
||||
),
|
||||
"/home/alex/.config/systemd/user/ssh-agent.service": (
|
||||
"config/user/common/systemd/user/ssh-agent.service"
|
||||
),
|
||||
},
|
||||
{"/home/alex/.config/yazi": "config/user/common/yazi"},
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
def laptop_user_config() -> list[UserConfigConcern]:
|
||||
return [
|
||||
UserConfigConcern(
|
||||
"laptop-config",
|
||||
{
|
||||
"/home/alex/.config/hypr/machine.lua": "config/user/laptop/hypr/machine.lua",
|
||||
"/home/alex/.config/noctalia/90-machine.toml": (
|
||||
"config/user/laptop/noctalia/90-machine.toml"
|
||||
),
|
||||
"/home/alex/.config/noctalia/99-extend.toml": (
|
||||
"config/user/laptop/noctalia/99-extend.toml"
|
||||
),
|
||||
},
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
def desktop_user_config() -> list[UserConfigConcern]:
|
||||
return [
|
||||
UserConfigConcern(
|
||||
"desktop-config",
|
||||
{
|
||||
"/home/alex/.config/hypr/machine.lua": "config/user/desktop/hypr/machine.lua",
|
||||
"/home/alex/.config/noctalia/90-machine.toml": (
|
||||
"config/user/desktop/noctalia/90-machine.toml"
|
||||
),
|
||||
},
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
def common_system() -> list[SystemConcern | UserConfigConcern]:
|
||||
return [
|
||||
SystemConcern(
|
||||
"package-policy",
|
||||
{
|
||||
"/etc/pacman.conf": "config/system/common/pacman/pacman.conf",
|
||||
"/etc/makepkg.conf": "config/system/common/makepkg/makepkg.conf",
|
||||
},
|
||||
),
|
||||
SystemConcern(
|
||||
"locale",
|
||||
{
|
||||
"/etc/locale.conf": "config/system/common/locale/locale.conf",
|
||||
"/etc/locale.gen": "config/system/common/locale/locale.gen",
|
||||
},
|
||||
),
|
||||
SystemConcern(
|
||||
"keyboard",
|
||||
{
|
||||
"/etc/vconsole.conf": "config/system/common/keyboard/vconsole.conf",
|
||||
"/etc/X11/xorg.conf.d/00-keyboard.conf": "config/system/common/keyboard/x11.conf",
|
||||
},
|
||||
),
|
||||
SystemConcern("base", system_manifests=("common/base-system.txt",)),
|
||||
SystemConcern("network", system_manifests=("common/network-system.txt",)),
|
||||
SystemConcern("desktop", user_manifests=("common/desktop-user.txt",)),
|
||||
SystemConcern("local", user_manifests=("common/local-user.txt",)),
|
||||
SystemConcern(
|
||||
"network-services", system_manifests=("common/network-services-system.txt",)
|
||||
),
|
||||
SystemConcern(
|
||||
"development", system_manifests=("common/development-system.txt",)
|
||||
),
|
||||
SystemConcern("privacy", system_manifests=("common/privacy-system.txt",)),
|
||||
*common_user_config(),
|
||||
]
|
||||
|
||||
|
||||
def laptop_system() -> list[SystemConcern | UserConfigConcern]:
|
||||
return [
|
||||
*common_system(),
|
||||
SystemConcern(
|
||||
"laptop",
|
||||
{
|
||||
"/etc/keyd/default.conf": "config/system/common/keyd.conf",
|
||||
"/etc/NetworkManager/conf.d/wifi-powersave.conf": "config/system/laptop/wifi-powersave.conf",
|
||||
"/etc/modprobe.d/iwlwifi.conf": "config/system/laptop/iwlwifi.conf",
|
||||
"/etc/X11/xorg.conf.d/30-touchpad.conf": "config/system/laptop/touchpad.conf",
|
||||
"/etc/tlp.conf": "config/system/laptop/power/tlp.conf",
|
||||
"/etc/systemd/sleep.conf.d/90-laptop.conf": "config/system/laptop/sleep.conf.d/90-laptop.conf",
|
||||
"/etc/systemd/logind.conf.d/90-noctalia-battery-idle.conf": "config/system/laptop/systemd/90-noctalia-battery-idle.conf",
|
||||
"/boot/EFI/refind/refind.conf": (
|
||||
"config/system/laptop/refind/refind.conf",
|
||||
0o755,
|
||||
),
|
||||
"/boot/refind_linux.conf": (
|
||||
"config/system/laptop/refind/refind_linux.conf",
|
||||
0o755,
|
||||
),
|
||||
},
|
||||
system_manifests=("laptop/hardware-system.txt",),
|
||||
),
|
||||
SystemConcern(
|
||||
"boot",
|
||||
{"/etc/mkinitcpio.conf": "config/system/laptop/boot/mkinitcpio.conf"},
|
||||
),
|
||||
*laptop_user_config(),
|
||||
]
|
||||
|
||||
|
||||
def desktop_system() -> list[SystemConcern | UserConfigConcern]:
|
||||
"""Desktop: shared policy plus desktop-specific home configuration."""
|
||||
return [*common_system(), *desktop_user_config()]
|
||||
@@ -0,0 +1,43 @@
|
||||
"""Package concern loading for Decman profiles."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
import decman
|
||||
from decman.plugins import aur, pacman
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
PACKAGES = ROOT / "packages"
|
||||
|
||||
|
||||
def _names(path: Path) -> set[str]:
|
||||
return {
|
||||
line.strip()
|
||||
for line in path.read_text(encoding="utf-8").splitlines()
|
||||
if line.strip()
|
||||
}
|
||||
|
||||
|
||||
class PackageConcern(decman.Module):
|
||||
"""One named concern backed by native and AUR package lists."""
|
||||
|
||||
def __init__(self, concern: str) -> None:
|
||||
super().__init__(f"packages:{concern}")
|
||||
self.concern = concern
|
||||
|
||||
@pacman.packages
|
||||
def native_packages(self) -> set[str]:
|
||||
return _names(PACKAGES / f"{self.concern}-repo.txt")
|
||||
|
||||
@aur.packages
|
||||
def aur_packages(self) -> set[str]:
|
||||
return _names(PACKAGES / f"{self.concern}-aur.txt")
|
||||
|
||||
|
||||
def configure_no_removal_guard() -> None:
|
||||
"""Keep Decman from pruning current dependency-orphans during adoption."""
|
||||
if decman.pacman is None or decman.aur is None:
|
||||
raise decman.SourceError("pacman and AUR plugins must be available")
|
||||
decman.pacman.ignored_packages |= _names(PACKAGES / "ignored/repo-dependencies.txt")
|
||||
decman.aur.ignored_packages |= _names(PACKAGES / "ignored/aur-dependencies.txt")
|
||||
Reference in New Issue
Block a user