decman.modules should be a list

This commit is contained in:
Kivi Kaitaniemi
2025-12-28 05:02:17 +02:00
parent 7e0b49be2f
commit 9b3454f434
18 changed files with 47 additions and 19 deletions
+1 -1
View File
@@ -102,7 +102,7 @@ Then import your module in your main source file.
import decman import decman
from syncthing import Syncthing from syncthing import Syncthing
decman.modules |= {Syncthing()} decman.modules += [Syncthing()]
``` ```
Then run decman. Then run decman.
+5 -3
View File
@@ -7,6 +7,8 @@ This contains the documentation for decman. Each plugin has its own documentatio
- [aur](/docs/aur.md) - [aur](/docs/aur.md)
- [flatpak](/docs/flatpak.md) - [flatpak](/docs/flatpak.md)
Check out [extras](docs/extras.md) for documentation for built-in modules.
## Quick notes ## Quick notes
"Decman source" or "source" refers to your system configuration. It is set using the `--source` command line argument with decman. "Decman source" or "source" refers to your system configuration. It is set using the `--source` command line argument with decman.
@@ -210,11 +212,11 @@ A **Module** is the primary unit for grouping related files, directories, packag
Each module is uniquely identified by its `name`. Each module is uniquely identified by its `name`.
Remember to add modules to decman. Remember to add modules to decman. Modules are added to a list to preserve deterministic execution order for hooks.
```py ```py
import decman import decman
decman.modules |= {MyModule()} decman.modules += [MyModule()]
``` ```
### Basic Structure ### Basic Structure
@@ -418,7 +420,7 @@ This method only gathers information. It doesn't apply it.
```py ```py
from decman import Store, Module from decman import Store, Module
def process_modules(self, store: Store, modules: set[Module]): def process_modules(self, store: Store, modules: list[Module]):
... ...
# Toy example for setting modules as changed # Toy example for setting modules as changed
+5
View File
@@ -79,6 +79,11 @@ class MyModule(decman.Module):
If these sets change, this plugin will flag the module as changed. The module's `on_change` method will be executed. If these sets change, this plugin will flag the module as changed. The module's `on_change` method will be executed.
## Keys used in the decman store
- `aur_packages_for_module`
- `custom_packages_for_module`
## Configuration ## Configuration
This module has partially the same configuration with pacman. You'll have to define pacman output keywords and database options again. This module has partially the same configuration with pacman. You'll have to define pacman output keywords and database options again.
+5
View File
@@ -47,6 +47,11 @@ class MyModule(decman.Module):
If packages or user packages change, this plugin will flag the module as changed. The module's `on_change` method will be executed. If packages or user packages change, this plugin will flag the module as changed. The module's `on_change` method will be executed.
## Keys used in the decman store
- `flatpaks_for_module`
- `user_flatpaks_for_module`
## Configuration ## Configuration
It's possible to override the commands this plugin uses. Create your own `FlatpakCommands` class and override methods returning commands. These are the defaults. It's possible to override the commands this plugin uses. Create your own `FlatpakCommands` class and override methods returning commands. These are the defaults.
+1 -3
View File
@@ -232,8 +232,6 @@ class MyModule(Module):
#### New #### New
`decman.modules` is now a set instead of a list. If you wish to have multiple instances of the same module class, just name them differently. Name needs to be unique accross modules.
Modules no longer have `version`s or `enabled` values. A module is enabled when it gets added to `decman.modules` and disabled when it gets removed from `decman.modules`. Versions are no longer needed because `after_version_change` has been removed and `on_change` has been added. `on_change` is executed automatically after the content of the module changes. `on_disable` will be executed automatically when the module is removed from `decman.modules`. It is no longer a instance method. Instead it must be a self-contained method with no references outside it. Not even imports. Modules no longer have `version`s or `enabled` values. A module is enabled when it gets added to `decman.modules` and disabled when it gets removed from `decman.modules`. Versions are no longer needed because `after_version_change` has been removed and `on_change` has been added. `on_change` is executed automatically after the content of the module changes. `on_disable` will be executed automatically when the module is removed from `decman.modules`. It is no longer a instance method. Instead it must be a self-contained method with no references outside it. Not even imports.
Module methods will get a `Store` instance passed to them as an argument. It can be used to store key-value pairs between decman runs. Module methods will get a `Store` instance passed to them as an argument. It can be used to store key-value pairs between decman runs.
@@ -245,7 +243,7 @@ import decman
from decman import Module, Store, prg, sh from decman import Module, Store, prg, sh
from decman.plugins import pacman, aur, systemd, flatpak from decman.plugins import pacman, aur, systemd, flatpak
decman.modules |= {MyModule()} decman.modules += [MyModule()]
class MyModule(Module): class MyModule(Module):
def __init__(self): def __init__(self):
+4
View File
@@ -42,6 +42,10 @@ class MyModule(decman.Module):
If this set changes, this plugin will flag the module as changed. The module's `on_change` method will be executed. If this set changes, this plugin will flag the module as changed. The module's `on_change` method will be executed.
## Keys used in the decman store
- `packages_for_module`
## Configuration ## Configuration
This plugin has a pacman output highlight function. If pacman output contains some keywords, it will be highlighted. You can disable this feature or set the keywords. This plugin has a pacman output highlight function. If pacman output contains some keywords, it will be highlighted. You can disable this feature or set the keywords.
+5
View File
@@ -45,6 +45,11 @@ class MyModule(decman.Module):
If units or user units change, this plugin will flag the module as changed. The module's `on_change` method will be executed. If units or user units change, this plugin will flag the module as changed. The module's `on_change` method will be executed.
## Keys used in the decman store
- `systemd_units_for_module`
- `systemd_user_units_for_module`
## Configuration ## Configuration
It's possible to override the commands this plugin uses. Create your own `SystemdCommands` class and override methods returning commands. These are the defaults. It's possible to override the commands this plugin uses. Create your own `SystemdCommands` class and override methods returning commands. These are the defaults.
+2 -2
View File
@@ -85,7 +85,7 @@ import decman
from base import BaseModule from base import BaseModule
decman.pacman.packages |= {"openssh", "qemu-guest-agent", "sudo", "vim"} decman.pacman.packages |= {"openssh", "qemu-guest-agent", "sudo", "vim"}
decman.modules |= {BaseModule()} decman.modules += [BaseModule()]
``` ```
This config is already enough to run decman for the first time. This config is already enough to run decman for the first time.
@@ -162,7 +162,7 @@ from kde import KDE
... ...
decman.modules |= {BaseModule(), KDE()} decman.modules += [BaseModule(), KDE()]
``` ```
I'll run decman once again. I'll also start SDDM manually, since decman can't autostart it. I'll run decman once again. I'll also start SDDM manually, since decman can't autostart it.
+1 -1
View File
@@ -9,7 +9,7 @@ class Example(decman.Plugin):
def available(self) -> bool: def available(self) -> bool:
return os.path.exists("/tmp/example_plugin_available") return os.path.exists("/tmp/example_plugin_available")
def process_modules(self, store: decman.Store, modules: set[decman.Module]): def process_modules(self, store: decman.Store, modules: list[decman.Module]):
# Toy example for setting modules as changed # Toy example for setting modules as changed
for module in modules: for module in modules:
module._changed = True module._changed = True
+1 -1
View File
@@ -5,7 +5,7 @@ import decman
decman.pacman.packages |= {"openssh", "qemu-guest-agent", "sudo", "vim"} decman.pacman.packages |= {"openssh", "qemu-guest-agent", "sudo", "vim"}
decman.modules |= {BaseModule(), KDE()} decman.modules += [BaseModule(), KDE()]
decman.files["/home/arch/.vimrc"] = decman.File( decman.files["/home/arch/.vimrc"] = decman.File(
source_file="./files/vimrc", owner="arch", permissions=0o600 source_file="./files/vimrc", owner="arch", permissions=0o600
@@ -46,7 +46,7 @@ class Flatpak(plugins.Plugin):
def available(self) -> bool: def available(self) -> bool:
return shutil.which("flatpak") is not None return shutil.which("flatpak") is not None
def process_modules(self, store: _store.Store, modules: set[module.Module]): def process_modules(self, store: _store.Store, modules: list[module.Module]):
# These store keys are used to track changes in modules. # These store keys are used to track changes in modules.
# This way when these change, module can be marked as changed # This way when these change, module can be marked as changed
store.ensure("flatpaks_for_module", {}) store.ensure("flatpaks_for_module", {})
@@ -90,7 +90,7 @@ class AUR(plugins.Plugin):
and shutil.which("mkarchroot") is not None and shutil.which("mkarchroot") is not None
) )
def process_modules(self, store: _store.Store, modules: set[module.Module]): def process_modules(self, store: _store.Store, modules: list[module.Module]):
# This is used to track changes in modules. # This is used to track changes in modules.
store.ensure("aur_packages_for_module", {}) store.ensure("aur_packages_for_module", {})
store.ensure("custom_packages_for_module", {}) store.ensure("custom_packages_for_module", {})
@@ -58,7 +58,7 @@ class Pacman(plugins.Plugin):
def available(self) -> bool: def available(self) -> bool:
return shutil.which("pacman") is not None return shutil.which("pacman") is not None
def process_modules(self, store: _store.Store, modules: set[module.Module]): def process_modules(self, store: _store.Store, modules: list[module.Module]):
# This is used to track changes in modules. # This is used to track changes in modules.
store.ensure("packages_for_module", {}) store.ensure("packages_for_module", {})
@@ -83,7 +83,7 @@ class Systemd(plugins.Plugin):
def available(self) -> bool: def available(self) -> bool:
return shutil.which("systemctl") is not None return shutil.which("systemctl") is not None
def process_modules(self, store: _store.Store, modules: set[module.Module]): def process_modules(self, store: _store.Store, modules: list[module.Module]):
# These store keys are used to track changes in modules. # These store keys are used to track changes in modules.
# This way when these change, module can be marked as changed # This way when these change, module can be marked as changed
store.ensure("systemd_units_for_module", {}) store.ensure("systemd_units_for_module", {})
+1 -1
View File
@@ -63,7 +63,7 @@ __all__ = [
# ----------------------------------------- # -----------------------------------------
files: dict[str, File] = {} files: dict[str, File] = {}
directories: dict[str, Directory] = {} directories: dict[str, Directory] = {}
modules: set[Module] = set() modules: list[Module] = []
execution_order: list[str] = [ execution_order: list[str] = [
"files", "files",
"pacman", "pacman",
+1 -1
View File
@@ -10,7 +10,7 @@ import decman.core.store as _store
def update_files( def update_files(
store: _store.Store, store: _store.Store,
modules: set[module.Module], modules: list[module.Module],
files: dict[str, fs.File], files: dict[str, fs.File],
directories: dict[str, fs.Directory], directories: dict[str, fs.Directory],
dry_run: bool = False, dry_run: bool = False,
+10 -1
View File
@@ -74,5 +74,14 @@ class _SetJSONEncoder(json.JSONEncoder):
def _decode_sets(obj: typing.Any) -> typing.Any: def _decode_sets(obj: typing.Any) -> typing.Any:
if isinstance(obj, dict) and obj.get("__type__") == "set" and "items" in obj: if isinstance(obj, dict) and obj.get("__type__") == "set" and "items" in obj:
return set(obj["items"]) # decode lists inside sets as tuples
norm = []
for item in obj["items"]:
if isinstance(item, list):
norm.append(tuple(item))
else:
norm.append(item)
return set(norm)
return obj return obj
+1 -1
View File
@@ -40,7 +40,7 @@ class Plugin:
""" """
return True return True
def process_modules(self, store: _store.Store, modules: set[module.Module]): def process_modules(self, store: _store.Store, modules: list[module.Module]):
""" """
Processes a module. Processes a module.
""" """