Added modules

This commit is contained in:
Kivi Kaitaniemi
2024-04-25 18:24:37 +03:00
parent ebed2cbbee
commit 0e22d22a69
3 changed files with 375 additions and 37 deletions
+72
View File
@@ -38,3 +38,75 @@ class UserPackage:
self.make_dependencies = make_dependencies self.make_dependencies = make_dependencies
self.check_dependencies = check_dependencies self.check_dependencies = check_dependencies
self.git_url = git_url self.git_url = git_url
class Module:
"""
Collection of connected packages, services and files.
Inherit this class to create your own modules.
"""
def __init__(self, name: str, enabled: bool, version: str):
self.name = name
self.enabled = enabled
self.version = version
def on_enable(self):
"""
Override this method to run python code when this module gets enabled.
"""
def on_disable(self):
"""
Override this method to run python code when this module gets disabled.
Note! If this module is simply removed, the code will not exacute. Instead set enabled to
False.
"""
def after_update(self):
"""
Override this method to run python code after updating the system. If this module is
disabled, this will not run.
"""
def after_version_change(self):
"""
Override this method to run python code after the version of this module has changed.
"""
def pacman_packages(self) -> list[str]:
"""
Override this method to return pacman packages that should be installed as a part of this
Module.
"""
return []
def user_packages(self) -> list[UserPackage]:
"""
Override this method to return user packages that should be installed as a part of this
Module.
"""
return []
def aur_packages(self) -> list[str]:
"""
Override this method to return AUR packages that should be installed as a part of this
Module.
"""
return []
def systemd_units(self) -> list[str]:
"""
Override this method to return systemd units that should be enabled as a part of this
Module.
"""
return []
def systemd_user_units(self) -> dict[str, list[str]]:
"""
Override this method to return systemd user units that should be enabled as a part of this
Module.
"""
return {}
+139 -29
View File
@@ -132,7 +132,8 @@ class Store:
def __init__(self): def __init__(self):
self.enabled_systemd_units: list[str] = [] self.enabled_systemd_units: list[str] = []
self.enabled_user_systemd_units: list[str] = [] self.enabled_user_systemd_units: list[tuple[str, str]] = []
self.enabled_modules: dict[str, str] = {}
self.pkgbuild_latest_reviewed_commits: dict[str, str] = {} self.pkgbuild_latest_reviewed_commits: dict[str, str] = {}
self._package_file_cache: dict[str, tuple[str, str]] = {} self._package_file_cache: dict[str, tuple[str, str]] = {}
@@ -168,6 +169,7 @@ class Store:
d = { d = {
"enabled_systemd_units": self.enabled_systemd_units, "enabled_systemd_units": self.enabled_systemd_units,
"enabled_user_systemd_units": self.enabled_user_systemd_units, "enabled_user_systemd_units": self.enabled_user_systemd_units,
"enabled_modules": self.enabled_modules,
"package_file_cache": self._package_file_cache, "package_file_cache": self._package_file_cache,
"pkgbuild_git_commits": self.pkgbuild_latest_reviewed_commits "pkgbuild_git_commits": self.pkgbuild_latest_reviewed_commits
} }
@@ -197,13 +199,20 @@ class Store:
with open(path, "rt", encoding="utf-8") as file: with open(path, "rt", encoding="utf-8") as file:
d = json.load(file) d = json.load(file)
store.enabled_systemd_units = d.get("enabled_systemd_units", store.enabled_systemd_units = d.get(
[]) "enabled_systemd_units",
[],
)
store.enabled_user_systemd_units = d.get( store.enabled_user_systemd_units = d.get(
"enabled_user_systemd_units", []) "enabled_user_systemd_units",
[],
)
store.enabled_modules = d.get("enabled_modules", {})
store._package_file_cache = d.get("package_file_cache", {}) store._package_file_cache = d.get("package_file_cache", {})
store.pkgbuild_latest_reviewed_commits = d.get( store.pkgbuild_latest_reviewed_commits = d.get(
"pkgbuild_git_commits", {}) "pkgbuild_git_commits",
{},
)
return store return store
except json.JSONDecodeError as e: except json.JSONDecodeError as e:
@@ -226,23 +235,65 @@ class Source:
Configuration that describes a system. Configuration that describes a system.
""" """
def __init__(self, pacman_packages: list[str], aur_packages: list[str], def __init__(
user_packages: list[decman.UserPackage], self,
ignored_packages: list[str], systemd_units: list[str], pacman_packages: list[str],
systemd_user_units: dict[str, list[str]]): aur_packages: list[str],
user_packages: list[decman.UserPackage],
ignored_packages: list[str],
systemd_units: list[str],
systemd_user_units: dict[str, list[str]],
modules: list[decman.Module],
):
self.pacman_packages = pacman_packages self.pacman_packages = pacman_packages
self.aur_packages = aur_packages self.aur_packages = aur_packages
self.user_packages = user_packages self.user_packages = user_packages
self.ignored_packages = ignored_packages self.ignored_packages = ignored_packages
self.systemd_units = systemd_units self.systemd_units = systemd_units
self.systemd_user_units = systemd_user_units self.systemd_user_units = systemd_user_units
self.modules = modules
def run_on_enable(self, store: Store):
"""
Runs on_enable of every module that was now enabled.
"""
for module in self.modules:
if module.enabled and module.name not in store.enabled_modules:
module.on_enable()
def run_on_disable(self, store: Store):
"""
Runs on_disable of every module that was now disabled.
"""
for module in self.modules:
if not module.enabled and module.name in store.enabled_modules:
module.on_disable()
def run_after_update(self):
"""
Runs after_update of every enabled module.
"""
for module in self.modules:
if module.enabled:
module.after_update()
def run_after_version_change(self, store: Store):
"""
Runs after_version_change of every enabled module that has it's version changed.
"""
for module in self.modules:
if module.enabled and module.version != store.enabled_modules.get(
module.name, module.version):
module.after_version_change()
elif module.enabled and module.name not in store.enabled_modules:
module.after_version_change()
def units_to_enable(self, store: Store) -> list[str]: def units_to_enable(self, store: Store) -> list[str]:
""" """
Returns all systemd units that should be enabled. Returns all systemd units that should be enabled.
""" """
result = [] result = []
for unit in self.systemd_units: for unit in self._all_units():
if unit not in store.enabled_systemd_units: if unit not in store.enabled_systemd_units:
result.append(unit) result.append(unit)
return result return result
@@ -253,7 +304,7 @@ class Source:
""" """
result = [] result = []
for unit in store.enabled_systemd_units: for unit in store.enabled_systemd_units:
if unit not in self.systemd_units: if unit not in self._all_units():
result.append(unit) result.append(unit)
return result return result
@@ -262,9 +313,9 @@ class Source:
Returns all user systemd units that should be enabled. Returns all user systemd units that should be enabled.
""" """
result = {} result = {}
for user, units in self.systemd_user_units.items(): for user, units in self._all_user_units().items():
for unit in units: for unit in units:
stored = f"{user}: {unit}" stored = (user, unit)
if stored not in store.enabled_user_systemd_units: if stored not in store.enabled_user_systemd_units:
entry = result.get(user, []) entry = result.get(user, [])
entry.append(unit) entry.append(unit)
@@ -277,11 +328,9 @@ class Source:
""" """
result = {} result = {}
for stored in store.enabled_user_systemd_units: for stored in store.enabled_user_systemd_units:
s = stored.split(": ") user, unit = stored
user = s[0]
unit = s[1]
if unit not in self.systemd_user_units.get(user, []): if unit not in self._all_user_units().get(user, []):
entry = result.get(user, []) entry = result.get(user, [])
entry.append(unit) entry.append(unit)
result[user] = entry result[user] = entry
@@ -293,10 +342,10 @@ class Source:
Returns all packages that should be removed. This includes pacman, aur and user packages. Returns all packages that should be removed. This includes pacman, aur and user packages.
""" """
result = [] result = []
all_pkgs = self.pacman_packages + self.aur_packages + list(
map(lambda p: p.pkgname, self.user_packages))
for pkg in currently_installed_packages: for pkg in currently_installed_packages:
if pkg not in all_pkgs and pkg not in self.ignored_packages: if pkg in self.ignored_packages:
continue
if pkg not in self._all_pkgs():
result.append(pkg) result.append(pkg)
return result return result
@@ -306,8 +355,10 @@ class Source:
Returns all pacman packages that should be installed. Returns all pacman packages that should be installed.
""" """
result = [] result = []
for pkg in self.pacman_packages: for pkg in self._all_pacman_pkgs():
if pkg not in currently_installed_packages and pkg not in self.ignored_packages: if pkg in self.ignored_packages:
continue
if pkg not in currently_installed_packages:
result.append(pkg) result.append(pkg)
return result return result
@@ -317,13 +368,72 @@ class Source:
Returns all aur and user packages that should be installed. Returns all aur and user packages that should be installed.
""" """
result = [] result = []
for pkg in self.aur_packages: for pkg in self._all_foreign_pkgs():
if pkg not in currently_installed_packages and pkg not in self.ignored_packages: if pkg in self.ignored_packages:
continue
if pkg not in currently_installed_packages:
result.append(pkg) result.append(pkg)
return result
for pkg in self.user_packages: def all_enabled_modules(self) -> list[tuple[str, str]]:
if pkg.pkgname not in currently_installed_packages and pkg.pkgname not in self.ignored_packages: """
result.append(pkg.pkgname) Returns all enabled modules and their versions.
"""
result = []
for module in self.modules:
if module.enabled:
result.append((module.name, module.version))
return result
def all_user_pkgs(self) -> list[decman.UserPackage]:
"""
Returns all active UserPackages.
"""
result = []
result.extend(self.user_packages)
for module in self.modules:
if module.enabled:
result.extend(module.user_packages())
return result
def _all_pacman_pkgs(self) -> list[str]:
result = []
result.extend(self.pacman_packages)
for module in self.modules:
if module.enabled:
result.extend(module.pacman_packages())
return result
def _all_foreign_pkgs(self) -> list[str]:
result = []
result.extend(self.aur_packages)
result.extend(map(lambda p: p.pkgname, self.user_packages))
for module in self.modules:
if module.enabled:
result.extend(module.aur_packages())
result.extend(map(lambda p: p.pkgname, module.user_packages()))
return result
def _all_pkgs(self) -> list[str]:
result = []
result.extend(self._all_pacman_pkgs())
result.extend(self._all_foreign_pkgs())
return result
def _all_units(self) -> list[str]:
result = []
result.extend(self.systemd_units)
for module in self.modules:
if module.enabled:
result.extend(module.systemd_units())
return result
def _all_user_units(self) -> dict[str, list[str]]:
result = {}
result.update(self.systemd_user_units)
for module in self.modules:
if module.enabled:
result.update(module.systemd_user_units())
return result return result
@@ -479,7 +589,7 @@ class Systemd:
Enables the given units for the given user. Enables the given units for the given user.
""" """
for unit in units: for unit in units:
self.state.enabled_user_systemd_units.append(f"{user}: {unit}") self.state.enabled_user_systemd_units.append((user, unit))
try: try:
uid = pwd.getpwnam(user).pw_uid uid = pwd.getpwnam(user).pw_uid
gid = pwd.getpwnam(user).pw_gid gid = pwd.getpwnam(user).pw_gid
@@ -502,7 +612,7 @@ class Systemd:
""" """
for unit in units: for unit in units:
try: try:
self.state.enabled_user_systemd_units.remove(f"{user}: {unit}") self.state.enabled_user_systemd_units.remove((user, unit))
except ValueError: except ValueError:
pass pass
try: try:
+164 -8
View File
@@ -2,12 +2,122 @@
import unittest import unittest
from decman.lib import Source, Store from decman.lib import Source, Store
from decman import UserPackage from decman import UserPackage, Module
class ExistingTestModule(Module):
def __init__(self):
self.on_enable_executed = False
self.on_disable_executed = False
self.after_update_executed = False
self.after_version_change_executed = False
super().__init__("Existing", True, "1")
def on_enable(self):
self.on_enable_executed = True
def on_disable(self):
self.on_disable_executed = True
def after_update(self):
self.after_update_executed = True
def after_version_change(self):
self.after_version_change_executed = True
class ExistingChangedVersionTestModule(Module):
def __init__(self):
self.on_enable_executed = False
self.on_disable_executed = False
self.after_update_executed = False
self.after_version_change_executed = False
super().__init__("ExistingChanged", True, "2")
def on_enable(self):
self.on_enable_executed = True
def on_disable(self):
self.on_disable_executed = True
def after_update(self):
self.after_update_executed = True
def after_version_change(self):
self.after_version_change_executed = True
class EnabledTestModule(Module):
def __init__(self):
self.on_enable_executed = False
self.on_disable_executed = False
self.after_update_executed = False
self.after_version_change_executed = False
super().__init__("Enabled", True, "1")
def on_enable(self):
self.on_enable_executed = True
def on_disable(self):
self.on_disable_executed = True
def after_update(self):
self.after_update_executed = True
def after_version_change(self):
self.after_version_change_executed = True
def pacman_packages(self) -> list[str]:
return ["M_p1", "M_p2", "M_p3"]
def systemd_user_units(self) -> dict[str, list[str]]:
return {"muser": ["M_u1.service"]}
class DisabledTestModule(Module):
def __init__(self):
self.on_enable_executed = False
self.on_disable_executed = False
self.after_update_executed = False
self.after_version_change_executed = False
super().__init__("Disabled", False, "1")
def on_enable(self):
self.on_enable_executed = True
def on_disable(self):
self.on_disable_executed = True
def after_update(self):
self.after_update_executed = True
def after_version_change(self):
self.after_version_change_executed = True
def aur_packages(self) -> list[str]:
return ["M_A1", "M_A2", "M_A3"]
def systemd_units(self) -> list[str]:
return ["M_1.service"]
class TestSource(unittest.TestCase): class TestSource(unittest.TestCase):
def setUp(self): def setUp(self):
self.disabled_module = DisabledTestModule()
self.enabled_module = EnabledTestModule()
self.existing_module = ExistingTestModule()
self.existing_module_changed = ExistingChangedVersionTestModule()
modules = [
self.enabled_module,
self.disabled_module,
self.existing_module,
self.existing_module_changed,
]
source = Source( source = Source(
pacman_packages=["p1", "p2", "p3"], pacman_packages=["p1", "p2", "p3"],
aur_packages=["A1", "A2", "A3"], aur_packages=["A1", "A2", "A3"],
@@ -28,12 +138,19 @@ class TestSource(unittest.TestCase):
ignored_packages=["i1", "i2"], ignored_packages=["i1", "i2"],
systemd_units=["1.service", "2.timer"], systemd_units=["1.service", "2.timer"],
systemd_user_units={"user": ["u1.service", "u2.timer"]}, systemd_user_units={"user": ["u1.service", "u2.timer"]},
modules=modules,
) )
store = Store() store = Store()
store.enabled_systemd_units.extend(["1.service", "3.service"]) store.enabled_systemd_units.extend(
store.enabled_user_systemd_units.extend( ["1.service", "3.service", "M_1.service"])
["user: u1.service", "user: u3.service"]) store.enabled_user_systemd_units.extend([("user", "u1.service"),
("user", "u3.service")])
store.enabled_modules = {
"Existing": "1",
"ExistingChanged": "1",
"Disabled": "1",
}
currently_installed_packages = [ currently_installed_packages = [
"p1", "p1",
@@ -44,12 +161,48 @@ class TestSource(unittest.TestCase):
"A4", "A4",
"U1", "U1",
"i1", "i1",
"M_p3",
"M_A1",
"M_A2",
] ]
self.source = source self.source = source
self.store = store self.store = store
self.currently_installed_packages = currently_installed_packages self.currently_installed_packages = currently_installed_packages
def test_after_update_executed(self):
self.source.run_after_update()
self.assertTrue(self.enabled_module.after_update_executed)
self.assertTrue(self.existing_module.after_update_executed)
self.assertTrue(self.existing_module_changed.after_update_executed)
self.assertFalse(self.disabled_module.after_update_executed)
def test_after_version_change_executed(self):
self.source.run_after_version_change(self.store)
self.assertTrue(self.enabled_module.after_version_change_executed)
self.assertTrue(
self.existing_module_changed.after_version_change_executed)
self.assertFalse(self.existing_module.after_version_change_executed)
self.assertFalse(self.disabled_module.after_version_change_executed)
def test_on_enable_executed(self):
self.source.run_on_enable(self.store)
self.assertTrue(self.enabled_module.on_enable_executed)
self.assertFalse(self.disabled_module.on_enable_executed)
self.assertFalse(self.existing_module.on_enable_executed)
self.assertFalse(self.existing_module_changed.on_enable_executed)
def test_on_disable_executed(self):
self.source.run_on_disable(self.store)
self.assertTrue(self.disabled_module.on_disable_executed)
self.assertFalse(self.enabled_module.on_disable_executed)
self.assertFalse(self.existing_module.on_disable_executed)
self.assertFalse(self.existing_module_changed.on_disable_executed)
def test_units_to_enable(self): def test_units_to_enable(self):
self.assertCountEqual( self.assertCountEqual(
self.source.units_to_enable(self.store), self.source.units_to_enable(self.store),
@@ -59,13 +212,16 @@ class TestSource(unittest.TestCase):
def test_units_to_disable(self): def test_units_to_disable(self):
self.assertCountEqual( self.assertCountEqual(
self.source.units_to_disable(self.store), self.source.units_to_disable(self.store),
["3.service"], ["3.service", "M_1.service"],
) )
def test_user_units_to_enable(self): def test_user_units_to_enable(self):
self.assertDictEqual( self.assertDictEqual(
self.source.user_units_to_enable(self.store), self.source.user_units_to_enable(self.store),
{"user": ["u2.timer"]}, {
"user": ["u2.timer"],
"muser": ["M_u1.service"]
},
) )
def test_user_units_to_disable(self): def test_user_units_to_disable(self):
@@ -78,7 +234,7 @@ class TestSource(unittest.TestCase):
self.assertCountEqual( self.assertCountEqual(
self.source.pacman_packages_to_install( self.source.pacman_packages_to_install(
self.currently_installed_packages), self.currently_installed_packages),
["p3"], ["p3", "M_p1", "M_p2"],
) )
def test_foreign_packages_to_install(self): def test_foreign_packages_to_install(self):
@@ -91,5 +247,5 @@ class TestSource(unittest.TestCase):
def test_packages_to_remove(self): def test_packages_to_remove(self):
self.assertCountEqual( self.assertCountEqual(
self.source.packages_to_remove(self.currently_installed_packages), self.source.packages_to_remove(self.currently_installed_packages),
["p4", "A4"], ["p4", "A4", "M_A1", "M_A2"],
) )