feat(decman): manage npm packages and deploy sddm theme assets
This commit is contained in:
@@ -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)
|
||||
Reference in New Issue
Block a user