feat(decman): manage npm packages and deploy sddm theme assets
This commit is contained in:
@@ -295,6 +295,12 @@ def desktop_system() -> list[SystemConcern | UserConfigConcern]:
|
||||
"config/system/desktop/network/NetworkManager.conf"
|
||||
),
|
||||
"/etc/sddm.conf.d/10-theme.conf": "config/system/desktop/sddm/10-theme.conf",
|
||||
"/usr/share/sddm/themes/noctalia/Assets/ember-oled-background.svg": (
|
||||
"config/system/desktop/sddm/ember-oled-background.svg"
|
||||
),
|
||||
"/usr/share/sddm/themes/noctalia/theme.conf.user": (
|
||||
"config/system/desktop/sddm/theme.conf.user"
|
||||
),
|
||||
"/etc/udev/rules.d/50-zsa.rules": "config/system/desktop/input/50-zsa.rules",
|
||||
},
|
||||
system_manifests=("desktop/hardware-system.txt",),
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
"""Declarative global npm command packages for Decman profiles."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import shutil
|
||||
|
||||
import decman
|
||||
import decman.core.error as errors
|
||||
import decman.core.module as module
|
||||
import decman.core.output as output
|
||||
import decman.core.store as store_module
|
||||
from decman import Plugin
|
||||
from decman.plugins import run_methods_with_attribute
|
||||
|
||||
|
||||
def packages(fn):
|
||||
"""Annotate a method that returns globally installed npm package names."""
|
||||
fn.__npm_packages__ = True
|
||||
return fn
|
||||
|
||||
|
||||
class Npm(Plugin):
|
||||
"""Install declared global npm packages without pruning unrelated packages."""
|
||||
|
||||
NAME = "npm"
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.packages: set[str] = set()
|
||||
|
||||
def process_modules(
|
||||
self, store: store_module.Store, modules: list[module.Module]
|
||||
) -> None:
|
||||
store.ensure("npm_packages_for_module", {})
|
||||
|
||||
for mod in modules:
|
||||
store["npm_packages_for_module"].setdefault(mod.name, set())
|
||||
declared = set().union(
|
||||
*run_methods_with_attribute(mod, "__npm_packages__")
|
||||
)
|
||||
|
||||
if store["npm_packages_for_module"][mod.name] != declared:
|
||||
mod._changed = True
|
||||
output.print_debug(
|
||||
f"Module '{mod.name}' changed due to modified global npm packages."
|
||||
)
|
||||
|
||||
self.packages |= declared
|
||||
store["npm_packages_for_module"][mod.name] = declared
|
||||
|
||||
def apply(
|
||||
self,
|
||||
store: store_module.Store,
|
||||
dry_run: bool = False,
|
||||
params: list[str] | None = None,
|
||||
) -> bool:
|
||||
if not self.packages:
|
||||
return True
|
||||
|
||||
if shutil.which("npm") is None:
|
||||
if dry_run:
|
||||
output.print_list(
|
||||
"Installing global npm packages after npm becomes available:",
|
||||
sorted(self.packages),
|
||||
)
|
||||
return True
|
||||
output.print_error("npm is unavailable after the pacman step.")
|
||||
return False
|
||||
|
||||
try:
|
||||
installed = self._installed_packages()
|
||||
to_install = self.packages - installed
|
||||
output.print_list("Installing global npm packages:", sorted(to_install))
|
||||
if to_install and not dry_run:
|
||||
decman.prg(
|
||||
[
|
||||
"npm",
|
||||
"install",
|
||||
"--global",
|
||||
"--no-audit",
|
||||
"--no-fund",
|
||||
*sorted(to_install),
|
||||
]
|
||||
)
|
||||
except (errors.CommandFailedError, ValueError) as error:
|
||||
output.print_error(f"Managing global npm packages failed: {error}")
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def _installed_packages() -> set[str]:
|
||||
raw = decman.prg(
|
||||
["npm", "list", "--global", "--depth=0", "--json"], pty=False
|
||||
)
|
||||
try:
|
||||
result = json.loads(raw)
|
||||
except json.JSONDecodeError as error:
|
||||
raise ValueError("npm returned invalid JSON") from error
|
||||
dependencies = result.get("dependencies", {})
|
||||
if not isinstance(dependencies, dict):
|
||||
raise ValueError("npm dependencies must be an object")
|
||||
return set(dependencies)
|
||||
@@ -7,6 +7,8 @@ from pathlib import Path
|
||||
import decman
|
||||
from decman.plugins import aur, pacman
|
||||
|
||||
from modules import npm_packages
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
PACKAGES = ROOT / "packages"
|
||||
|
||||
@@ -34,6 +36,11 @@ class PackageConcern(decman.Module):
|
||||
def aur_packages(self) -> set[str]:
|
||||
return _names(PACKAGES / f"{self.concern}-aur.txt")
|
||||
|
||||
@npm_packages.packages
|
||||
def global_npm_packages(self) -> set[str]:
|
||||
path = PACKAGES / f"{self.concern}-npm.txt"
|
||||
return _names(path) if path.exists() else set()
|
||||
|
||||
|
||||
def configure_no_removal_guard() -> None:
|
||||
"""Keep Decman from pruning current dependency-orphans during adoption."""
|
||||
|
||||
Reference in New Issue
Block a user