From 401233a352c04aae0a9aa0c0ca0a6d89a09c5af4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1vid=20Groniewsky?= Date: Mon, 29 Sep 2025 14:57:34 +0200 Subject: [PATCH 01/13] Added support for declaring installed and ignored flatpaks in the source file. For this the variables 'flatpak_packages' and 'ignored_flatpak_packages' are used. These work like the pacman equivalents. There were test cases appended for flatpak testing. I have installed the modified version on my system and tested it extensively. No errors were found. --- dist/decman-0.3.4-py3-none-any.whl | Bin 0 -> 50426 bytes src/decman/__init__.py | 2 + src/decman/app.py | 69 +++++++++++++---- src/decman/config.py | 31 ++++++++ src/decman/lib/__init__.py | 115 +++++++++++++++++++++++++++++ tests/test_source_resolution.py | 77 ++++++++----------- 6 files changed, 235 insertions(+), 59 deletions(-) create mode 100644 dist/decman-0.3.4-py3-none-any.whl diff --git a/dist/decman-0.3.4-py3-none-any.whl b/dist/decman-0.3.4-py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..ac2a02232ab1e27ff99184ff1ec9fb35e2a264cf GIT binary patch literal 50426 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 &Mqwl-oZTQD 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%0DC4tV)(l;Vbe%Q@~ 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 literal 0 HcmV?d00001 diff --git a/src/decman/__init__.py b/src/decman/__init__.py index 99ba5dd..c1c341d 100644 --- a/src/decman/__init__.py +++ b/src/decman/__init__.py @@ -412,3 +412,5 @@ enabled_systemd_user_units: dict[str, list[str]] = {} files: dict[str, File] = {} directories: dict[str, Directory] = {} modules: list[Module] = [] +flatpak_packages: list[str] = [] +ignored_flatpak_packages: list[str] = [] diff --git a/src/decman/app.py b/src/decman/app.py index 053c51d..04854c1 100644 --- a/src/decman/app.py +++ b/src/decman/app.py @@ -205,6 +205,7 @@ class Core: self.store = store self.source = _resolve_source() self.pacman = l.Pacman() + self.flatpak = l.Flatpak() self.systemctl = l.Systemd(store) self.fpkg_search = fpm.ExtendedPackageSearch(self.pacman) @@ -257,36 +258,76 @@ class Core: self.systemctl.disable_user_units(units, user) def _remove_pkgs(self): + """ + Remove pacman and flatpak packages + """ + # pacman currently_installed = self.pacman.get_installed() to_remove = self.source.packages_to_remove(currently_installed) - l.print_list("Removing packages:", to_remove) - if not self.only_print: - self.pacman.remove(to_remove) + + currently_installed_flatpak = self.flatpak.get_installed() + to_remove_flatpak = self.source.flatpak_packages_to_remove( + currently_installed_flatpak + ) + + l.print_list("Removing pacman packages:", to_remove) + l.print_list("Removing flatpak packages:", to_remove_flatpak) + + if self.only_print: + return + + self.pacman.remove(to_remove) + + # flatpak + self.flatpak.remove(to_remove_flatpak) def _upgrade_pkgs(self): + """ + Upgrade pacman, fpm and flatpak packages + """ + # flatpak + fpm l.print_summary("Upgrading packages.") - if not self.only_print: - self.pacman.upgrade() - if conf.enable_fpm and self.update_foreign_packages: - self.fpm.upgrade( - self.upgrade_devel, self.force_build, self.source.ignored_packages - ) + if self.only_print: + return + + self.pacman.upgrade() + if conf.enable_fpm and self.update_foreign_packages: + self.fpm.upgrade( + self.upgrade_devel, self.force_build, self.source.ignored_packages + ) + + # flatpak + self.flatpak.upgrade() def _install_pkgs(self): + """ + Installs all pacman, fpm, and flatpak packages. + """ + + # pacman + fpm currently_installed = self.pacman.get_installed() to_install_pacman = self.source.pacman_packages_to_install(currently_installed) to_install_fpm = self.source.foreign_packages_to_install(currently_installed) + # flatpak + currently_installed_flatpak = self.flatpak.get_installed() + to_install_flatpak = self.source.flatpak_packages_to_install( + currently_installed_flatpak + ) + l.print_list("Installing pacman packages:", to_install_pacman) + l.print_list("Installing flatpak packages:", to_install_flatpak) # fpm prints a summary so no need to print it twice if self.only_print: l.print_list("Installing foreign packages:", to_install_fpm) + return - if not self.only_print: - self.pacman.install(to_install_pacman) - if conf.enable_fpm and self.update_foreign_packages: - self.fpm.install(to_install_fpm, force=self.force_build) + self.pacman.install(to_install_pacman) + if conf.enable_fpm and self.update_foreign_packages: + self.fpm.install(to_install_fpm, force=self.force_build) + + self.flatpak.install(to_install_flatpak) def _create_and_remove_files(self): l.print_summary("Installing files.") @@ -356,6 +397,8 @@ def _resolve_source() -> l.Source: files=decman.files, directories=decman.directories, modules=set(decman.modules), + flatpak_packages=set(decman.flatpak_packages), + ignored_flatpak_packages=set(decman.ignored_flatpak_packages), ) diff --git a/src/decman/config.py b/src/decman/config.py index 4c1c794..bb9686f 100644 --- a/src/decman/config.py +++ b/src/decman/config.py @@ -34,6 +34,13 @@ class Commands: """ return ["pacman", "-Qeq", "--color=never"] + def list_flatpak_pkgs(self) -> list[str]: + """ + Running this command outputs a newline separated list of installed flatpak application ids + The first line just says 'Application ID' so this one is ignored. + """ + return ["flatpak", "list", "--app", "--columns", "application"] + def list_foreign_pkgs_versioned(self) -> list[str]: """ Running this command outputs a newline seperated list of installed packages and their @@ -47,6 +54,12 @@ class Commands: """ return ["pacman", "-S", "--color=always", "--needed"] + pkgs + def install_flatpak_pkgs(self, pkgs: list[str]) -> list[str]: + """ + Running this command installs all listed packages, and their dependencies/runtimes automatically. + """ + return ["flatpak", "install"] + pkgs + def install_files(self, pkg_files: list[str]) -> list[str]: """ Running this command installs the given packages files. @@ -78,6 +91,12 @@ class Commands: """ return ["pacman", "-Syu", "--color=always"] + def upgrade_flatpak(self) -> list[str]: + """ + Updates all installed flatpak REFs including runtimes and dependencies. + """ + return ["flatpak", "update"] + def remove(self, pkgs: list[str]) -> list[str]: """ Running this command removes the given packages and their dependencies @@ -85,6 +104,18 @@ class Commands: """ return ["pacman", "-Rs", "--color=always"] + pkgs + def remove_flatpak(self, pkgs: list[str]) -> list[str]: + """ + Running this command will remove the listed REFs. Unused dependencies might be kept, but to remove them another command needs to be run. + """ + return ["flatpak", "remove"] + pkgs + + def remove_unused_flatpak(self) -> list[str]: + """ + This will remove all unused flatpak dependencies and runtimes. + """ + return ["flatpak", "remove", "--unused"] + def enable_units(self, units: list[str]) -> list[str]: """ Running this command enables the given systemd units. diff --git a/src/decman/lib/__init__.py b/src/decman/lib/__init__.py index 46d711e..7719764 100644 --- a/src/decman/lib/__init__.py +++ b/src/decman/lib/__init__.py @@ -419,6 +419,8 @@ class Source: files: dict[str, decman.File], directories: dict[str, decman.Directory], modules: set[decman.Module], + flatpak_packages: set[str], + ignored_flatpak_packages: set[str], ): self.pacman_packages = pacman_packages self.aur_packages = aur_packages @@ -429,6 +431,8 @@ class Source: self.files = files self.directories = directories self.modules = modules + self.flatpak_packages = flatpak_packages + self.ignored_flatpak_packages = ignored_flatpak_packages def run_on_enable(self, store: Store): """ @@ -639,6 +643,36 @@ class Source: result.append(pkg) return result + def flatpak_packages_to_install( + self, currently_installed_packages: list[str] + ) -> list[str]: + """ + Returns all flatpak packages, that are not installed or ignored + """ + + result: list[str] = [] + for pkg in self.flatpak_packages: + if pkg in self.ignored_flatpak_packages: + continue + if pkg not in currently_installed_packages: + result.append(pkg) + return result + + def flatpak_packages_to_remove( + self, currently_installed_packages: list[str] + ) -> list[str]: + """ + This returns a list of flatpak app ids, that need to be removed since they are installed but not found in either the list of ignored packages or the list of flatpak packages that need to be installed. + """ + result: list[str] = [] + for package in currently_installed_packages: + if package in self.ignored_flatpak_packages: + continue + if package not in self.flatpak_packages: + result.append(package) + + return result + def all_enabled_modules(self) -> list[tuple[str, str]]: """ Returns all enabled modules and their versions. @@ -922,6 +956,87 @@ def echo_and_capture_command(program: list[str]) -> tuple[int, str]: return (returncode, output) +class Flatpak: + def __init__(self) -> None: + pass + + def get_installed(self) -> list[str]: + """ + Return all of the installed applications. Dependencies and runtimes are exluded since they will not be explicitly installed and thus flatpak will manage them. + """ + try: + packages = ( + subprocess.run( + conf.commands.list_flatpak_pkgs(), + check=True, + stdout=subprocess.PIPE, + ) + .stdout.decode() + .strip() + .split("\n") + ) + + # The header might be included. It might also not. This will make sure that it is not present. + if "Application ID" in packages: + packages.remove("Application ID") + + return packages + except subprocess.CalledProcessError as error: + raise err.UserFacingError( + user_facing_msg=f"Failed to get installed flatpak packages using '{error.cmd}'. Output: {error.stdout}." + ) from error + + def install(self, packages: list[str]): + """ + Install the listed flatpak packages. + """ + if not packages: + return + + returncode, _output = echo_and_capture_command( + conf.commands.install_flatpak_pkgs(packages) + ) + if returncode != 0: + raise err.UserFacingError( + f"Failed to install flatpak packages. Process exited with code {returncode}." + ) + + def upgrade(self) -> None: + """ + Upgrade all flatpak packages. + """ + returncode, _output = echo_and_capture_command(conf.commands.upgrade_flatpak()) + if not returncode == 0: + raise err.UserFacingError( + f"Failed to upgrade flatpak packages. Process exited with code {returncode}." + ) + + def remove(self, packages: list[str]): + """ + Remove all the listed packages and their unused dependecies. This has to happen in two steps. + """ + if not packages: + return + + returncode, _output = echo_and_capture_command( + conf.commands.remove_flatpak(packages) + ) + + if not returncode == 0: + raise err.UserFacingError( + f"Failed to remove flatpak packages. Process exited with code {returncode}." + ) + + returncode, _output = echo_and_capture_command( + conf.commands.remove_unused_flatpak() + ) + + if not returncode == 0: + raise err.UserFacingError( + f"Failed to remove unused flatpak packages. Process exited with code {returncode}." + ) + + class Systemd: """ Interface for interacting with systemd. diff --git a/tests/test_source_resolution.py b/tests/test_source_resolution.py index c4c887c..8b98577 100644 --- a/tests/test_source_resolution.py +++ b/tests/test_source_resolution.py @@ -6,7 +6,6 @@ from decman import UserPackage, Module class ExistingTestModule(Module): - def __init__(self): self.on_enable_executed = False self.on_disable_executed = False @@ -28,7 +27,6 @@ class ExistingTestModule(Module): class ExistingChangedVersionTestModule(Module): - def __init__(self): self.on_enable_executed = False self.on_disable_executed = False @@ -50,7 +48,6 @@ class ExistingChangedVersionTestModule(Module): class EnabledTestModule(Module): - def __init__(self): self.on_enable_executed = False self.on_disable_executed = False @@ -78,7 +75,6 @@ class EnabledTestModule(Module): class DisabledTestModule(Module): - def __init__(self): self.on_enable_executed = False self.on_disable_executed = False @@ -106,7 +102,6 @@ class DisabledTestModule(Module): class TestSource(unittest.TestCase): - def setUp(self): self.disabled_module = DisabledTestModule() self.enabled_module = EnabledTestModule() @@ -133,7 +128,7 @@ class TestSource(unittest.TestCase): version="1", dependencies=["d2"], git_url="/am/url/yes", - ) + ), }, ignored_packages={"i1", "i2"}, systemd_units={"1.service", "2.timer"}, @@ -141,11 +136,12 @@ class TestSource(unittest.TestCase): modules=modules, files={}, directories={}, + flatpak_packages={"f1", "f2", "f3"}, + ignored_flatpak_packages={"i1", "i2"}, ) store = Store() - store.enabled_systemd_units.extend( - ["1.service", "3.service", "M_1.service"]) + store.enabled_systemd_units.extend(["1.service", "3.service", "M_1.service"]) store.add_enabled_user_systemd_unit("user", "u1.service") store.add_enabled_user_systemd_unit("user", "u3.service") store.enabled_modules = { @@ -174,16 +170,19 @@ class TestSource(unittest.TestCase): self.currently_installed_packages = currently_installed_packages def test_all_enabled_modules(self): - enabled_modules = [("Enabled", "1"), ("Existing", "1"), - ("ExistingChanged", "2")] - self.assertCountEqual(self.source.all_enabled_modules(), - enabled_modules) + enabled_modules = [ + ("Enabled", "1"), + ("Existing", "1"), + ("ExistingChanged", "2"), + ] + self.assertCountEqual(self.source.all_enabled_modules(), enabled_modules) def test_files_to_remove(self): created_files = ["/test/file1", "/test/file4"] self.assertCountEqual( self.source.files_to_remove(self.store, created_files), - ["/test/file2", "/test/file3"]) + ["/test/file2", "/test/file3"], + ) def test_after_update_executed(self): self.source.run_after_update() @@ -197,8 +196,7 @@ class TestSource(unittest.TestCase): 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.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) @@ -233,10 +231,7 @@ class TestSource(unittest.TestCase): def test_user_units_to_enable(self): self.assertDictEqual( self.source.user_units_to_enable(self.store), - { - "user": ["u2.timer"], - "muser": ["M_u1.service"] - }, + {"user": ["u2.timer"], "muser": ["M_u1.service"]}, ) def test_user_units_to_disable(self): @@ -247,15 +242,13 @@ class TestSource(unittest.TestCase): def test_pacman_packages_to_install(self): self.assertCountEqual( - self.source.pacman_packages_to_install( - self.currently_installed_packages), + self.source.pacman_packages_to_install(self.currently_installed_packages), ["p3", "M_p1", "M_p2"], ) def test_foreign_packages_to_install(self): self.assertCountEqual( - self.source.foreign_packages_to_install( - self.currently_installed_packages), + self.source.foreign_packages_to_install(self.currently_installed_packages), ["A1", "U2"], ) @@ -265,46 +258,38 @@ class TestSource(unittest.TestCase): ["p4", "A4", "M_A1", "M_A2"], ) + class TestModuleUserServices(unittest.TestCase): - class ModuleWithUserServiceOne(Module): - def __init__(self): super().__init__("one", True, "0") def systemd_user_units(self) -> dict[str, list[str]]: - return { - "user": ['foo.service'] - } + return {"user": ["foo.service"]} class ModuleWithUserServiceTwo(Module): - def __init__(self): super().__init__("two", True, "0") def systemd_user_units(self) -> dict[str, list[str]]: - return { - "user": ['bar.service'] - } + return {"user": ["bar.service"]} def setUp(self) -> None: self.source = Source( - pacman_packages=set(), - aur_packages=set(), - user_packages=set(), - ignored_packages=set(), - systemd_units=set(), - systemd_user_units={}, - files={}, - directories={}, - modules={ - self.ModuleWithUserServiceOne(), - self.ModuleWithUserServiceTwo() - }, - ) + pacman_packages=set(), + aur_packages=set(), + user_packages=set(), + ignored_packages=set(), + systemd_units=set(), + systemd_user_units={}, + files={}, + directories={}, + modules={self.ModuleWithUserServiceOne(), self.ModuleWithUserServiceTwo()}, + flatpak_packages=set(), + ignored_flatpak_packages=set(), + ) self.store = Store() - def test_user_units_to_enable(self): self.assertDictEqual( self.source.user_units_to_enable(self.store), From 27a8c279c4b2f0d9ba3426caf498a3a7de5d1061 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1vid=20Groniewsky?= Date: Mon, 29 Sep 2025 14:58:21 +0200 Subject: [PATCH 02/13] Removed the build and dist directories --- dist/decman-0.3.4-py3-none-any.whl | Bin 50426 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 dist/decman-0.3.4-py3-none-any.whl diff --git a/dist/decman-0.3.4-py3-none-any.whl b/dist/decman-0.3.4-py3-none-any.whl deleted file mode 100644 index ac2a02232ab1e27ff99184ff1ec9fb35e2a264cf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 50426 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 &Mqwl-oZTQD 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%0DC4tV)(l;Vbe%Q@~ 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 From 0ecd8350904b03465cd312c7b9aaa6d53d53052c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1vid=20Groniewsky?= Date: Mon, 29 Sep 2025 15:19:42 +0200 Subject: [PATCH 03/13] Extended the flatpak functionality to modules. All works as expected. --- src/decman/__init__.py | 7 +++++++ src/decman/lib/__init__.py | 13 +++++++++++-- tests/test_source_resolution.py | 4 ++++ 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/decman/__init__.py b/src/decman/__init__.py index c1c341d..1561e24 100644 --- a/src/decman/__init__.py +++ b/src/decman/__init__.py @@ -380,6 +380,13 @@ class Module: """ return [] + def flatpak_packages(self) -> list[str]: + """ + Override this method to return flatpak 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 diff --git a/src/decman/lib/__init__.py b/src/decman/lib/__init__.py index 7719764..f277a8e 100644 --- a/src/decman/lib/__init__.py +++ b/src/decman/lib/__init__.py @@ -651,7 +651,7 @@ class Source: """ result: list[str] = [] - for pkg in self.flatpak_packages: + for pkg in self._all_flatpak_packages(): if pkg in self.ignored_flatpak_packages: continue if pkg not in currently_installed_packages: @@ -668,7 +668,7 @@ class Source: for package in currently_installed_packages: if package in self.ignored_flatpak_packages: continue - if package not in self.flatpak_packages: + if package not in self._all_flatpak_packages(): result.append(package) return result @@ -702,6 +702,15 @@ class Source: result.update(module.pacman_packages()) return result + def _all_flatpak_packages(self) -> set[str]: + result = set() + result.update(self.flatpak_packages) + for module in self.modules: + if module.enabled: + result.update(module.flatpak_packages()) + + return result + def _all_foreign_pkgs(self) -> set[str]: result = set() result.update(self.aur_packages) diff --git a/tests/test_source_resolution.py b/tests/test_source_resolution.py index 8b98577..2d9275b 100644 --- a/tests/test_source_resolution.py +++ b/tests/test_source_resolution.py @@ -1,5 +1,6 @@ # pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +from typing import override import unittest from decman.lib import Source, Store from decman import UserPackage, Module @@ -73,6 +74,9 @@ class EnabledTestModule(Module): def systemd_user_units(self) -> dict[str, list[str]]: return {"muser": ["M_u1.service"]} + def flatpak_packages(self) -> list[str]: + return ["M_f1", "M_f2"] + class DisabledTestModule(Module): def __init__(self): From 9ce42ccf3e25c2b36f6281583ad8991f2f260275 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1vid=20Groniewsky?= Date: Tue, 30 Sep 2025 22:30:24 +0200 Subject: [PATCH 04/13] Added the config option to disable flatpaks (disabled by default), and the flag --no-flatpaks which will skip flatpak packages. --- src/decman/app.py | 19 ++++++++++++++++--- src/decman/config.py | 1 + 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/decman/app.py b/src/decman/app.py index 04854c1..787b9e0 100644 --- a/src/decman/app.py +++ b/src/decman/app.py @@ -1,3 +1,4 @@ +# pyright: reportUnusedCallResult=false """ Module containing the CLI Application. """ @@ -27,6 +28,7 @@ def main(): epilog="See more help at: https://github.com/kiviktnm/decman", ) + parser.add_argument( "--source", action="store", help="python file containing configuration" ) @@ -52,6 +54,12 @@ def main(): default=False, help="don't upgrade foreign packages", ) + parser.add_argument( + "--no-flatpaks", + action="store_true", + default=False, + help="don't upgrade flatpak packages", + ) parser.add_argument( "--no-files", action="store_true", default=False, help="don't install any files" ) @@ -177,6 +185,7 @@ def _set_up(store: l.Store, args): args.print, not args.no_packages, not args.no_foreign_packages, + not args.no_flatpaks not args.no_files, not args.no_systemd_units, not args.no_commands, @@ -195,6 +204,7 @@ class Core: self.only_print, self.update_packages, self.update_foreign_packages, + self.update_flatpaks, self.update_files, self.update_units, self.run_commands, @@ -279,7 +289,8 @@ class Core: self.pacman.remove(to_remove) # flatpak - self.flatpak.remove(to_remove_flatpak) + if conf.enable_flatpak and self.update_flatpaks: + self.flatpak.remove(to_remove_flatpak) def _upgrade_pkgs(self): """ @@ -297,7 +308,8 @@ class Core: ) # flatpak - self.flatpak.upgrade() + if conf.enable_flatpak and self.update_flatpaks: + self.flatpak.upgrade() def _install_pkgs(self): """ @@ -327,7 +339,8 @@ class Core: if conf.enable_fpm and self.update_foreign_packages: self.fpm.install(to_install_fpm, force=self.force_build) - self.flatpak.install(to_install_flatpak) + if conf.enable_flatpak and self.update_flatpaks: + self.flatpak.install(to_install_flatpak) def _create_and_remove_files(self): l.print_summary("Installing files.") diff --git a/src/decman/config.py b/src/decman/config.py index bb9686f..eda3cee 100644 --- a/src/decman/config.py +++ b/src/decman/config.py @@ -265,4 +265,5 @@ build_dir: str = "/tmp/decman/build" pkg_cache_dir: str = "/var/cache/decman" aur_rpc_timeout: typing.Optional[int] = 30 enable_fpm: bool = True +enable_flatpak: bool = False number_of_packages_stored_in_cache: int = 3 From dbeed0b7d814a7e245e42086f3c47c31c2db55d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1vid=20Groniewsky?= Date: Tue, 30 Sep 2025 22:40:19 +0200 Subject: [PATCH 05/13] Updated the README with the announcement that flatpaks can now be managed. Added the required documentation to the example. --- README.md | 3 +++ example/my_module.py | 4 ++++ example/source.py | 9 +++++++++ 3 files changed, 16 insertions(+) diff --git a/README.md b/README.md index cb0a932..39e945b 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,8 @@ # Decman +> 🎉 Support for Flatpaks was just added! 🎉 +> By default flatpak management this feature is disabled. Take a look at the example to learn how to use flatpaks. + > 🎉 Decman now has a AUR package! 🎉 > > To start using the AUR package simply add `decman` to `decman.aur_packages`. To ensure a smooth change, add decman-git to ignored_packages during the conversion. diff --git a/example/my_module.py b/example/my_module.py index f99a453..193a34c 100644 --- a/example/my_module.py +++ b/example/my_module.py @@ -1,5 +1,6 @@ # from import is ok for importing classes and functions # just remember to not import variables this way +from typing import override from decman import Module, File, Directory, UserPackage, sh, prg @@ -112,6 +113,9 @@ class MyModule(Module): def aur_packages(self) -> list[str]: return ["protonvpn"] + def flatpak_packages(self) -> list[str]: + return ["org.mozilla.firefox"] + def systemd_units(self) -> list[str]: return ["reflector.timer"] diff --git a/example/source.py b/example/source.py index ecae193..0573298 100644 --- a/example/source.py +++ b/example/source.py @@ -12,6 +12,9 @@ import decman.config # This is fine since the thing being imported is a class and not a global variable. from decman import UserPackage, File, Directory, UserRaisedError +# Flatpaks are disabled by default. This way no already installed flatpaks will get suddenly deleted. +decman.config.enable_flatpak = True + # Configuring what packages are installed is easy. # Duplicates are OK, so if you have multiple modules that want to ensure a package is installed, # you can add the same package multiple times. @@ -28,6 +31,12 @@ decman.ignored_packages += ["rustup", "yay"] # Installing AUR packages is easy. decman.aur_packages += ["decman", "protonvpn"] +# Flatpaks work the same way as all other packages. +decman.flatpak_packages += ["dev.qwery.AddWater"] + +# They too can be ignored of course +decman.ignored_flatpak_packages += ["org.signal.Signal"] + # To import GPG keys, set the GNUPGHOME environment variable. # It can easily be done with python as well. os.environ["GNUPGHOME"] = "/home/kk/.gnupg/" From 515a27cb7d2add3f564bb5f8181f697563964c0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1vid=20Groniewsky?= Date: Tue, 30 Sep 2025 22:45:54 +0200 Subject: [PATCH 06/13] Added the check for missing flatpak installation while the flatpaks are enabled. An error will be printed and decman will exit. --- src/decman/app.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/decman/app.py b/src/decman/app.py index 787b9e0..3f0ebc6 100644 --- a/src/decman/app.py +++ b/src/decman/app.py @@ -5,6 +5,7 @@ Module containing the CLI Application. import argparse import os +import shutil import sys import traceback @@ -212,6 +213,10 @@ class Core: self.force_build, ) = opts + if conf.enable_flatpak and not shutil.which("flatpak"): + l.print_error("Flatpaks have been enabled in the source file, but the flatpak command could not be found. Either disable flatpaks or make sure that flatpak is installed and can be accessed by decman. Exiting.") + exit() + self.store = store self.source = _resolve_source() self.pacman = l.Pacman() From 613beaa917135ea067030d03a6905d948a324fa4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1vid=20Groniewsky?= Date: Wed, 1 Oct 2025 11:15:26 +0200 Subject: [PATCH 07/13] Added the option for installing flatpaks to the user installation. --- src/decman/__init__.py | 10 ++++++++- src/decman/app.py | 20 +++++++++++++++--- src/decman/config.py | 27 +++++++++++++++--------- src/decman/lib/__init__.py | 37 ++++++++++++++++++++------------- tests/test_source_resolution.py | 2 ++ 5 files changed, 68 insertions(+), 28 deletions(-) diff --git a/src/decman/__init__.py b/src/decman/__init__.py index 1561e24..578c9e8 100644 --- a/src/decman/__init__.py +++ b/src/decman/__init__.py @@ -382,7 +382,14 @@ class Module: def flatpak_packages(self) -> list[str]: """ - Override this method to return flatpak packages that should be installed as a part of this + Override this method to return flatpak packages that should be installed to the system installation as a part of this + Module. + """ + return [] + + def flatpak_user_packages(self) -> list[str]: + """ + Override this method to return flatpak packages that should be installed to the user installation as a part of this Module. """ return [] @@ -420,4 +427,5 @@ files: dict[str, File] = {} directories: dict[str, Directory] = {} modules: list[Module] = [] flatpak_packages: list[str] = [] +flatpak_user_packages: list[str] = [] ignored_flatpak_packages: list[str] = [] diff --git a/src/decman/app.py b/src/decman/app.py index 3f0ebc6..24ea995 100644 --- a/src/decman/app.py +++ b/src/decman/app.py @@ -29,7 +29,6 @@ def main(): epilog="See more help at: https://github.com/kiviktnm/decman", ) - parser.add_argument( "--source", action="store", help="python file containing configuration" ) @@ -186,7 +185,7 @@ def _set_up(store: l.Store, args): args.print, not args.no_packages, not args.no_foreign_packages, - not args.no_flatpaks + not args.no_flatpaks, not args.no_files, not args.no_systemd_units, not args.no_commands, @@ -214,7 +213,9 @@ class Core: ) = opts if conf.enable_flatpak and not shutil.which("flatpak"): - l.print_error("Flatpaks have been enabled in the source file, but the flatpak command could not be found. Either disable flatpaks or make sure that flatpak is installed and can be accessed by decman. Exiting.") + l.print_error( + "Flatpaks have been enabled in the source file, but the flatpak command could not be found. Either disable flatpaks or make sure that flatpak is installed and can be accessed by decman. Exiting." + ) exit() self.store = store @@ -284,9 +285,14 @@ class Core: to_remove_flatpak = self.source.flatpak_packages_to_remove( currently_installed_flatpak ) + currently_installed_user_flatpak = self.flatpak.get_installed(True) + to_remove_user_flatpak = self.source.flatpak_packages_to_remove( + currently_installed_user_flatpak, as_user=True + ) l.print_list("Removing pacman packages:", to_remove) l.print_list("Removing flatpak packages:", to_remove_flatpak) + l.print_list("Removing user flatpak packages:", to_remove_user_flatpak) if self.only_print: return @@ -296,6 +302,7 @@ class Core: # flatpak if conf.enable_flatpak and self.update_flatpaks: self.flatpak.remove(to_remove_flatpak) + self.flatpak.remove(to_remove_user_flatpak, True) def _upgrade_pkgs(self): """ @@ -331,9 +338,14 @@ class Core: to_install_flatpak = self.source.flatpak_packages_to_install( currently_installed_flatpak ) + currently_installed_user_flatpak = self.flatpak.get_installed(True) + to_install_user_flatpak = self.source.flatpak_packages_to_install( + currently_installed_user_flatpak, True + ) l.print_list("Installing pacman packages:", to_install_pacman) l.print_list("Installing flatpak packages:", to_install_flatpak) + l.print_list("Installing user flatpak packages:", to_install_user_flatpak) # fpm prints a summary so no need to print it twice if self.only_print: @@ -346,6 +358,7 @@ class Core: if conf.enable_flatpak and self.update_flatpaks: self.flatpak.install(to_install_flatpak) + self.flatpak.install(to_install_user_flatpak, True) def _create_and_remove_files(self): l.print_summary("Installing files.") @@ -416,6 +429,7 @@ def _resolve_source() -> l.Source: directories=decman.directories, modules=set(decman.modules), flatpak_packages=set(decman.flatpak_packages), + flatpak_user_packages=set(decman.flatpak_user_packages), ignored_flatpak_packages=set(decman.ignored_flatpak_packages), ) diff --git a/src/decman/config.py b/src/decman/config.py index eda3cee..5bd2ca8 100644 --- a/src/decman/config.py +++ b/src/decman/config.py @@ -34,12 +34,19 @@ class Commands: """ return ["pacman", "-Qeq", "--color=never"] - def list_flatpak_pkgs(self) -> list[str]: + def list_flatpak_pkgs(self, as_user: bool = False) -> list[str]: """ Running this command outputs a newline separated list of installed flatpak application ids The first line just says 'Application ID' so this one is ignored. """ - return ["flatpak", "list", "--app", "--columns", "application"] + return [ + "flatpak", + "list", + "--app", + "--user" if as_user else "--system", + "--columns", + "application", + ] def list_foreign_pkgs_versioned(self) -> list[str]: """ @@ -54,11 +61,11 @@ class Commands: """ return ["pacman", "-S", "--color=always", "--needed"] + pkgs - def install_flatpak_pkgs(self, pkgs: list[str]) -> list[str]: + def install_flatpak_pkgs(self, pkgs: list[str], as_user: bool = False) -> list[str]: """ Running this command installs all listed packages, and their dependencies/runtimes automatically. """ - return ["flatpak", "install"] + pkgs + return ["flatpak", "install", "--user" if as_user else "--system"] + pkgs def install_files(self, pkg_files: list[str]) -> list[str]: """ @@ -91,11 +98,11 @@ class Commands: """ return ["pacman", "-Syu", "--color=always"] - def upgrade_flatpak(self) -> list[str]: + def upgrade_flatpak(self, as_user: bool = False) -> list[str]: """ Updates all installed flatpak REFs including runtimes and dependencies. """ - return ["flatpak", "update"] + return ["flatpak", "update", "--user" if as_user else "--system"] def remove(self, pkgs: list[str]) -> list[str]: """ @@ -104,17 +111,17 @@ class Commands: """ return ["pacman", "-Rs", "--color=always"] + pkgs - def remove_flatpak(self, pkgs: list[str]) -> list[str]: + def remove_flatpak(self, pkgs: list[str], as_user: bool = False) -> list[str]: """ Running this command will remove the listed REFs. Unused dependencies might be kept, but to remove them another command needs to be run. """ - return ["flatpak", "remove"] + pkgs + return ["flatpak", "remove", "--user" if as_user else "--system"] + pkgs - def remove_unused_flatpak(self) -> list[str]: + def remove_unused_flatpak(self, as_user: bool = False) -> list[str]: """ This will remove all unused flatpak dependencies and runtimes. """ - return ["flatpak", "remove", "--unused"] + return ["flatpak", "remove", "--unused", "--user" if as_user else "--system"] def enable_units(self, units: list[str]) -> list[str]: """ diff --git a/src/decman/lib/__init__.py b/src/decman/lib/__init__.py index f277a8e..0e52fe6 100644 --- a/src/decman/lib/__init__.py +++ b/src/decman/lib/__init__.py @@ -420,6 +420,7 @@ class Source: directories: dict[str, decman.Directory], modules: set[decman.Module], flatpak_packages: set[str], + flatpak_user_packages: set[str], ignored_flatpak_packages: set[str], ): self.pacman_packages = pacman_packages @@ -432,6 +433,7 @@ class Source: self.directories = directories self.modules = modules self.flatpak_packages = flatpak_packages + self.flatpak_user_packages = flatpak_user_packages self.ignored_flatpak_packages = ignored_flatpak_packages def run_on_enable(self, store: Store): @@ -644,14 +646,14 @@ class Source: return result def flatpak_packages_to_install( - self, currently_installed_packages: list[str] + self, currently_installed_packages: list[str], as_user: bool = False ) -> list[str]: """ Returns all flatpak packages, that are not installed or ignored """ result: list[str] = [] - for pkg in self._all_flatpak_packages(): + for pkg in self._all_flatpak_packages(as_user): if pkg in self.ignored_flatpak_packages: continue if pkg not in currently_installed_packages: @@ -659,16 +661,17 @@ class Source: return result def flatpak_packages_to_remove( - self, currently_installed_packages: list[str] + self, currently_installed_packages: list[str], as_user: bool = False ) -> list[str]: """ - This returns a list of flatpak app ids, that need to be removed since they are installed but not found in either the list of ignored packages or the list of flatpak packages that need to be installed. + This returns a list of flatpak app ids, that need to be removed since they are installed but not found in either the list of ignored packages, + the list of system packages or the list of user packages that need to be installed. """ result: list[str] = [] for package in currently_installed_packages: if package in self.ignored_flatpak_packages: continue - if package not in self._all_flatpak_packages(): + if package not in self._all_flatpak_packages(as_user): result.append(package) return result @@ -702,12 +705,18 @@ class Source: result.update(module.pacman_packages()) return result - def _all_flatpak_packages(self) -> set[str]: + def _all_flatpak_packages(self, as_user: bool = False) -> set[str]: result = set() - result.update(self.flatpak_packages) + result.update( + self.flatpak_packages if not as_user else self.flatpak_user_packages + ) for module in self.modules: if module.enabled: - result.update(module.flatpak_packages()) + result.update( + module.flatpak_packages() + if not as_user + else module.flatpak_user_packages() + ) return result @@ -969,14 +978,14 @@ class Flatpak: def __init__(self) -> None: pass - def get_installed(self) -> list[str]: + def get_installed(self, as_user: bool = False) -> list[str]: """ Return all of the installed applications. Dependencies and runtimes are exluded since they will not be explicitly installed and thus flatpak will manage them. """ try: packages = ( subprocess.run( - conf.commands.list_flatpak_pkgs(), + conf.commands.list_flatpak_pkgs(as_user), check=True, stdout=subprocess.PIPE, ) @@ -995,7 +1004,7 @@ class Flatpak: user_facing_msg=f"Failed to get installed flatpak packages using '{error.cmd}'. Output: {error.stdout}." ) from error - def install(self, packages: list[str]): + def install(self, packages: list[str], as_user: bool = False): """ Install the listed flatpak packages. """ @@ -1003,7 +1012,7 @@ class Flatpak: return returncode, _output = echo_and_capture_command( - conf.commands.install_flatpak_pkgs(packages) + conf.commands.install_flatpak_pkgs(packages, as_user) ) if returncode != 0: raise err.UserFacingError( @@ -1020,7 +1029,7 @@ class Flatpak: f"Failed to upgrade flatpak packages. Process exited with code {returncode}." ) - def remove(self, packages: list[str]): + def remove(self, packages: list[str], as_user: bool = False): """ Remove all the listed packages and their unused dependecies. This has to happen in two steps. """ @@ -1028,7 +1037,7 @@ class Flatpak: return returncode, _output = echo_and_capture_command( - conf.commands.remove_flatpak(packages) + conf.commands.remove_flatpak(packages, as_user) ) if not returncode == 0: diff --git a/tests/test_source_resolution.py b/tests/test_source_resolution.py index 2d9275b..3b42ec5 100644 --- a/tests/test_source_resolution.py +++ b/tests/test_source_resolution.py @@ -141,6 +141,7 @@ class TestSource(unittest.TestCase): files={}, directories={}, flatpak_packages={"f1", "f2", "f3"}, + flatpak_user_packages={"fu1", "fu2", "fu3"}, ignored_flatpak_packages={"i1", "i2"}, ) @@ -290,6 +291,7 @@ class TestModuleUserServices(unittest.TestCase): directories={}, modules={self.ModuleWithUserServiceOne(), self.ModuleWithUserServiceTwo()}, flatpak_packages=set(), + flatpak_user_packages=set(), ignored_flatpak_packages=set(), ) self.store = Store() From 7320c889404bda4ea9dc411fc35cfe4e4490247a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1vid=20Groniewsky?= Date: Wed, 1 Oct 2025 11:19:04 +0200 Subject: [PATCH 08/13] Updated the README and the examples to include user wide installation of flatpak packages. --- README.md | 2 +- example/my_module.py | 3 +++ example/source.py | 3 +++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 39e945b..733cfe1 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Decman > 🎉 Support for Flatpaks was just added! 🎉 -> By default flatpak management this feature is disabled. Take a look at the example to learn how to use flatpaks. +> By default flatpak management this feature is disabled. Take a look at the example to learn how to use flatpaks. You can install to the user as well as the system installation. > 🎉 Decman now has a AUR package! 🎉 > diff --git a/example/my_module.py b/example/my_module.py index 193a34c..16a6e54 100644 --- a/example/my_module.py +++ b/example/my_module.py @@ -116,6 +116,9 @@ class MyModule(Module): def flatpak_packages(self) -> list[str]: return ["org.mozilla.firefox"] + def flatpak_user_packages(self) -> list[str]: + return ["org.github.kolunmi.Bazaar"] + def systemd_units(self) -> list[str]: return ["reflector.timer"] diff --git a/example/source.py b/example/source.py index 0573298..3ee4218 100644 --- a/example/source.py +++ b/example/source.py @@ -37,6 +37,9 @@ decman.flatpak_packages += ["dev.qwery.AddWater"] # They too can be ignored of course decman.ignored_flatpak_packages += ["org.signal.Signal"] +# You can also install them to your user installation instead of the system installation +decman.flatpak_user_packages += ["dev.zed.Zed"] + # To import GPG keys, set the GNUPGHOME environment variable. # It can easily be done with python as well. os.environ["GNUPGHOME"] = "/home/kk/.gnupg/" From d765eea3f46f6725f4022c688930efa4d1e5f6c7 Mon Sep 17 00:00:00 2001 From: David Groniewsky Date: Wed, 8 Oct 2025 19:13:08 +0200 Subject: [PATCH 09/13] Simple fixes --- example/my_module.py | 2 +- src/decman/app.py | 14 +++++++++----- src/decman/lib/__init__.py | 3 +++ 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/example/my_module.py b/example/my_module.py index 16a6e54..33edebf 100644 --- a/example/my_module.py +++ b/example/my_module.py @@ -117,7 +117,7 @@ class MyModule(Module): return ["org.mozilla.firefox"] def flatpak_user_packages(self) -> list[str]: - return ["org.github.kolunmi.Bazaar"] + return ["io.github.kolunmi.Bazaar"] def systemd_units(self) -> list[str]: return ["reflector.timer"] diff --git a/src/decman/app.py b/src/decman/app.py index 24ea995..f9f353c 100644 --- a/src/decman/app.py +++ b/src/decman/app.py @@ -216,7 +216,7 @@ class Core: l.print_error( "Flatpaks have been enabled in the source file, but the flatpak command could not be found. Either disable flatpaks or make sure that flatpak is installed and can be accessed by decman. Exiting." ) - exit() + sys.exit(1) self.store = store self.source = _resolve_source() @@ -291,8 +291,10 @@ class Core: ) l.print_list("Removing pacman packages:", to_remove) - l.print_list("Removing flatpak packages:", to_remove_flatpak) - l.print_list("Removing user flatpak packages:", to_remove_user_flatpak) + + if conf.enable_flatpak: + l.print_list("Removing flatpak packages:", to_remove_flatpak) + l.print_list("Removing user flatpak packages:", to_remove_user_flatpak) if self.only_print: return @@ -344,8 +346,10 @@ class Core: ) l.print_list("Installing pacman packages:", to_install_pacman) - l.print_list("Installing flatpak packages:", to_install_flatpak) - l.print_list("Installing user flatpak packages:", to_install_user_flatpak) + + if conf.enable_flatpak: + l.print_list("Installing flatpak packages:", to_install_flatpak) + l.print_list("Installing user flatpak packages:", to_install_user_flatpak) # fpm prints a summary so no need to print it twice if self.only_print: diff --git a/src/decman/lib/__init__.py b/src/decman/lib/__init__.py index 0e52fe6..276f357 100644 --- a/src/decman/lib/__init__.py +++ b/src/decman/lib/__init__.py @@ -998,6 +998,9 @@ class Flatpak: if "Application ID" in packages: packages.remove("Application ID") + if packages == [""]: + return [] + return packages except subprocess.CalledProcessError as error: raise err.UserFacingError( From 7ea1ef2d8159e1b3163f9615906f25495302795e Mon Sep 17 00:00:00 2001 From: David Groniewsky Date: Thu, 9 Oct 2025 22:42:19 +0200 Subject: [PATCH 10/13] Fixed the management of flatpak packages installed to the user installation as requested in the PR --- example/my_module.py | 4 +- example/source.py | 4 +- src/decman/__init__.py | 4 +- src/decman/app.py | 46 ++++++++++++++----- src/decman/lib/__init__.py | 92 ++++++++++++++++++++++++-------------- 5 files changed, 99 insertions(+), 51 deletions(-) diff --git a/example/my_module.py b/example/my_module.py index 33edebf..fa889f6 100644 --- a/example/my_module.py +++ b/example/my_module.py @@ -116,8 +116,8 @@ class MyModule(Module): def flatpak_packages(self) -> list[str]: return ["org.mozilla.firefox"] - def flatpak_user_packages(self) -> list[str]: - return ["io.github.kolunmi.Bazaar"] + def flatpak_user_packages(self) -> list[tuple[str,str]]: + return [("username", "io.github.kolunmi.Bazaar")] def systemd_units(self) -> list[str]: return ["reflector.timer"] diff --git a/example/source.py b/example/source.py index 3ee4218..e2742af 100644 --- a/example/source.py +++ b/example/source.py @@ -38,7 +38,9 @@ decman.flatpak_packages += ["dev.qwery.AddWater"] decman.ignored_flatpak_packages += ["org.signal.Signal"] # You can also install them to your user installation instead of the system installation -decman.flatpak_user_packages += ["dev.zed.Zed"] +decman.flatpak_user_packages += [ + ("username", "dev.zed.Zed") +] # To import GPG keys, set the GNUPGHOME environment variable. # It can easily be done with python as well. diff --git a/src/decman/__init__.py b/src/decman/__init__.py index 578c9e8..ede1412 100644 --- a/src/decman/__init__.py +++ b/src/decman/__init__.py @@ -387,7 +387,7 @@ class Module: """ return [] - def flatpak_user_packages(self) -> list[str]: + def flatpak_user_packages(self) -> list[tuple[str, str]]: """ Override this method to return flatpak packages that should be installed to the user installation as a part of this Module. @@ -427,5 +427,5 @@ files: dict[str, File] = {} directories: dict[str, Directory] = {} modules: list[Module] = [] flatpak_packages: list[str] = [] -flatpak_user_packages: list[str] = [] +flatpak_user_packages: list[tuple[str, str]] = [] ignored_flatpak_packages: list[str] = [] diff --git a/src/decman/app.py b/src/decman/app.py index f9f353c..7a5271f 100644 --- a/src/decman/app.py +++ b/src/decman/app.py @@ -6,8 +6,10 @@ Module containing the CLI Application. import argparse import os import shutil +import subprocess import sys import traceback +import pwd import decman import decman.config as conf @@ -285,16 +287,12 @@ class Core: to_remove_flatpak = self.source.flatpak_packages_to_remove( currently_installed_flatpak ) - currently_installed_user_flatpak = self.flatpak.get_installed(True) - to_remove_user_flatpak = self.source.flatpak_packages_to_remove( - currently_installed_user_flatpak, as_user=True - ) l.print_list("Removing pacman packages:", to_remove) if conf.enable_flatpak: l.print_list("Removing flatpak packages:", to_remove_flatpak) - l.print_list("Removing user flatpak packages:", to_remove_user_flatpak) + self._remove_user_flatpaks(only_print=True) if self.only_print: return @@ -304,7 +302,21 @@ class Core: # flatpak if conf.enable_flatpak and self.update_flatpaks: self.flatpak.remove(to_remove_flatpak) - self.flatpak.remove(to_remove_user_flatpak, True) + self._remove_user_flatpaks() + + def _remove_user_flatpaks(self, only_print: bool = False): + # get all users through a command instead of pwd because pwd also lists all 'virtual' users. add root since they can also have user installed packages + users = subprocess.run(["users"], check=True, stdout=subprocess.PIPE).stdout.decode().strip().split('\n') + users.append("root") + + for user in users: + currently_installed_flatpak = self.flatpak.get_installed(as_user=True, which_user=user) + to_remove_flatpak = self.source.flatpak_packages_to_remove(currently_installed_flatpak, as_user=True, which_user=user) + l.print_list(f"Removing flatpak packages from user installation for user {user}", to_remove_flatpak) + + if only_print: continue + + self.flatpak.remove(to_remove_flatpak, True, user) def _upgrade_pkgs(self): """ @@ -340,16 +352,12 @@ class Core: to_install_flatpak = self.source.flatpak_packages_to_install( currently_installed_flatpak ) - currently_installed_user_flatpak = self.flatpak.get_installed(True) - to_install_user_flatpak = self.source.flatpak_packages_to_install( - currently_installed_user_flatpak, True - ) l.print_list("Installing pacman packages:", to_install_pacman) if conf.enable_flatpak: l.print_list("Installing flatpak packages:", to_install_flatpak) - l.print_list("Installing user flatpak packages:", to_install_user_flatpak) + self._install_user_flatpaks(only_print=True) # fpm prints a summary so no need to print it twice if self.only_print: @@ -362,7 +370,21 @@ class Core: if conf.enable_flatpak and self.update_flatpaks: self.flatpak.install(to_install_flatpak) - self.flatpak.install(to_install_user_flatpak, True) + self._install_user_flatpaks() + + def _install_user_flatpaks(self, only_print: bool = False): + # get all users through a command instead of pwd because pwd also lists all 'virtual' users. add root since they can also have user installed packages + users = subprocess.run(["users"], check=True, stdout=subprocess.PIPE).stdout.decode().strip().split('\n') + users.append("root") + + for user in users: + currently_installed_flatpak = self.flatpak.get_installed(as_user=True, which_user=user) + to_install_flatpak = self.source.flatpak_packages_to_install(currently_installed_flatpak, as_user=True, which_user=user) + l.print_list(f"Installing flatpak packages to user installation for user {user}", to_install_flatpak) + + if only_print: continue + + self.flatpak.install(to_install_flatpak, True, user) def _create_and_remove_files(self): l.print_summary("Installing files.") diff --git a/src/decman/lib/__init__.py b/src/decman/lib/__init__.py index 276f357..6fbf15b 100644 --- a/src/decman/lib/__init__.py +++ b/src/decman/lib/__init__.py @@ -7,10 +7,9 @@ import os import pty import shutil import subprocess -import sys -import threading import time import typing +import pwd import decman import decman.config as conf @@ -37,7 +36,6 @@ def print_continuation(msg: str, level: int = SUMMARY): if level == SUMMARY or conf.debug_output or not conf.quiet_output: print(f"{_CONTINUATION_PREFIX}{msg}") - def print_error(error_msg: str): """ Prints an error message to the user. @@ -45,7 +43,6 @@ def print_error(error_msg: str): print(f"{_DECMAN_MSG_TAG} {_RED_PREFIX}ERROR{_RESET_SUFFIX}: {error_msg}") - def print_warning(msg: str): """ Prints a warning to the user. @@ -53,7 +50,6 @@ def print_warning(msg: str): print(f"{_DECMAN_MSG_TAG} {_YELLOW_PREFIX}WARNING{_RESET_SUFFIX}: {msg}") - def print_summary(msg: str): """ Prints a summary message to the user. @@ -61,7 +57,6 @@ def print_summary(msg: str): print(f"{_DECMAN_MSG_TAG} {_CYAN_PREFIX}SUMMARY{_RESET_SUFFIX}: {msg}") - def print_list( msg: str, l: list[str], @@ -121,7 +116,6 @@ def print_list( print_continuation("", level=level) - def print_info(msg: str): """ Prints a detailed message to the user if verbose output is not disabled. @@ -129,7 +123,6 @@ def print_info(msg: str): if conf.debug_output or not conf.quiet_output: print(f"{_DECMAN_MSG_TAG} INFO: {msg}") - def print_debug(msg: str): """ Prints a detailed message to the user if debug messages are enabled. @@ -137,7 +130,6 @@ def print_debug(msg: str): if conf.debug_output: print(f"{_DECMAN_MSG_TAG} {_GRAY_PREFIX}DEBUG{_RESET_SUFFIX}: {msg}") - def prompt_number( msg: str, min_num: int, max_num: int, default: typing.Optional[int] = None ) -> int: @@ -420,7 +412,7 @@ class Source: directories: dict[str, decman.Directory], modules: set[decman.Module], flatpak_packages: set[str], - flatpak_user_packages: set[str], + flatpak_user_packages: set[tuple[str,str]], ignored_flatpak_packages: set[str], ): self.pacman_packages = pacman_packages @@ -646,14 +638,14 @@ class Source: return result def flatpak_packages_to_install( - self, currently_installed_packages: list[str], as_user: bool = False + self, currently_installed_packages: list[str], as_user: bool = False, which_user: str = "" ) -> list[str]: """ Returns all flatpak packages, that are not installed or ignored """ result: list[str] = [] - for pkg in self._all_flatpak_packages(as_user): + for pkg in self._all_flatpak_packages(as_user, which_user): if pkg in self.ignored_flatpak_packages: continue if pkg not in currently_installed_packages: @@ -661,7 +653,7 @@ class Source: return result def flatpak_packages_to_remove( - self, currently_installed_packages: list[str], as_user: bool = False + self, currently_installed_packages: list[str], as_user: bool = False, which_user: str = "" ) -> list[str]: """ This returns a list of flatpak app ids, that need to be removed since they are installed but not found in either the list of ignored packages, @@ -671,7 +663,7 @@ class Source: for package in currently_installed_packages: if package in self.ignored_flatpak_packages: continue - if package not in self._all_flatpak_packages(as_user): + if package not in self._all_flatpak_packages(as_user, which_user): result.append(package) return result @@ -705,17 +697,28 @@ class Source: result.update(module.pacman_packages()) return result - def _all_flatpak_packages(self, as_user: bool = False) -> set[str]: + def _all_flatpak_packages(self, as_user: bool = False, which_user: str = "") -> set[str]: + # loop through all the user packages and save which ones are owned by the currently selected user + current_user_flatpak_packages = [] + for pkg in self.flatpak_user_packages: + if not pkg[0] == which_user: continue + current_user_flatpak_packages.append(pkg[1]) + result = set() result.update( - self.flatpak_packages if not as_user else self.flatpak_user_packages + self.flatpak_packages if not as_user else current_user_flatpak_packages ) for module in self.modules: if module.enabled: + module_current_user_flatpak_packages = [] + for pkg in module.flatpak_user_packages(): + if not pkg[0] == which_user: continue + module_current_user_flatpak_packages.append(pkg[1]) + result.update( module.flatpak_packages() if not as_user - else module.flatpak_user_packages() + else module_current_user_flatpak_packages ) return result @@ -953,7 +956,6 @@ def print_highlighted_pacman_messages(output: str): # Break, as to not print the same line again if it contains multiple keywords break - def echo_and_capture_command(program: list[str]) -> tuple[int, str]: """ Runs the given CLI program and arguments. @@ -973,21 +975,27 @@ def echo_and_capture_command(program: list[str]) -> tuple[int, str]: return (returncode, output) +def get_user_info(username: str) -> tuple[int, int]: + info = pwd.getpwnam(username) + return (info.pw_uid, info.pw_gid) class Flatpak: def __init__(self) -> None: pass - def get_installed(self, as_user: bool = False) -> list[str]: + def get_installed(self, as_user: bool = False, which_user: str = "") -> list[str]: """ Return all of the installed applications. Dependencies and runtimes are exluded since they will not be explicitly installed and thus flatpak will manage them. """ try: + uinfo = get_user_info(which_user) packages = ( subprocess.run( conf.commands.list_flatpak_pkgs(as_user), check=True, stdout=subprocess.PIPE, + user=uinfo[0] if as_user else 0, + group=uinfo[1] if as_user else 0, ) .stdout.decode() .strip() @@ -1007,19 +1015,26 @@ class Flatpak: user_facing_msg=f"Failed to get installed flatpak packages using '{error.cmd}'. Output: {error.stdout}." ) from error - def install(self, packages: list[str], as_user: bool = False): + def install(self, packages: list[str], as_user: bool = False, which_user: str = ""): """ Install the listed flatpak packages. """ if not packages: return - returncode, _output = echo_and_capture_command( - conf.commands.install_flatpak_pkgs(packages, as_user) + uinfo = get_user_info(which_user) + + proc = subprocess.run( + conf.commands.install_flatpak_pkgs(packages, as_user), + check=True, + stdout=subprocess.PIPE, + user=uinfo[0] if as_user else 0, + group=uinfo[1] if as_user else 0, ) - if returncode != 0: + + if proc.returncode != 0: raise err.UserFacingError( - f"Failed to install flatpak packages. Process exited with code {returncode}." + f"Failed to install flatpak packages. Process exited with code {proc.returncode}." ) def upgrade(self) -> None: @@ -1032,32 +1047,41 @@ class Flatpak: f"Failed to upgrade flatpak packages. Process exited with code {returncode}." ) - def remove(self, packages: list[str], as_user: bool = False): + def remove(self, packages: list[str], as_user: bool = False, which_user: str = ""): """ Remove all the listed packages and their unused dependecies. This has to happen in two steps. """ if not packages: return - returncode, _output = echo_and_capture_command( - conf.commands.remove_flatpak(packages, as_user) + uinfo = get_user_info(which_user) + + proc = subprocess.run( + conf.commands.remove_flatpak(packages, as_user), + check=True, + stdout=subprocess.PIPE, + user=uinfo[0] if as_user else 0, + group=uinfo[1] if as_user else 0, ) - if not returncode == 0: + if not proc.returncode == 0: raise err.UserFacingError( - f"Failed to remove flatpak packages. Process exited with code {returncode}." + f"Failed to remove flatpak packages. Process exited with code {proc.returncode}." ) - returncode, _output = echo_and_capture_command( - conf.commands.remove_unused_flatpak() + proc = subprocess.run( + conf.commands.remove_unused_flatpak(as_user), + check=True, + stdout=subprocess.PIPE, + user=uinfo[0] if as_user else 0, + group=uinfo[1] if as_user else 0, ) - if not returncode == 0: + if not proc.returncode == 0: raise err.UserFacingError( - f"Failed to remove unused flatpak packages. Process exited with code {returncode}." + f"Failed to remove unused flatpak packages. Process exited with code {proc.returncode}." ) - class Systemd: """ Interface for interacting with systemd. From 69bcaadf90e5d05d52d969e2e9378b9d9c42a8c8 Mon Sep 17 00:00:00 2001 From: David Groniewsky Date: Thu, 9 Oct 2025 22:47:35 +0200 Subject: [PATCH 11/13] Forgot to set the HOME variable so set that as suggested in the change request --- src/decman/lib/__init__.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/decman/lib/__init__.py b/src/decman/lib/__init__.py index 6fbf15b..6b8c6d3 100644 --- a/src/decman/lib/__init__.py +++ b/src/decman/lib/__init__.py @@ -1023,6 +1023,9 @@ class Flatpak: return uinfo = get_user_info(which_user) + env = os.environ.copy() + user_env = env.copy() + user_env["HOME"] = os.path.expanduser(f"~{which_user}") proc = subprocess.run( conf.commands.install_flatpak_pkgs(packages, as_user), @@ -1030,6 +1033,7 @@ class Flatpak: stdout=subprocess.PIPE, user=uinfo[0] if as_user else 0, group=uinfo[1] if as_user else 0, + env=user_env if as_user else env ) if proc.returncode != 0: @@ -1055,6 +1059,9 @@ class Flatpak: return uinfo = get_user_info(which_user) + env = os.environ.copy() + user_env = env.copy() + user_env["HOME"] = os.path.expanduser(f"~{which_user}") proc = subprocess.run( conf.commands.remove_flatpak(packages, as_user), @@ -1062,6 +1069,7 @@ class Flatpak: stdout=subprocess.PIPE, user=uinfo[0] if as_user else 0, group=uinfo[1] if as_user else 0, + env=user_env if as_user else env ) if not proc.returncode == 0: @@ -1075,6 +1083,7 @@ class Flatpak: stdout=subprocess.PIPE, user=uinfo[0] if as_user else 0, group=uinfo[1] if as_user else 0, + env=user_env if as_user else env ) if not proc.returncode == 0: From 6ae8c789594e6925060401f8ac09cdfa5034632a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1vid=20Groniewsky?= Date: Mon, 13 Oct 2025 09:36:46 +0200 Subject: [PATCH 12/13] Switched the variable type of user defined flatpak packages to match the type of the user systemd services --- dist/decman-0.3.4-py3-none-any.whl | Bin 0 -> 51701 bytes example/my_module.py | 4 +- example/source.py | 7 +- src/decman/__init__.py | 6 +- src/decman/app.py | 66 ++++++++++++---- src/decman/config.py | 14 +++- src/decman/lib/__init__.py | 117 ++++++++++++++++++----------- 7 files changed, 144 insertions(+), 70 deletions(-) create mode 100644 dist/decman-0.3.4-py3-none-any.whl diff --git a/dist/decman-0.3.4-py3-none-any.whl b/dist/decman-0.3.4-py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..4d1174ff6fda7b07e05497d0a79214487125411f GIT binary patch literal 51701 zcmY(qQ;;r96Rp{{ZQHiF+qP}nwr$(C&9`l9x2@go`OaL-Kj$K&BI+U|@}}xpxiS@H zK*7*}fPkQYR-0vY*iyAnWMF`Re6NKKT_LgYCaCm6|1{+S2)q4aVH~p@ObIRR9)>&%Puzlo7BE( z42_EU(sV46)OWr^pK)8yt9GkS;H}0v((qG{+H2}eq;`f7if~;EMFEBxGgfR{nR>!G zHEN?Rqy8N^uOZk^s}*5g*8vNHc^MM$LWP{2yptDozOAk^>Z8;gq`I$aD;ZsEZ=;4r zJC(f~jwejtZDI5HA~-eQYZlI}-+Y&;S=V9|ObU*kl*CudR>z0-w`mKdYE(k-T==Q3 zjrmjjN;Zd+=_~z2JDk|$Ms$d9@VwjDS=wN=Y*-4{^lME4KkN4jm{0Rnx*@G4FIc(O z%8)zZV1p*ce5FT|t?KXOqbC>TogiKA_Duz6>>#iyx`P@&077nHoc4jXNZ6mY9*)InFcH-mUw2u$su=88T z>yjH?m~H-YxzQ@^(@ZE@TIiUuTcY@4woz(-AvxEsn3Y(=ceAe@rua5QkV*$q<>E)8 z#a$dUVSW;Rop6#ZHnVn0-hrfXaTfbyvcpF=NX4-E?5nI>MLK~7l~3#0qbbv^SBkw*;E)f&;x%LdPhEzKrX zx*PDj9z49}#pWl1J6lEiUbg#aGrUiTqb+E@MeVw^lbT%Lmb|xPY600dCyrJj!K*gY zL@QFdb~j@OQ!NgirN998a6fJ=Ncmk@@2{4U9KB#hf~9L`p;4{4Uy+tU+5+H-aG|bH z4FfpYvf;l;P-ZZrm_o?MqaI=+;!$bza6vKDs@1YmKuz@!`?U4D^_77Y1nlK$MU22r zV$TDv;#x#cl0t;~it@B;)so8lt954n6JDSrj8wMqa)C7D4S{hC>_(oOHM{G-n%*G( z)`Vfxo)=hNWb`XT6WY?Y(CvZ#Fi`!60l%+0qR0mXp^67&kZxd4iEd+PKfZsyBRKe3 zfm^@wO+q1iMF`04bm|Qmt<*Q2`#KAOmCIWl=N(F;LK9)ohZd%Wt-L15C|q`&CpZZZ zb`suB1IA1ld;`aK$H5r<0|);*AZ!<@AD;RG2zaz3wtHzo(~&8ztfYVtLjkiwScqy1 zO1|RYBS_lnrk$9RiV8XwK8M&OK!(GNXdQV)L(t3XMvn~_dQWd86dULW`S;%Oie&5v zn{DZ#cL$sshKjUc)ZbJxEFi~_c}qFdh@Uu+j3eEZ)$gt*u|Wj+sdEO6tttw@uD!d4+46|*%HGMDj$V1mZD7K*^tSu}d3^K( z)fEP#j4ZaS6tn|u^3sE}$B2Sby~4P>X;9$_ABRzrG9>%l%}#gBQsM9kX6Bo4Is*ha zpbQQyrMi+Op|leS>omv>8;QCFpn?Syj)Y??*6S}oBk;ta=0O)K?KMUtU9cX9h-(B{ z3qn&o*6SzX{rCZR#frEM+e?xLvs>WeRbhW*Tz@PJb9JoqHCIRM@(ztWl;SamBBA$P zG9JqtRV^SZ&&qooBsW{QXOW8^f|`m;+M=bVY(_(`nWuqX!9zwb>o&ZYp_dKL`mRm{ znxdpBZN}Uy^>mHe&Z3v2Vt)l$rYIekg%p`Su{am!fRzuXw3%>hm+As)$u-qMrP?jdcE$}Zda**1qP=DjniNwYrYeAmOQL7q@GY9{ox(-bSrb%H%c zaHM^X%VHD`B7Av{$Uqs6ym5m&7R5NwsKhqkEpzx;mdCkGxi@f<_yd{Nx^LQHy1fLR zl>FWsO@55GK?q>1ilvMTidJEDI^^-BRY5~V6N~bnTI?y*YND)Mou2LUw;|I6Ywf9j zTWCHXaJGAO6^OPVyPnxG6948 zVbX*niy(2H#_7e2zzl!jtV(u^L;~egDzL;~6DXN^>CffxI)m8s7}CAk;#H|u^0kI5 zHUr#I>>DCRjb#F^UfqdvkE_~E9#LLCDK?&bgjZ?S4&q?INd;7&Oy1FA&XY!KZ^h^0 z`#Iu=ERH)nwNKR>a<-Y=OX$kp?p;#}0VpAAXKqOax@d&R!fIq>38s|ijj%Ff_97dS zljL8M1a00S!X?Hmh@fRDNpHkAl&XKoEia#$6>cc3pp&wpf=#tM=`a_Qa zR=)4|9P=3VKWH`V&j!ki>K>f%{n2cYJYnPc#aLxJHBl5VS2>c^ajR?K^MU54CS^{r z%WQ>Y^&At%b#{lC{kE*06iU<&iW!k^H;P^7RgBx9A?{PDViF*!q7Wie*v(9*0?sQU zf5{Em}a(iZWqA%`#TonN(6Cw(jmL`mJn<(sQV*t^jw{1X9Jb0;^?Q}^Wx z_sV_+{-jsmt?iD3uuI1DBv;&Virr{Rg{T|tdP4?yL7pEH+8h((0>|zea{<}QOyl?6&U}7m~ zGn0~W(zCB0Adl(}uZG_@(1o3TM=IL{$^Ck4_o*Aufk1juzG6KowJE2 z67FQF_N;ihYpX@CQk)fE=i)X2NVj7G&ag9`0IbSY^W*fYNTR>5R7E}QQjE;{`I{WPB*t>FVJ~y+ znypz(+euqsvI?7c;+#B-T~US^F0OLj(w=^S6#qZ;^bq6I(JGh7BKQwE$^VQ03q9?O ztnB|^^qiTcpJb#_`XePbCpWt+Lq~ORB+~fejLQ1N#@6-!%cjUHr_*xRP^*Ce1QaI- z1O)#-kTr62{67qftLJds`M^IAtQIICfp6EKAP&^QW$O_HeoMO1CrVfkDV$`P6GvB# zfn9dI|BnEFS$IWPBLQ3OOxn^-SN zuS)%@*7jWuODx61_*exrL&Pfris7vN45)70VJ9G})n7$IhH`Bpahue=aZwsC*bt!9?5+EZKt4jHDOtC5yW|>Y`jc83bc<9igy9Wcw0 z`<-OP?GA5+;aP9kNqpLG_elRKue#e3lC)ktS|{K%=iYp1J2R7QW~if6cKLb>$caoTAFoazenBOYj3u3CZN|!xB1Wk{~WsOJf4n>t_yRh zC*V!myvx10lu(^ELJN;Rb=-EX=i^saUUl6;edQThekM>@X0n|Wf3_U}90+cvfo}%` z?qTGZLrkJ35~tWhZX)A^49}Pp?<4F{qh}Vd7R)WJJDle&u~YJ@d0$Yx)E?KclzfnZ z(VM%KYHK*<)E(2+AZ<$MW5m-k^n#p$drpj1UR+`@qo6D~p!z9ph~pS3nr2n=`2EqB z*e=&oAS&wolHxAD#aC&dI7`*zj?F%UdDVP1cHGkxGg4t2nj?}2sKsImW^G4y?B>dm z@d)rXkCIPC`_UW-u8oRE{D z2uXd}gH)#&{(K-LI#6xlwjV;n+Z(en>@}svRhG*V(q^on zU(~41chqJ0PR}8#eWZ}T@e9yBj9zQb%2n>S->R+W$VOX(1!2(x%sJA3cEr-sF8UXs zN5gA;4K%Zf5V0$Xl7LmSe1{#N#n8WE*}Z>qpR|ocQorEf`sbaA?($M7r_AGtKLmwE zW!GEjcH44-EN9WBm7KW#iV|cDo-=^L&3pC!(VoOdA8xJR*vX@U8-sEd!x4j$lu87b zofKMwNy}{4Ez`qK0ZK330={AYe1K+xW--`cUmZe3&&>c)GNd5W=JOUGfH6mw6Or*H zph|mEtlLm9>@q2AuOY^@-Cn^v2G(a~OMIKBCxhg%!a{8`Me7!COQm@F+rHT|hT zCCbjS+b{0I$|@qmgwDMBt^bBQU9uT*G+OdEJu#sWD&{8@`>SNXmIQIC#?T|}v|}_d z#OCQV2sI$qQmSWF7BEf34w;}0EHVpE3lG_4JY$;gAt%}$;TkzYVFbV& z;$w*Ch-m4T+TYIiAW`s~?N@Gfp%N!xdXWUWLJjdvK*PuKh^te!%Y=KBQaRC&WtV_J zJI;ePvac^BvT={U&+n)w6=LIy(rhyml5&3z9}h8=|DaE2=r9Uf1&N~2r2sgTg(rIi z(npGR>8e@PRQ{|LcI3Jt=tAmCQm*E45U|lTu(zf96=gf!BzX+`F#(rV<^`(+|3GXG zNm*>O7D6O0X6QQlOKc?g5|YW8TGzS@1AP!DU&TMw^8iF1v_e!uy*dG4h`)U~4M0Tw zckmn+YL^&~?2o4k{hHxWHu)}feMU3M>IxdvhgMOO%-{NkeYF_|3%xf89J*g^J7EGD z7lfd|ntu;$NwQm*LR1v*kwmYxLS={t3dUnNnZEk*+OP-$09%Y7ZVnCs^*PiHhOWL# z4ESQ>IyABhak81#`b=++-?Ip8HhJ4pP?$XSoiY@Hky@0M7dv^`V}q=O@l zx4ArB;6;G@6Z_7l>n1k_K@wqW_UC-j*s3?VO75t_LaxpaW?FTHwZ+rByh2tuQ(2e` z-WayIY(9xPQJE$&n7dk4IeOj?+&L{nYg-xO)B|t-a!*|*9#|*2HWfF?#LuKTlOVTW z5eig+RyBK^o^f3ULxM&6214c$9wLJbWk&^vs;uDpl0?N<mVSzW=ps({ccx;3mHOziw!RJz`ccgPv^U8iUS>17D@56Y7rUe=~11t1G* z@eQ?)lO)&_DR~rfj&`|^imK*PMV@C8w7(=i^n9rp42n2S!WQrnYDSO6Ad1n$u_*a%66Go7O zvSe!c4g{?gEU*`NwTMNn$&w&q;6XgYJ96pccLe`>_hJ@YM}_Y@gA9ln@-YHLEhA*C zVIR#)ri1`KMS6z$3rH=PiHBEmf#8uC&5;vXvqcg`Bg4FEuZdgI6d<0VXs&Q88nMx| z|Dryv`2avNS*?Nf=@5_S&BqSC*QFo~{Utf0iT(bhIT>Jp$eur83VH}bk`*2K9m&bu zBs>&BC5B3xm^T<4JZWq!5WS}Bws?#;Xb(5KKt`Y8#Y$W5f%+>cq7Kwe=M zm9EHC)k|Kr!EWY*y8q<<%?r-9p%cq1iZf#$N=pHHOUKKlAiv?`QI{+P%R^Xx5E|-i zq!zC!%q0C|&EE-l;++hDOu|QX(hb3_Vz;?wU!RRN}W! zvxF%i%FAk8+H0fiU(kL0R6C&%We!u$4bDO%_wotU$GH__?-M2o9NrvRN=%IUmVA&JsGxUK1*D|80egq~vg#H8tkIZ-(Q%;F6Q+Tv#xbSccyEdUVcDt?~~F z=$0a15nYkKC`l98^Bx$h5_^l>MBokWer<2eEDz2NS^!90wq1 zhnrlFPkKZ@*Qc*cg2xc&!DHgF@N#S%t-}*orRBu%-vrWRTH8o?&^lh{nQ#B zAAo61kXZbS=^Q|#VTyeTO#q#}9v&AC{p)H+9>;@Id{_oxG_BSzVDcMK?7u1Y-4^wJN5eXh z_#`+qKjZwAane8G;<2>E9Fd^^kNy3#^?BSerYTjP^6IJHf{7Ut7+bOs^!(t7Ln7_Q+~~ z5Wb%^inHy$u>a|8$~cm>r|{o?A>ycdM>u~`*6_&f}VrM4B3`{xLPF)z5|C+GsL?wI#xslB^kJqVxEuo zq7D34)+XA(1~=v-(-+TU;VuJS(E9YgKP0^#40tZLP6LEAId($E!ClWQ=; zsePKOcNC1G$VT$;=1(L~VWYs}uhuupBH7*Hyc*Ix{{~83*~U8qobdz)Gfnq;qgBW(0LKwdaTfw@w^8FRt&wIhjdW5F1Oi^b6ThT ze_YpNTO}^DG&gKq*c;A`u*3G#)G~e{8j#@U56Pl{hp}-EVk2Cu*hLZijD`+GB3w{s zeBTw~Mkg zCfuQbfYiu!-j zC~r$zKnzkAJzl6u}7o*cm3MlOLLuwX%oeG^1DxgKsJWE<~6_rTevY zNYco8%Q!nt8?1-xY0`Cw-qfS{QFs(k6v+QD{X-#(T3TlPW7ScfZiii)37BjUHf7Kt z@`t8{6*X@mduR7A%0@OxGY7Y4$kL<5`kTDLPU^Huhq(f@B7=n+NKBb@3#}xxF%}f9 zfzKDT*2$JWo%_DY3^-9v@|F7=-E|R6U)6Y;dXf!{BobxHt2rnxyc2K5Ggw6wuEn_; zoyumc&5j14TF*bl{jOIf2L`CO)EbHpwqc4`@;L)-wvrCz*L@4WYARn30|*>Xe%+pynQX%SJnvo zUP?iV=QG96y`V)Gx~xFPwflJ)Xi-^FXHhQZ8f}LTbbsy4v?!Hmeu|a`j+eo33v#Cz z?g&Y{2*#J_X_$>}VJJg)ghTT34ksUO7uWf*xaIg|44Qd*rl{1#wLck4w8N$W+YvUH z?!qbnD~uo>U`}-JiL{!D(jQAh4#yUSYVJTsJApf>>_!c6EG~(gTbWD+LNer0F*mdU zozZ%DyXARR!n{VR*5zCY;e!aTa+88@sgW7oGaG_RW5GMh+Mk)4(edM(Tyt~q<#R}T z7^{XZjhm68?Kf*4`*qa4>uIdq2uk=wp3)qOen#~=O^hv)7kdVJY!dBV1p9IU9(Ys9{|reSd@Jeuqd^2`!ZjQ7%vI(~vEC=pHl2mHScG{Pj=L z1I5(YBjL?4MN$zonjJYWqc04~!XDzoaz3>-YJCC*%5W1L&TM8S%H87xlan+d)S-!< zhLUJ(qJi+kGrTPeg~cdGNLX!`h+DrKiw8!)Rn@Fm#gKZfdyHE(|ARrh``S7}&#Cb@ zew!T*J=PbJoUvON>?Zy=eU~}eOlj~U2E{`;K(CICeq&^AKxnk0o#^Hq@zw`VEznAgGB$;*HR{H@EVCqXe#f5qawH zlTE}kWZd-tMwwn1a%HZY=WAXqo*con0culVzK zjLgwj-3NHi-H5#h0dLO`Ka9%(_n!j;JB!}Gkk6uPzG&ASnfNEw2Y;eFKA`%zw~s?b z{h~cy_9jCbep~MR;3E>ZgGm|(z2&2VizF%yLge1v!#X&9S4ag8p9&QFTaJG2;5 zW643&V}Uu*tY3`+@r`UffHK;#c8}k~I<-B7)OEYlXMsb7pzE%vD)e<3FMw2rGG>6F zX^-H-H8l>{gvQPbRr; zf^diohR|0POuznoSJ&SI;|jVS!yQ?-=Lt7rYj}UXK8}g=AZb*p(cUI)4GE94v)h>W zt1c2{&NCFePcqQ_WkkR^&XRo%LArD36eyQ=A}Aya|Xi1m{bsEau^%Rntcb1LNMQ zhcv%_cP_;3CM9bNakmL_nG4N72>>KTHp%E+)=CrNa{?=;=x9e_aM~6?YGe_FYz`5OxCQx5)A2KK& zpWKk{ocs`a43zmCWUKvq`qwYfqfPeBA#91#HphLFoYxvDbtpB`SP`qYxG1TD%q!@I ze>7XY@qA@Dwa6m^5*L3l7qa!1#z|ebj5Mabq~ZivI7Ne&%V{6lMIP7}wW|#87$de| z#Of!w0+*Yy-tw=s7{Kj1Rs0m3VB5uagP7S3$U{&i^m(NUa5ViI8DZW463=CkWF#6_ z<`ZQ$Z&)5r$G;pplgPRM_V9h$?FEJ8dw7^yE;WqtPJ1uu-A9uWSjUrYouXFJKtK7D z0{%FnmeFl4)m#>gd0u;6?V%wW$FsT2EjvfQN{r98e^zs~xN!6lvAD77x#{+l-;R&E zZp~Ld;WPH4fND}wG++qUdRM|FG|yo>pwgW=4lM(3*UkBcZXI`jgBuG5${_O~Nuy5P zShABsPLtiKoM@u%{e5bunSWb3PG$RAA!}2%-JoowgCXWB#d$T;bz0Ll=%0_R3EKzx zT|R}w1X_wwy-_h$d|E6=&*fr1b2* zDbn#ExSl!SCb(C0L>d{l_4&|4)+&&$wu#9Z|7pHC$Zw{{kU6)~%+12`Yk~vC!Fb3m zRwRxv74Sbs`9D2(1i1u|^8eW6_rE~=AETH#J3Bc4hbVgS%rGNNaN-^XBA!+-MZA}OwQbm0F)=j3`FjNp7R_WzmOiDaGissuz~7DZ zO8*8%c)4im(7EQJU0>QZJcZlexKIJp3D34UwyX%@s6%m-?~W!%yh>E+VzgtQ=&hYx zEDfStC#mE88YGOH0GW`Cna>hs4%bzfp~nBAm|?kr_<~(tEqWLo{Pr#D|6fU1d~!N5 znf-gsxIjQGSwKJ-|0~JX%J~19V&S)O+5Y3a|3ZuO5i+HdO?yXCCB|(hZZm^hW;;W^ z-QjehN)?F}&6NTa5@8xP_Vw!eua^)=n7{X`uW=e9>LTmn;!IXy-jE6Fx^IqQrnEOM zU3`m9lZ|$o%52#zS>Y8zu{VQWLtINTpe9<^pONO>PHlg>VYRoaCG2BWw|?4qng(3k zLZ-ZbzrOiPn%z02+TL^hwPC7t(qdCBRTbIa1x8B?WLgoz;Pv$=V}fxHuP<)D_m}*~ z@maw3&$g7MhHVxmF*2Nw9^eL%oKWT@hJy+I-1 zW!itV8MC0!uj8bk=_I4&*ks@wQs4D?igwcg1>r71aP&^FpCzX8;Jc>;lhVNo3fZ^u zY`KF_e$Sup!_nE=l@+%(Psw4e`4OV}Ca>4e4aRZ)-(G<4)9Lv&s_(Cng9D>ki=~>m z7e1)k6`eye5AEY&b2m?9bvK?QtUCY$5?M$HUdh9?QF1cS^kCmZb09j|5;|ADUeh&Y z^{Ke(aH`t0<$7imWZt}Y@m!G|3-~FqCM8vmLxXumPuR^f6O*0s?T*5vjq0H9ID(1r zXaiJrv(^YLXJ`_K9C|wOC*d!VPI!u>ZuLp!B}w%eoi&a6CGgFrB^O=fTDYrS{QR8! z{2E3BC*-0w@$r#&N-DSIq$6y8QeY2haC)kPKR=HTEXlD6_F#i`x(pMJ&I79BKeEyy zCtPBVzD8YBHU84IG{t2`Bx^dJ2uRE?Xq`rK(9~me+9K^agwVL`+A%>&zmwrK*a^Anj7fCBz2?ZvL zu)3_r(t!q`EAVD-3x1xD5tW|FKCpMNXJSsR2t8KICFgg~=?-?k$jjApwh&XTb z;M9=)J5};^gMi1Tu_wvdnec#pFaRtKboUl)A{TrTMa_Y2tB%2Jk&57*xgk7^6g9@| zJA`%+n7({o)BQr5{dFE%1PVFLrloCSS7%_3tn-G-%?qG&o0v?0dvT($9sCu5WmEX_ z&0ruNEN}kyoO?Z=i59Hto3KCAyeEY4Rv78V7J~blldunC*tJ`p1m>uxNacG&5o(%p z_QdpyIuOKb>$=%I7N)I)TuWK;0(8e%7*?De9tznrNK5isz6&Z+U-VvlaXmLfHoEKo(;} z=eV1b-|zig5J9oirDXcBWyjMx6@$~1CW}v~l@8Z;VQOs4<+ak1>`=+*;2=Uul~6%s zmHcOs{AGDMN;m}qyL65?-ForPVphZRA=7NR!7Lweqg_*4jaM>wb`z5F#iM0PP5CgI zhNexDE@SetL<7zBH@Ubmge;O^@_@@_Jvh7G5P>y>9Fbuoykq~2w2Tdy03XcLQiWvU zoN|>)X^Hhnm~Ss0EZsoZyreBI813l1<_~jgiX6rmyk;zNcjn;%#@Fyc`onL{5Y+~P za%oKxUd@@f%#8+gqftZ0(D7#w`19q- z%fogF=LGR&HG?$A&?H==r$aZ;Zq(i}t4{8P5_%CK+9T}K>_>Q$?xR`$HFg)AZtQS#XxjQ_Dz|m;B~A!$c$Yk1?S`?r zB3giabuo1X9EE7i;0G+G%~GIFo8gh}&Zt?+u{0NkT_l&-EFeVN!pUB1HLN10X1m&a zhRq&mU|N&ti0^f{9%Y8d63@Mg>gqug7pP)0HE<%SvT1LFy_{UekY0#wbLs8!Y1J%+ ze#sMNhuKUx92zRRJAK1$MBa2)Q* z=5pKO!s&&np+X2i|HB2VmW|fNgKk{a`&tYfl2Ts-J!Fg(%A3FvVwz$cY)|d5NYBKU zgTJ`~OnKodIL1q8Y$6%_2(vx-zBG_|)~S2vN-4Y8p)FMng7-wh7*2yF^P_|T7|0{- z1d+CAw`%FN5#{Wf;uOa5+_Eo>K`t?zDhTzuPTtV;!hjDV00edPIbF1Z zS)+~1UQHUQSqNR%>^<}~br8+9Au$ohm&#$xAB{6f`=d;#ZY%?U^`?qy1E+;L35_xM z$Q)RQ`q&z_gdv}?vP4OJdJ%0$$Ay`|5V{+zp3AcgF5_I9iDYfo@lrhj4&BKicWeCL zY_6%2DGp~%u1Jd$a}0MO-JbB#ilff#HgAzcZl8-|kWFl0Ot>VjxYc?qwh$I;u@FhZ z9dW<%y78?$b-3&dqK}AmAq$sYY3Zz*Ysye9Ox!LxYId%C?o@0Szj(5CYObcqatynS zdhmmCGqQv!{EKUgE=e$H#O!cCX)Bk>8w0=DpRDL0N_01 zldS-4TQ~b^R+kUn;Z%y%4wxI=7VkPLBxtEpuiqS0yS9kkMPgFJwp`(`eG8e}VMu~b zd4>fQ7FL#ST7I9fW;UHeW%gLMam(nIAP#ofxZdEw5BLT+YRqq9dju|m!$trdprW|p zenm2pQD$~+>Uy|ZrVh|uS-q>E#kVdCA^k+Mdu8h3GOK!G zFP{!M31!q~+~fQr#J9|q%RoHl;y4L_5GZ>Z2~~4h#*mP;4iCmF7qzYkaN@T5D7xTu z(BTs7g%Zhcg(7hXlO91LRSgq2SlUk>KuQE7bnIvA)PhwF_1S09c%)LZ`H^YFkVr-7 zR`LnV9{x$rFyChXt7pZMypq!)P48Nl+o&JX59S?xpdYZOIAH2d+&qPBSL8i6 z9(L7t5OGBq%k6ZYi5SjLG5r)yI1%!3KsU~mpTF;Ruw}b$6tt*86nFg{~N#riZ9y|De&c``uh%s6| zm_Y~irJTBl!@hD_%Y4uIJbR7L=91=ni`NqBbiT<=BQ;}p)1z~Sux<73z~3|^hYY0o zQ~_^!7>_G(>VUg~4jN0(m2eGH58C8$JX|QvBkTFHP){bu{oNJ=S0$uV9k6M*RTAkS z4~dY{$atpI87#AU@WkW>>er5rjk_sFfZ64^PxAI>{4!ACXUn}Nr#D)aUz%{h{G$>H zyDkTel$CRLyBUiWCG8}kzUNGYEV8z9xfcm4J~RZ>|MA@FEPqRiz% z8C|&{ht4bfOkg_Xb zYFbB9IFs^kqumyjYlY8(1%a4s`LHT`SV7%f7Q_qxOU&oQMrL*0YB~g=9)NL! z|G`rP7$ddvl-ZB|^~E6^0g0PG0pE}Db;4aXO|*!-s6}{gH4^p|mV1yzxHWPFE0br` zlrKgkN!N;sSDCKrmt_j7KY#{bjh1WCqA0#pa~w8aC@0T8sMnrjj4dQSy*^Q^gS1m> zIQxPYT$josCZrLhotLL>ndb!`1qbhypQhchI|WY3C1sf!Ow$+%bk!0~{q*RK6CwjD z`OdByB2R#lU>$H~GEN}mJ#Ze5ie|>d0j&jJ!z+sGfiy(PK@b~rV@?HAG<~^x-U407 zbh`sRsPGd<1*6Q3Yy0nuAM^}ay(z@?mo+LKELB4+c+Sl`K65%zq9M2eCy}^e>FOyw zANTnaL;rX!6GyT?O}XmY6PT6Gu=BTrG?+g1nmVFgcx5Iu2~t@3k^w{F72y$7PT#f^ zjO~|Mc`j2#GP-0WNx>A>MQLWRuZ4a!GxAlF#FFd*iq1sdgA=oP*CzWtVJGq)Vi1Rs zv8C5eq_onwLa2H|nVXH2vS720zZlXqb-e*VJK!KJr#4?+it^VdX<0FHk4+r;K}uR; zzO<5tCj@5bw4g-3RmA}STmFJ%O zq67$}1$2`0*E9k_vgZ27=p4b^kLcOEWdcWsnFc*-OfI7J%X5B{?&@KeeJ0BT>MSrv|*Hzca@#{Ia8zzj4aGnUmBIXeEc@b9|@_X6b ze`_)cm)R1;u8wlNIPxYgi6{wpF{?;<8)DxKQ;}l3&MRyw4q7o(`&2m#-}vNnT8Hh)4DaChe6V}%p)sre z@y_$Q_C=uBEEaPjc7@D=m+tcLtr99tH&w#G@chC2}`#Oi|bo>@Lef}!WZSk`PGx=VYf z{E5kIue%cTw1D|}3BPmQ$pFe<;&lNuZbNm$-q8Dk2t;9sH-VVZT`Y;E^11P*GOZ3AY#olSTM-WismHoPb?Vw_~7hA9)}PjZ6Me) z3Va$}>k_#Trq*>HtsnQG&Ruic;saDo5KDz2r;uiU?67a&lLZ|kn#7@w#sRoGjdyfa=<21NCJ(6w` z`AyB?gEqi45wgeUUsNPhE?9Q?^292>M0A|!2Z)k$w8>PWkw&|50Z4jHcS+5w44j32 zNSuX-etn$_Chur>!c%o`L4!N0j2-;l@-Af=rx3&-G;pukIiF@8_XG9qQ?ef7@W&8@RYMrgs zOwfU8YeGx~79a8rt+S%zxF)D{z-C%N7)%fY79+ zmnOC4wem>{*`&hQ!(cR!^P8N>*T0Rz@7Izd`yCvdmqGkU4>k~@776fU&L4DNBZUs) zihQD<4t&69yt&73fIMcrD#1ei&*PuDL3e)6+TBAl)82F#Y~J7E^0ZK64yT+g^(8OV zbn8()(i>p~jXrW?RJwH-9j!K{Pc z1hlkyV(LGOp0CjELHzkEh%YhUgD~%X*N5ux6_YPM4+{N7pT>h!h>3r>j@ga}yBR#| z;g^IB2SWK&ASReW_a56~zQV^srJ1%L9>^((be2M!D&(I~oH#+F%@M;hvQGPF+`G=e zcw2;8I}TnJZSa(FmlcxFTc#mlnO91A?qUzoj>@1{AWDoCty*rnX56=^I{xADpKv7c zLf*Mcx%_%DGWFYxysC|w>j+a(bdToymJsZu-(4nK!zl!=z6?ih8AQwzi2>TOSbJ5z z0Y?jV-%$tydAauS`TGyzZ)Fyp_uH)^&;GvG}F$?x?J+AC!)p3yho5z z8HdIT{YKacMTRfVvgS#Y!h1XL{=grTNMfI@tx`^bBhFc7CVjl(_gF^yBQO!oUizzW zh83M%A#^o2Q@7Z}lvTMM(+jR-!TM&`(pM>ihvyW~F<74#RHw$ffp&w&V+W_(RDsn1 z!BkkR3_5-rS#_>7GzZ`q{cKv#?!(fli&Li`?@30_%JJzQc19z_IqCN~36-i^{c7Oo zs2S#NPV&g+jyg zo{mPk2QZ>xLBv3w5x;sik_lj~Natnd6sSTas}%liP$L_~s6rtQpSPp=vOzt>WHP7de3 zBA5t(Ax#j!bgsK2pK;em`aChpHcy!G5WWU3TuQWi*0=p~G)#4~%X&XuoDhqg&i~MV zDV&7IOX_aTdY(js7M8ig!{$@EILiYo4-o>>KgXH!a=89;+Z_LXMr z>GZG#x!q3T=2U~{IO`Hg%+L_AF?+}SI!+>F+zoQ18=&Ue6XnwrNx;!Zo^PG)>bF5- zyR!}FLs+tBw9pgl6(DLqz#M*kY!V*K?{uSi&pY{6^W#VdD0l=nwjNq`rtg@zqag#q zr6vdA?8LJ$p9o zk1g9R3|KJ0mb^61rmr7Ag?{8c&WNM@w1c@kJ4)eqZem9%7D=6->)O-%(8wc9jqZl;0_S&XD|+tnu9!70+KhfV-LG(X+4R*{*wr-Ye*yGdKkm9bmGYzTSB} z5zi(QV@Npj7`EzJ@@bEeU{Gt*H~sBl4EB=vesx^TD<(7`m37-1@l}`4mUf$l*Du}= zuQa|c(6JX`zNd=GRPqmVo}=aCGi83T@OKWZb^BZ(KQuZ`RFL>v@P97f|1mCwO`W{a zNq~Slih+QT|8JMi+|lm;toZPMC*XF(58N3rpMi9|#;{r;b{b$f7%SMKT)#els#6_uYeN8u@di+a`_DRE~#cC$(yRA!meN~mc-So3Fd@jE;dcZ+- znd5a#mN@F$;JsBFAXA8R)RYZLsHQifILP@%`qoO-a6fW%a$;J3s2sk z$Q2TFMQTVozb+Py0_si|Mbl*OG^jVLdF2!H12EE}oR=_{O85x#1cyBeo90Pcbf@!YW=?y?5zQIE8-@S6wXY$Xp$)sGB?PM~veQDHU?g$40 z0Qrpgz6byv=C`Y+0+y?}3*?xQas5(SpUt(xpD1wEbK?k@%`HQCgQ z5J3{MP$7Ruf9Wq`bde%C69Sl&|B5~%Mf7L<%l~Yq^tmBYP@8Fl)%j1q0Ami{`L7fI zJ8+DGrN|?}gv8GpcmO8WWx9Ms-a{(I)b6XL$?FyGE*0U^T{Ol*m$ij)e&ZTOlUs0x zH*59uM%fLbkJs0%O zW_7aKX?p`2gQqEkkaPTU3e!P}H48-M(&%V*Us-9nNJ33~|O` zNnD+xlK9du$;9+a^2-1wFIfRle(RT&T-(a0enRzpb;{rZWYL<7Oc@*oHji1feoe`p#Pmp*lP{j zkEU#-aK|biX?mGe??R1-)ty+^nSc1{$lbepAzS zoV^Gv3w%m6LP*lCrfNtP#@y_Ls5qIV74KN)iC&!RrgN_|=0Ov5U&o?K7UZFoM&WE` z%fJx|%<3B^6%nRfwP#@^+o#-?_lRAqbuD3dGCGi+Bs$Ns?RI;bo#ww)9EJkTEp~xfj6u)ig%ePpFb0lDrX#{EP6)BoFm}61NJN3=1Nx{Q zLTXbJx3mM09#Yh3X_e<{2t?8@FI%;2hNDQWHFD9|wE#y~$@fu!KPD94&;=si`EC%y zFrh;UG|YzjUS!w!9kj>x9vzLrs@Ck`w8(fjleHp8#6kQqA%0pYSaGnLp6K!VhEj~3 ztxG_i`Ge|CJm(3M*S%y>W_-(gMOlH)$Q3M{=twYxY40oH{vowmYtl;N;Cykw+HE$g zRI@s5Gxbn5ZSD-CpDo`ZBML#H9lqSbCWf2lO5J@V8xbrVfgFUSzg&Ctvcod;#l+An zHF68;sAse%^x|iLx85&h|dE^YhUcS8RM#P zola_A_&_gH4N{l(O>7_U0I8yE6nebL9(r9&fy=%_M|iAu%?Gdj*($Zw>Em>PFIwPZ z@^#)-i#z1l)2-M1?lk-L1Q)u~t>3-fX`Xe$H@h+*Y$~?}^6SZLQ)3&Zytf?!EDd%o zK#b6AvT3neRV_@m>;{T1z`h=M-5n)I$?{Gw@5OV)$n0=HX_>Ond--YA!uof#CSYV$KU>b;l%xG7M0QvJRBw<*QWqnXLCFl2X5A|AfCXn}zpPbJx zq)@PQbunod2BDgzo3nb>cMmO6bjH)~gDUE2pme=lEUNnkT8B?45;c#pOW}p_?bN&h z$y?&M$$76}wie{FKtKB&cWbkz+SF6p@kZG12^%evrv+_C;dJ$C*tW0`dKh7i0Xch! zDdBnILX~gAd*|m|GX6?a4>#K;gqu_gg`+L08q$nl${b(&c2(W4Z2cw%V7IQ4)ukiK z>~jT8nr#rHpz12O*GxO}^Gu@Jr+UUV3z*V0j4s9@RXKsMnFEw?aMD+nfn{u(XlMzq0v^N*0NZOzRw3meSL9xzFqGIuA3KUsU|P|Nh0WK z>!qrl1^ScTL7xw9+IHQXy?9Y<>Ks=@V5QANUe(tx>h<)+-M_!U0r+}*`mgL2b-ftv zd;=gP>wE6($M=6j(G3mm27yKZ(=6pVF+{Q~g^~of7>G+j$3DkUq#RJ50Fo1anNW)y zjQqEzS`CBdnSW*%gZ6Q)2A9Y?0}5#xq?Mm;%Wpn#^8mRGrY8Y9%eJ$NQ~LAtGU`7* zD4SW+#@+dgf&S49F4H);;0s;wV(=sU99+hbxqFd8lvwYRb@2g0x4;o94IOcpE1GM= zUzj_=1A3`_I^m4#u8U8|b6_D`n|WSsR%lfRi}N7n6Zxc9UiQuQ*1W1!ahohb``B6b z1^F7akn`w6(zb_4D9q2hcRa(#?11|GG2T~)_Cie^ZtNR)x38+%95q#c(ZBaq^L8QG zG{AX2_?A921Jn*i{V?L)PzmTQd_4kKVslhNdxf8m0B3iCC6IUg@(=)Kv$an~Z<*+) zV!S}k`MB zZS?YLNtiVoprDA_)V}x*11gmD->a7V`y?KPsZMTE`fGRUDR3@)_3*VYvA`JEZh_I} zPQYDqlR9aBup7{i$U1s8?OQ=*Y{k)?+Pi8Vw^A}WWFgn{qm;!EGDaESVJsLFJtnskI}^i*}|lU1P4p!PgT)%pDD&0duz;GP3?7iQX0 z1|xo=Zn9r5Ic|UPNPVPN9f?&D&w<2xhzYP2n-rg-le~r|a7W*|sSsUxx<_avZ;MAP zw*>m5enD##bRFzi>@K!EFa0fy-{=qS2CFG6%uw2sPjk@LqYK}W_j3;C|`n2Y`2x7QP1;STV+C%QyZ z_>)!5L||!}@sr`tQDutWO8{UeNz`>;SjEaoRKee2@m1v5i5yiYZ}L}uJ33Y_&EpXO z&_#SMEE>S1B_M*md$>Cx@;?BxpqKdoG37)Cp(Zvj$Ww-LZ0`ol;Zp{;P-K0cC3a_} zpVWT|DeVcg=}lEN$}L5QFyc67h;cUX44cjv7HyLF%^YB$(BARQi{xm0ef4;5rh;pC z)FCs1mW4$l_M!VmK5614n*D!U=WtsmP3ouFJx-j{hM3q9f)3Qy2Jvj0QQQ}tO+DfY zpK!~d2~DK9vmM^A<3*2QLzXz&H=l5VMQ_+_OAFd)mp7m`DeQ*}lcel$5(Cf|1N*;K z|JR~NEBmPtIZpBK7P#67Fvs`D*}7_)@@nCVdIyd%=|Zi2mM6NUgN=k`xteIrULP0! z%6%ywyf3r^9H9|Q)@HL@7WHE&TVE5WZabjt0)e}Qt}`*W)VW@!li$PhZx3Cc82))j zZK>|{ik67(7mhN|v#*3ghjHWxs?$*ERG{D$DombV!4q8^wDz8pihLWL%5?_?$OTr- zWmqWXBH19u`+~<3rvvyrH*l&slM_XTdPi_E(%{gtCVcYH@)VtXa4lWBs)5cz1f`G7 zvl{FI&4TFMRJ0j;|C&i#F+Q%k!K|yIsYC$0iW)_zV@C+X)VQd(|KlH_5P2v1;?+`H zJ@+@Ia1~vtMYX!7GdIBpfS-1qV27+78j^*yMRSf;kJHGjsbfVu0<{8Q7yN?li)IgH zFZ_ru%06M~N^@Pm>^i%2&^c~NA3s&mp0j4X7)N+E6U?wOnO4hXwc3mL!RO~h)Ink5 zxu8=Gu0USTibSwuCp&P)k9~dYB1wA8#gteqY_TA${=i;y<=zn@MLdmD5cA(uOJ$A& zxFK;9@*rM9ApXfW8WKTtrt3}dWV|gcN2v6tPhY<+R`CBI1Cu_4trLe73!4ZmJbku5 zI=N{pQ~($G2mB+BRvxqFdX^5+j)Iv99z7c@XF+Nua2s2e(A2~G|uNu*5H?el05)ozHkwgAbBcr)GJsM#Z)Kdfk>7!O& z3GZkhKA{3R_*S))$BpCUJuE09TBGT?GI0`1t-;9)omT`W7H^p;cND_L-;O*$>S4Vo zr)4VrX^xo?pHB(N zDfKBi^lUnFmwnwf^jt(WB>WH|vedoirrLmNX#@cS7#d2CsY^B^h%Yb_eriYeh?CP{ zfPx7`uzT$ONN3Dp2>Ni6sC}5{5zoWUD?A#Loa^@`$RP|#*L|MBJh3Sh1lprr(nu!i z@+_6b#|GiC7OZ0VHKjX*wQrUrGM~k^NGOlJGbc_UbLF&@?~vASG_q2YiYUk@FU#6L zyg9t8+8bWTUH~7DIwI@KdDNx!58a$!TzSH3e zJ?AiQ>qMgzs`Zj$Vx~&XzNSQ@+FOMQA`U)ZK6vA@>Vhp_bL4EdmL|#vRQ6?!$gnH- zFg$&VN$OA59&!p-lGJCS?ItoSOn?uPKDoOml#H=71jcCz;{hNy{FMeBua5f6PPz}$ zqm*1$!OGGZd(Zbn$k)|+_+=tG8Xspur{PH7g=sV$mTlB8b6&O=UfRcyge$w7EYQ_6 zcS7l+6L2RB=@U(n{~&x=?=^08j|Ry~9p`13OL*9<>TbPR=8aWLOhvOR9DC<1rAtsOZa=prxz_59!!tryI*gWy=v8sS9A6$8X#>qd;Q zEbEh#7LSRD*3j}oS}3s&&EXlYZdZ`T1X-a)UxF>`eeddz)xV)~GBiD=KvlEWndFuO zM12&!ofPZTZXzModsJ}l}p22SDI}nyazk2!iG8@Pj*u9a1 zkMA6$l8qh3Ftu5~&nMCa$O<25sQdH)$ta?IsK9?zC;;sF7|Iv&V(6%+sn_70lzV<+C3Rhx;cs7r14D zOcKFz^-P9~;p3@8bM(Yq*ErX+ZBTXhf&#%j2{PIx-7H$Qk~?AA_|n$?AQCa?>xpuL zz%0P4EJ(KYXOm0Eergb1DZ6x1f@pl$h|+aC_a|6ByXoGO&7ot}p}iPumQE~XQXse; zSyCkSNeg;WPdW?oD65Yy=p_cjS9CQ|dUL14P>`pv#RjkOH%`(wzwi|_9bdiuqI0>c za9loJckh($liZi--aYVICue_n_0cG8iv^~EIAc~e!Cgn$n>K}0`Un#x&Fyv=TI&FC zNO%gJ1E$`r(kmoIucOZkI@{0{QAMrH*fhgvFvsEeb);^fWrJM0>F-TD!{B*=;=4ER zzKhF_a$Pc?sVU}u^{EsX^B(uw81L-Qps>qwdykd8vD*gbhMp3|V?>Nyp1m{&>qJp7 z3w;_vxet2888-mtBfIN6d}1Pmyc&_a?HA1O$iB5+*V~A1DXZ#zUN`*X6GF0u^2Lm0 zh=ln8Q6|FA>2lo>fsH`JE3&?ku4^;@KjJcnvfO5Hwe}(ID+4dqRn-RJImP0>c*NUV zri%?_PZPe`0cu;%Y1$6P*#OVjiU6#Amt*zD@Q7l5Fp#v&t*;2UE@k(p@1lx=dV;S# z!9re}fulT#Gt|VK+{3rBhN2bvrrR66%T3aRwUZL~aG*;7;2%c!>U(ln4OFYUvaVKSfsUdT9?N^pJ&1TT5FHadcBKE}TQ%^05p@MU4Y7g# zbl}5ES|>jG){5B;aXwNn4eWREXcrx%Fi;o)yHnRWJ>Kpu4HPR1<@JfzR`sk^e;H0J(K zOU9Jzejn$>?U-CSJ{@E?zQ{hP7FKHa*>iB3?MZu1*q`TKcy4%31>PHC>=T{V=8yUu zh`t~!+=B2bLR07hfY*8l&kjjgO7o;&#CLt^WEDx5W8~yXc%x#9MM1>Lt_Ns*k$uDY zIg=Nm*wx%=+V)mjPWw(GhIm`tyQo1|^W?Ido6Z}k9ASa3s@wy?n`a)ay7teaOB}Ux zF1pbXpD6KDsgp`_QXfsX-6^(px@8Ev)8w8PekGB>X7*jF?k8s4-7YT!2^DR;T^D$z zx?g#`qLT>gBeBSD@Qseqcg{v3T%5R)GhH%ntQ>f=R$r8xegEUfci;c@r?=l}HF9~2 ztVvQ($cOB%s7qAafkR+2AFe$ zU{8RTs272E(2-Fwq(I1Pgn)uG$b>Tkr|gJeMG)9iXpvj&IccEwF}(ZxTh|-*rUY=??QRW6a*~>XpZJc zZ+eTaywL0-{^pQ2r3%G!74IityGKW@^7}R49qNufpblU@+DId7ZMi}B6{xg2B&LCr zn)_6n-nj2qT6SWiB_#|Aq}lFtY*FJQGxXEX3w$%?dKrY<&PKTF{$4cx582MM2d<9j?$*}Qqs6HBIPKa7N3hezi&#I zS3NZ8ixmvQRB+eE%vK#*aC?m}-ELKn(J<3VRE*w;>JZjtVqm{@?zy-oemibI`Jc~B zuzl<@5!^lTrl>h!(`Oze77^Zx61(8z%z-W&aXw4pJU}G9AvtTR3Qb=v$Ri z(9+w5!R-y6a@&ziUlj{`TUTl?-_rMh9Ub%dqRIpSQd9P*uBVZ&56KGNv!!E^JaB8r zJl!d^d-r~Vf_1s>D3E?@N8RkK%0URs-91zfDNQCrxXMQ4wg_}(BJCjclh#2^hc5l@ zb-5~=8=+jD9i-`zM>(bN+Ed^ihDTyASH?3(GfOWHU_I53{UOYWt#O!HAr-7;h3YJijufnbIpyBTk&ci=ciyXnNbDWfB_ zsX2WEO^h@%Yr~mxIc?Spbs-WyxiIKGSs8RyHiq%oeqz1D@z=+|Y}_&`-P(?_rn9zU z-D0?so-4j4$;|TH?;Z?)QOgV%+OX=n%hk{g7eg-;%}+x>}|^*(6o ziYekX9b-i|#v~)FbhxFzD0gnnrPt@K&YEz_Sfe&_%<+!T@W+V=S@oYnLPXNRFVr>C zGG;Q0SR5S>BaU5+1N7|O=2}V}f8k|>_8-V!pNYo=pUL3$G_UpYii?Cki06)i)ysQd7*9zPdQy3Faams9v`qaB7C!~c zqdw|87C^2xlp^PmFQh}+PXUJV;4`v~^F+6F(+Hlt&5spX6bP9cS3!d76Uj0XQ^Q;% z9P2-EY3n4s2<`?EWe!4SpXw-TCQrdx-O(`FIANq|Hz}Xco^dFwS)0Avip_=8dO82~ zgx6)A{a~ZN*>uIu-P~(>PsYYalTb^$NNa2#{1L~F=p)Tti{EJ630B$%p0B7Ydz*0^ zb!3eCBKzyhS3jIt$#k!n<1wkR&O>&4O|eh+NGtpTbD-JaHXahcl$$XeBP)QT+f@~y z-a+l(Hcg8gbb4NbQT1Gly{c*F<%7M$79W$lJjfmKq{9zg>!$gRSKyWpoF_3igr4Zv z;Snt21I!iBH zqNGuWmmNBlX_nf+D>bMED4zfyP+W3tu8)s))riyeuzQF>kv}L*w*9oEv)*ZA-^C`b z5+zAX1LSUzx3XfEXYV-YChPSDh6rvM*(gXfoUmMu(wpIHZRPZC^v4O??4EyLKE+oY zOti8=GA0hnL~V)iCtK@hb5RAbxK0S6NvDyb>J}V@Ea_|!IA${0pi@0afTj(UXp{Xo zFOH?!kf|}w2KnG0;Htp~c{a?Tk>JJmnv9l&Fe!ml_b?RmIp2K35h7vQ4Zrb3`fm&= zYnge#Y(m^A30K+$DVJF*dD1Yv6q)fnP9##Gk)gVia+W^&L^vGiDvgJIQC*7$hCN=< zLp-9#ctg{2>>w{_uLm@5){C+o4t`xF&YPrff=v^wgAdrLmaGD7*HMcta(_Hy^wP^g z$2x~|wD*qH#8O8a@~P#n9~eHz_~Y>jJ;fx82^F`NjRjlv0z4+?+0$nO3XfrW1oxwQ zfE;73xy38JS-|f}3Z(I5hWP{LKe+`B{7;?!!4lA2_>Y+TT{jgTCYfbmsilW+RwenZ zCpU`?BuS-g;uy5vvnw4(SI~jnv$*cN5_RyCE|!pvrXVYly`2WSK=i87jqM9*?5{uhWoBKv0nmvrug-+i}k*B5p3 z_}#C@&YxVfuW8kywL8~ri>`CI?P|NCt~l=m{aHF|85(XZI`9PQx6*_ zXmgcoveMr6OWMs|>5jB+X4iD?DMMVkbr+yn*NtgCHgJ@>+qlMlE2_K&=+AZYt-bJ@ z=vysbv)j7u?2&hOO}6Ta6~VzTTl_V5ZSh#S+z{5f>>EA)obBzt?i&UyueZQwyYVYA zS{xzHDFoKO+Y|Iv*JN!sHQk?{xTrc;<#JDteb!|3vm5Vd4t6Bu;|f7feEy7%Q$IG@ z_VsI8XbaHWo?fBrYTPy}=qW50aR|&{k)msD-_lA1`WM1HAU;id^dz1zFPtf$oS?!f z{Elw{OSapUL~XQlTjN|=wb$DK`Uuq0Wi5~aN(AbcdxkLUyG_k7eyw}E$|_z7f5R$_ zEj@orN2qJ2gfAOc*=It;&Sl>K4loelnc`14U*p!UX+YbAImP)D6mE76or(6mq2-UC zc1SgLfckEeb+|~g$)1En5)w){Q5f(5K6|Fu-8>5U(+XQ#tg)nhF51ocG;BdgIb4sx!tZeCln}rod`8C12U^Shk!^P+t@{!LZNJ(?jwjS@99A*( zQ&%uXn7i%T>0!4$@#8LQ>2DQaVZ^vqK?_!dLOxNpbw=T84+tw)&f|qfBu;Pdndzqf zX_JtiU@K51turHX2|%?QA{VA?B^1(It!){^yxG6S}_3Q{{TAn2{EQ{3d1WH8w-b>Gslpcw=bkUPXd zL4OGQG2ZU$EhDX19g)J27IH3|t>^*fK*Cc8l}G1p`pO(0W5nEep~#uGh{>{&0O}t% z{Kz(|`Zbvk!~~5|N!QjaWuVgu+0dCVpJ+q6>S|SQcf_280VPFRBr6~RjK5!^B)y45 zPaKiVBFk*MCsN+hnkz#q6W>%(G%Udq^=j2OFfe2@vcm+Kb>6Q!F^$N2A`;GfQpdnK zy#--X&=N2qAt72v;6DppvE|3Ay{~}PU6;{$hNAV^iMorI0fZ=qJ%T|}EavMCiAa$V zYK=Li*P7VNgGxIr^kjjEvW7sT-!17%tFBNit1Use15+?G_Gz59;-)k0qOWghKs|A1;3;wFMbMG44{MF~^GI@*3UL^GQ+uXla3 z!@)tH#D+~P<~|cm-KvDoYgW%NI+!(|>LM4CB#lP6w>gNkk%v7%V(Kx5#HtSmU*QOb zwFq6lBiTwf{{%v#C&=blEtGap( zSc0dZM6P;=Ev}lFqGNdgQiKpE)=cC$2nMoJ8&@Bsyjy<|*H==b|42ADy(qWuf3!PQ>8ex{OX0^xf;S zperb|XO-(>Qqx95-^#FOGgZ+-Bj{m zs8esLXpM*7+IdEs=_BG9<~dz?%PPiBNaarGsfoVe+6iCDBpNjnf@}|JNM#(sOTKbe z75|e(d=r^XL?zQA0-GSpd(Lcs!l25t9kU&N*kw)U?*Z=^Vi}_Itli*>L}5atN9!UQ z2UEo`VZBmy!Uin-xXO?TxDu;GB|V+HFIhh@a0O_+EP)9HJW*m3g~`A^D7;_A)*%0D zvSj}X4`So=DMrT3PF6LoMbF@O6Geq96ky`6D(tD3oh;9*8?~eK&jHEc?(C7+^$^cF zlL>nXq90i+WaOvdr@ALLPq6pQGLQv`*vNG45S#=@>p+^M9q7lhU}}T!R8(u+hGUT^ z1jq7e#ZSaLB4!{l{Rea#{f}GqoyI)C^TrEF8;m&o6^3~6l!=9oJG<3$f)Ggampv)N zAVPB zSmxQ4Ww;5#(UTjKJNOhluU0}h0?P(hVS8QLfcVv|;ah-3r5>nwDTIYjY%^4Zu8fUW zxz~*KA`~BNDU$`oLkhJ6FmmItU^}v3TiD$k-VkG2Lr(%fVMb`Siu*h{*?@mR7Mg?H zCL4KALSwcQ{er8yVtf6%hRM-RjQT`u9SG%#yQ`O%$&OXwH7YH%Eh@+A8a4z1Tx(M0 z!6|}&5q6evMc6x%TgCeaKf`o%N#c$vmR>gMRg#`5!76Q}htL-aHIdokA+yDY4!nSE zSy&2Ic@17S3TCfX1X6a{NF5dKSuZ9852FBItU34mMOD}htWYP-7Nphu>{BRk+)!b$j-(unhgZ+JxtZC z$?d`;Exw*0Qut8>a>0Ki_q2c=CdMvS$YE0u^khnAV9olGbRPZeA0U^l&i9C3Og$=B znOKQoqgKp2mh^%oX8ToNW+B?P1sY1S{`%|@!H`P8Gla-s3G%|G+uDQh4NMw z7_^t?h>IfI3b&CE=;C`= z)5mO>LNDt^;(R&46G1Z&C&h40CMrczS(;H`EO98sH|r1tl!%2A&syM&XJs*c&VXUH zbTf|0);`n}k17Pu_#|1mW5>~OzM?MG<%@O$UH!-U8D8rY>M*aJ5W43%zwE2ljMN1Qe%oX$UKHxi@7| zA*|N+%YYhh6$Ed|Zfd+bMK*<)atkXY&pKv~ZC`mOmfc+;g*9ReOESrv z(@=m(VMwxQgxz+4Ue6%M@G2qxl!k)Y??bsw%K^2l6w+{OIIl#;1waR6_`cjXH%a zy28ydZAcKaZ$peF1#)z0Mvp3~DGuomFhMNA7qoPntXxl(zxh`ZgCN`)jUfV*DKj#k zO+uqdz zR{a)!04)4Lr3h#Q7CYWBf}BXBEMVy5h;mwc&*mcC5*zV@dXTq>!LT}c^F0T7Ti6bS za^#U)M0Nq-j~9?5KJW2a@*d)p-wDHh0D5MmtzKU;$cO`D)hlJk>w0i`P^Cm7OXz8` zb@QP1VKMm-GhN1NQ`Ket2xUUPWyWdDh@&JZL~L)@eM#E9WV)dPKPq?V^Kgd92z&F! ztDrB7+nYpibO`R#nmM_(9CNcjd~e?q8n#P%{mnoA@e_E;v?O@pQgI;GlZYo(4e4_v zaexNR_4qTivEo4(jHSX*R!T1TIRheaSj}0|rLjjr#J?;^hm5xw;LQ5{>@Z})7RKOJ z_<(|V5vXLgHmiaOMn&=w^~gjVGUc@yv#6*-O^mNfWYBODE~4pFWw9djjssgY!%%(d zX0cx=HaY#;RgkXiM3P8{@tvBuY@1Al-6f~m2$d1nzEWiB$pSVHF3lXrCg0xQE)u@| z-PdPbN(&BQp=VZ%O&Y@fa9ARr_f6 zI2s-qvb<+wXQYgkBQ^g|T=b)`Tz!e@}_+ zleg{EGX6bHw2!DRjr1QFs%mYF17pGGF4R&Syri)g9~4We){6*df{Ea-_HV~1R?}Qg z(=>dcZK}wAkqljS&c>@m{h;hugs^=QWIRfgp~{C)#mf{L*U>_dj0@PFP+~>imD6nC z@_)4#XWRMxP!xB}oe<7QHMQ`AJgd0kWq$UivU-IaJJQog&@=r593qcsTp(7MoM4=Z z9&~jcYlOTF%PN9|UlTmxBodco{_ws(l;jK%OQh#uD~cOVd{MWpYkeg_7IS2y1-Qbb z^F;|F0;VZHj#Pb+7D5}jppwKh6W!EH4~9BbAL^KC6(xJ4Q@ygstjSB%oxs+XBBs5B z;5nqo1yuvb1%d^36u&?8!BSj$Z#5~L%=VhIj}aOn&Wc={0)(iw6LC>3F5^m#))C+$ z&wa6%4|7?YpQH35Y`n?Z7g8;RDVA4aJxlU~S2>w@c?;ZVd}*M(#yjbOLS$OY64>t4 zolfr}I-8f^Q-l}CG;bh_n6ZxT<&%#IH)?GFdv4Y*!6|g zUP+pW4W~if(ZJAQ`955M09$fY-)BgDSVzo_Eix<6Cm}jNg~JGE7pdu|y6m^2)kxCR z4G|&!Hb_T$Qy@t(S7wquK7yc;w=F!R5k-9Xu1&JkQROS-#)nYFG-D+zRapeUz;LP$ z)_gb$Z2-LF%bum+jc~ah9I`&z~s9G*_9F5hg4NIh0xtG4~m5 z1-}r}&QGaMy(VKt)#(SioD;fq7$`XsFb*0}gpr9FuqDY1?#9GO0-Zv6I^Q+5(qQOXV)my0^V4CF^Z7c^QtVls# zRPFy?&zy-+Yv8|EVg*huOMS8SNDq_g0DeLJ)!t=|gl>~ZL~|zbCvQydL|ZjdVl9|S zj7$kENl+skh0F=0caXzSC(~&nN1Y5l zPLI=r9E1`6*Xd#EGo);}s}|qZQVpO{BVxU_uC(Ap65xJM_)=+3L+zOu{R(C=Qx!^* zV_B=Z2QN`SOzzBASV#tvsiUgZ1&J1%(sLqRhu&p+bz0GpT-YH!YK7#JMy-wlnA&hz ziBi^#&5NS-K_v+k*Q-_5vSlXjC{pUkU?8v{6_)T(v5Jo?rzPyi!51AGm?!v=auRuB z=?%o6meDRc!T3w{#+aSujuicpnc+A+Q)!8$(%HCXU+&{vOiRIFj8Z4J{^EtJMC!QG+^)URBLs8lu z(C+jkDGB3A<)5&30UUCOO3409B10k%Sl$8cF`BbK=QUV(sIY6(E6_r&B=Vi;UvbFc zA{F9waiM;U4x?`Pk1iM#$tl~6SCvw07<7fC3{PU0P<4$r-&vC(>bIt3nevSW?@~=?x z=P}Id0P>=ec!4AW9T$}d?v7-_hq*y4p%glZ1=7n)C(?G!3PZ0+v(>&vB65v}Z`9XL z#6+&Hgm?(7U5Jd5;9~+H`~)N^(y&LN85kTm#7mB|7?@*_d?zUw7Z(n*-?6P&OBEfFS12PQB3+eP+a@g^(@|K$=Z*W&+c4VP z$|0CRGH$Pk1vjHd4SHcblyXJx4+Qa9voSgEBojb@bDN1&*)d?KFbZbmQM8XB_m`Ed z!2yG$9mGVt07^aYVNDCF5Je6f2)KR4OS3J2-_U+quE-e(wkAMh9MqJsnbQJBk0E~+ zu7qDm-HtfO3zk&;Z(kmivRG0fxTRy&c{>GdtXz)7z*PIdv|`_c!&y_wnwR=8><;jHg-kTLm2cG z%TYCaV?Y@!&O*z0DT#*ET1pqSRYb&qgo@-`Qkui5&81^Ef0z#f>J z(W(PcnTBG$&{vNS+eun4t;$(zH?6pVDRyIRP-5*%=N9>9A&DvKF!Ng_oTwEe>ohtV zn|{~^7~CexTC}n{(Q(1F)B38N>-wVG2fNgW2hF7_XGg=ELkL?|j`R~*!vCWM$Icv8 zVRG(aeA^?HLy1e5`n^VceS3<>(1PD<0x6AXhe!{{bPPEMl`1yebyZ6OII#oi?T(wl zBq;=AZ=dqkh$gk^S}gAY#<_4Wl$)i&RKs+q3ySWzyZLk;%9c|26F=(I(GM&1GBaAj zb6L8acj;j}ayh!s9x*}pJA{y>HbQeh%(Ibg0v?*=D0)kQuBJ*#<^Vl`nu|YM6pA7^ zK$-DAftvfdm2s@9%T`U@w&_AzqVWPRh*UUv&)yp5fLxmQJjy)MnZz%Ln26Z2 z#0P%N2Vsr}4rqSFj%tQa7Yv(m;hg%eyu0@tNl5!%LA0Si17JcFX)~sYzCv1r6YmI6 zYVm`5T8yoqFYGMlWgeUls7{jK#sml2!5WvA-hjrnUiu)s>D$ggvs#ZM9Tn09$n}Pm zlct}>d9@c)ZWrb_5VCWo87R#)`LYSgiTQK~n~@|9q-I2#++w>ina$EnlNPI|fviC- zM(72Xgp8c}G z@RLd&VUkGuf;lJ;LGhd@Ab;%RCpcLUBf+X;r7<{0rgH8rIoSmQ&MV^nE#kyV17uC-S2E7L-PG?q#|MBb(II8r%IFHE&4zZXM z;6qNRP@HP+SmPq>F%5H4!@4_8ANo-b9Pd3^_Yrr~x~&e^$ek#@^vw_C@%`B2yn_st z@kMZ`V;&)1dr13bOGTOzxMfyf?`;v#D9v(kM~-T#NMPYzd(Kq zIWKOLDZCL8Vp6`G*#~OIMGmcf^LG}eZ+;rX@D?_vPeR;PsI}qy+8y{Od$hPt#nsN0xSwX#|=U{nc;%paPgno4n_7630L@B}d z8t7oU$;YA!&TXVw1wVG&o|sYLha-fu#Gw@$t4lES%x!yZ5;og=$mAH5x!vHW*#cr4Rn;}*%;0!Z z$EfZHrxCVFYj{GSn8wG&xl~J#@zchA*D0kT;k~T}Wp(i+$p907Wg40F$lniZJRqIh z!Ex=G@3I|TqgLLW3m3|)Esj3$T!5{rZ3ZW?+DFFd+T*&~r_ZV8l#-#N>xO1yGLUMn zVApUlemIJN&>bLBZdAlJt~Ozvjqn87caUon4b7WS3d-gU&aRw@_<_$6h05C)VE4*( zSOA!F?UFh@Ub`1kYA;?-rUK$vSyG~)?Wvl#Oq@k;aAx+%L88D`Ju4UBxa$nI$-y+b z0G|m9+GIhtETj+s-e0Uyme!U*O>}DdCHnuEVu?gKJb99oPy1<;*?GgvJl;44HT*WN zVQ+h4yeBQ#IR$?jqhN5w%sl}51>b_}IPd(j!HI6_^gW>&;3wBV7O5zb|-3Rxhg^rOp3u<(8+R)7~KpD1V zOb1FjECb}BS}OI>*a=BR-GQe4W?-M)_{cL)9M8l zC0q_31~86Wp`1im5ZKD-Z@bMvY`u}t$H$~7jE#5HKg!5McE69Bh@D!(jR`X&CGVPq zs>cSPIU%eUxe^!) zOorgEPUL_!#eP{pg)iR-8}LDKnnP~B@OmXY%xiH(F6^Ca=|z`bbwUBtr0vcAH`!0@ zm)2oINadb~16qAmf(cr7P`wOm1~~i21xNpO+`0r$Lyb{QAAXdMwo$u;uboVjBQm>e zNDN#knM=8f2b2URcliw}l4AGctXh%W%%qe%OiMW|Ipk6VDg;?f06(< z30MjbhBvX057gnq23f#T(!dS#g0rQA(L-qgG9e#^2}>KeMF7v z_w_STG{zSbq4~lWeu;-u1<7-w*J_wm8E988r`p88J@hYwGcnah+-a~DB;u^MB7B}q_Ys)kj!jdJ}km-!ES!E}P;=y@}`Jahc{Aorg z9ovUl0=;hxislnA%v4FPQ`~*nxHs0Jb=0#uxOO$zTZG{Es|{C zzv79EId@S?7>1La&c#8%Ts@A&MRO|*&kPn9na^bpxU@@g+*ffjQI{Sekn0JU zV9uEF-?^y28x1DbmcO9t-Fl6Zs{^xH{cT8|BQ8|yGjG~v7;>MZmVBsu?t4FFNDoFs zs8eXt{UgU>x^7eNvr;oOaB@q3|%WbkAhhLfUQvs938gI^m`JT6bBaU}WF zWZ`KnS}J9}X|_@$hi(UoR7`#U+o{(S7@o&z!Q{r-;3i|Pg1H-7!p&jH^m8i3fm<3?@6H^(!eVpC=PR(>P6P&Bo2Iw zg%v)$s&hu6cY>T|9uuTV$gbr_Lc!}GRKTf*k@lQRduN0?F~TyAYlQIwn0nZhIi_j_ zsqP%V*ifp_vETf8aer64ZX(w<%x9m^$lVt z&Pr!M%n{R(b@N!Lie%uUI35nOLd8enf)qv3(p4moLTq(J5MhLS5`_tq%g%lbJ4{oF zWd4FgIxHgy^RfNtX;Y?dQ}BplD*z@Q{%cGuesE!k%xESh4Ck>j-4%+#hX6*TWpxTl zQnG9g!rNl=G-7}Gn2P`=jf2N}E6wmt(LTXpO-nrrv~!Z0krHklG_6TdqYK#3bEk0^ zSh7M6W#OJ&EW%K(n<5R^_L&r9iP=oYtlVHsDwJzEEUF_trWY-;Etv+8I>_w1p@Ovc zz7{>9p78X*b>{YU&L19=Lu?kL)xoY+=k?eConWAa(L%o3H^AlN~xPY zcg7wYktcaN8TLn~OR9&YGJF~)zO-ZaMQrOf4)u!j7Q`}$j?Sn&DOfMD1Zh}~dfE%Q z7&={J(j3pxG1Fs@Ue4OrpJAB}E z%56z0`HTfVPbWQ;3d2skfk+EQv|7A#CiQa#kM_+&cu7Y6kh9)xLZ-gY3@A%tlLbH%kV zg=AYWkx{ywR!)Yndz$I=2i3t=Z+^JIaI5?986&#y8?4yA;xBXM_14>Oqj`T4qR4el znLYFVA&;}`c`}ToGZOnT0()+?iVcMid9ey*`F@LIDYw0!Z%2rPy2ZXBHgx*ki)enz zXB)hr8LIfU*6{|uwX7|+ec5IHJ26tJcQBc25EAesQhcWvA{Bh}_?oJhgSdPe{!sw& z4Kp5oG7c4anF!Q!059gmeRrLFy9%~0dGr{e;{7-_fkF#ruy_)lrMF1HY!j7lMx~hA zrJ|p!lUQIt8-M0UR|+4x7+@*5;GAe!H(+9^&h#~E!{)Jcn$epdFHq4PtxSBEVg?Vg ze(CcR|Eog4U{H=nq*UKjB4I?vL4Lw(qrsOav_AFdXEj~~-)cLV*k~rs<%qLh)8PxF z4>)LV%<&RrPQ|B1`hy7+T&NRT=njOe@pZMn*80%g{|l#^nvi(=k?Vn0u9336WSD>O zx`K-(MFa`7iWjXPm+mT|m%m-uJ4ZK6aR2VoZ!e9)BS6!sZxKpms}3M*bT$3J7BY-! z!I(exsV^2XpCO$C!0CT^z#-vyunx|L3T;2G4-=AhMN@=sUaeXTQ7?9G0HqWW;)NU? zB_)4DM?Po5TZE4qiA@Z2CwV4Mo`f5jjLOCXsHvx;<6is|Rgb}fq4LXw;&QD4_10i0 zA(j`09bF?_nGvog8rDSh1f{K%yYhE!5>>@JD%ocQF9I^4dX%0p-Zgz`Cv zF$A6xVxV+DHzv}}VPRab*#%Fqc83`Crrv21isfUgP3^N3ei4p81^CP{_!7GIsy&79 z-~x$a{@BYG7U?f=Ggv0bC#%ep8-CC11=7b7TDb4)`LXn?R)ZW8vV-l)ZX9DFBkX+{hjEM2;fRkb$w4~#nsX;bP+qFJH`qiPPs?( znHT{Cczvw51Pg!Hwfi$nkWMQpuRw>+ZyOfj&8<>RdSpuLv zM!1OoF9xcvls&7@j&V$@vX)LzG`9S~-$-Z;Vp_|#F3~^FgOkPlfQ?=}6=eeZk(fzh zgT+G9L2poOEEpmpwmVA(^q~CA@<)qF+mFY(9{G>xlJEJ%7YO5jALYAFGe(h=b3ORd z@~=>N*JP|J0Bw>%((n4PFYp|{O6;{A0~@Rx-GbfC@Hg-@3pjpFJc}M+unZq8@oS~VAD+CENfWA?rL`GB);IM=dAlM+m%0M3)@tQ>z_a;zwc+fCFg(D zayyYOy=)4%Mg}Dd!-+INe(|eLCGu9*+2y(YC#KW=N;~ipwQt}1RrY<*@3s1}jlp&Q z623P+xa6Yq{-CBDq;KeWM)5@}Ico?0g*N=0w&ADVjX4M{#5xLTKRO$>o1PzfHW$Yc z5YHLDU9~}a!8hr!H!hYvU8hW?g+J=eHE0lTvb_y`wV0s6Yx&@36jsu9HMYD6xu*;- z8x5~}C!Qcyc*zgq@=2u@+SLQ8Ws@|vV_C%romp}Rb{OTeuH)iOl zJ6M>z68lJGa=_obg0OSwiVmTKx9N7PS1BNbe1w)Z`yJwqoG-NDGlU*Kh9SJI1n56k zpSC&=C&Hcxcv^e2W=kYwwxiJWTbnIk0Y+JP$M&tN_uoD*;`n3zjspYKbFjfq$YLV8 zSk`|`LgA!>%*Cdtwd7qLF?PL2NrMKg<7W{3C;eP=&P82ta7l8=_trS=g%LqeA+qruB4b~_=URFfxGePZQ@e%=UghW!xv;t2>M8^ zTRJX`#6)^QGCOP+zfA4_&?emyiDb@DBG%N%WIFw@*IwK0WbiL{c7jP68zP{&wn!jB zC|f$YNF6kU(M0QeMCzb>DgB!JZpggKHlvsSAdW!6oNIU;(MqQa#?;PUxT4K z+;fBaVQ$Dm5zU|6#q(zWny~!3eB6~6n{Hdu<@`TTO9KQH0000802(|^TU>t9=aw-5 z06L!l03QGV0AyujZDDRLFfKDLG%jRmb960fZf0*UO+{2eL_t(R&3)Z++(?e@eLn?5 z7spmaS>5iQ>Dh5lbVOMyw`)h1w36Ij-yN!$C2`FnYe|;m+NVGG0AwbTMarJNI*OQ> zwnQe8022Q|esFzW8HPi*W&x9&350G_YZyc^7NP=%HFlkWjD>bf67~z zy=!)L!JC_9%X)Q^{o|qUx80i`e_VI#lcv4@@%Ome;?cGHhQ3yI+19n2vhQ7y^$lTZ zyU+5v$cnP-+wyMLyNsT_qlGv4W9hoMO0&-T2U?*LdO_u$BSk7 zYvH=`zIH`ekFek8U+A@`e4p)`UCV$M%?5wz9{h@oR!4|;3R#xD-GjUPHt+gbT<)8g zDeKlaQ!~|=Ae;H3XZL_&=^UbSQwBQC?>2~xAk-v4DyrieF z>>H<3XakJwu$c!~o2nn_afbYhDq5v#T*y6+azqt!YL%>eGX(vnN8_ z*5zLSE-)70o#9WwL+jSAZE>~;(@LuuPFPrK+tS_%rc3(lI95_F%o39I4|xxe!*UP# zqp+M|Nh2XdFdV>}y;OE-@6+Q(D{ZoJjZb7xW%qDA3!kLDtsEqGM;KdS>B411KOpwr z^(;)*32aZqxAbeX1fDg=B!EN9fNKcWl|YNd>a2E8VKIh3(h2Z~Wxv$TQ&_)fur~7` z(P2V9G+3&4t6u2O^1EXy)=qG~ZQUbDOyM*f5W;QY>OE*{H!0te1+b62`=XDqbl9~m zj&jTUm8VWvJ`txhU>rOMB8CN5uI=ee61uxB9pLh=tjZolRI-dx*J5)9Fv_*%a5`i~ zxd!XMIb4^(#P5I%%Sbqqy!TNIzN2rrulZ(M(f6K%tlNIl z)%h6P(n0MXvi^yE+MIL@N?q!nY8lDvTnX z$nH|UuG~EdEw)ZNHj>mlnOP2?Wq)Mpu8cRyMS!LVyMDp&sOWy-x668k(Lg9zk-Tq7 zj=Qhh3d+kIMWJZx#H=9!)IxXenYDOVi4;5$0Pcv*MZNLIqn`0Vbi_09^R4^uUD-ON z5AUxekr5Q`og*e|ztFGp2$~4JVkTT6V3w?R$b8y>grsI+h|$}5uQ)6+(j{aIHoK0t zYc-%ihT{b>Rni0D`=fZiX*P`Hay6n|Ak}35S-zqNSRxQ!d#HZGPrIwbQit0e2p_yy z7R*vNtlQI(+0fdnxDuh?wRIF-cQ}I0YPD-&n90Y`Dg|W~@L-i$0cKSTG2F1uVJVfhz)2MHw9SM7-3oZuZ()#Na?-iIF-g zY04V#&oT!G%c~H2 z2<9Vyx_=30VbYr#UpRy(j>M7LxSX&P?15KRNhGfFC)H=-BGHFaUpw0L@N8!)8tDkQ zO$nrv2!XOE<9Bj@F1r%K+U>!Y3$WxY;o;M%?@p&hxuT3R!S6Q97C8Z-k4i+LD2U}c zR=wE~nJP#0)RDg6x}${jN70x(BFOjPMk~W`5b>OstDP=J~l}tNUi*y#Lpp}3T8w07RA0`t(lYG zihngbBfbpwpIG*@fE3?{*a?b*;OrcZ1ZhwDXH_y+TOYfu*PyCnl^_hx@_5BhMq1OM zBz5%_HUa&gTkU#{*+GUy14@-d7L08?1&6DA%*7J}?XPtIU@WqzsvQ{-K|Y4X5;^&j zkwMEU8WLf7Jo5~fKjvk{po^0hvBlC!91LRY4mK#Mmzur+*N}+~yW~;aP&%?tuBs*} zNH~3T!(#^d0U4#%SlA-VGFJmE{glx`t#^Bwda!UcvLRk8Ve13iW;LNTXXiusB4_&o z1_Nx`sB$9>YS#le;T(7%J^3FU{3;H^iIb!uSJ@9?aQuwa@%xWXKFq%$3)+EBl8C9-kN@B}|If5HYhI&688+2})AUSV`B z^o0t^TKt`;c)Y@q9k8oUYPobzO3)B&kBVs-^(6q0bR&zU69bIEkgz9jo*0O{qe7I< zmeozvvGJ^v#MsRwm?9J+LLy!#B9K?yILE;e2=eVharV;HX#;*2aMCY4^ zo{y0fMh|NY6^DMQX zFY{x`a_}mJM^;UQCHbZFHS!vXLQ!fT^8X@aoszh1>X6O~&NZZJG_B}E{CHT%yvDNV z?wB-n`v?V5i_I$vLtV7E0S!7B)E~eB5k;(waHx2C*&zcmp#@lDp@nx;H+KgLRTZh| z&=`6Lo)C$Q2JHZnqVJov^&l-$hiowG#uB}}KRaO@y^dwXw zs3SLmEPxRD5l&uZV7r`+6nQwVRf3K7$FN)k0iuvnu4;FXdVClFPFcCuyN82tMNG2E z^K@#X_>et0Qe#p6LD9j-(V(HWL8xo?JbRBz(Rcc9>H>n ztSJ+&1^$SOL=7D?;8@juoO2(I53NR2$bn+~lkDI2NgRsvkWHl_glKmW3%zfi5EYrh zEGQZ$2g#ptzx<2nfepn36JZ#<=$l5IDE$MSu*ld+y3r?f{Ml$VDqx%d!ang9kj|;K z!4@C|BHF?tsW^~boD4D-V{|vtT1_Nlcm+Q`TbC7EtzEN%#Z!owQ0mJc2bnb`lccWP z@Gwgrw;(mHXq9aLzRovDP**XNP?a^p%iZoSpw3%Q!K?F!k`ZsCy&ArbHorGBkToS? zuz~f4`VKZqcC`ui3(jphN*)%Mh{8JS)L5;nop6m zCeoh6bN0s#q4I_Uyo?C(#t&giJ{$R*KmV`6nu?5}yq0*QQ6$Jy{{%ZI}M*&Ee}omp*rkSMzHi962Az&fX0KQ`j5dfQs~8Bx)~ z3LGDc!)qJY`w+i}2MHXqwcuyuWOX5k$7!uyU6q^Cu9fDRyi7cd5@?WvoK^D2gxd6Q z&BlWPEXE!<0HXiC+o$MatDqDHkrijif;niCE|IHj0&wuS?03ETRPiIjeUm{`H&2x- z?w#y7Eaub5>ujBuk`sqVzzmCX`6roeih5fI#*1Bq)-A zYci-slhiJVvlQYYmc(QdRIopDrJ=9oVc4G#rPWds8|xw_R9u27I&w_VJNJ-R>zT?G{*}bM2)8j5A~AS?He&(8dzRFLNl0zvX!-z> z#DJwh$$bNs^_5?M0+Q{Y!_;%Gd^e zIf0z>dCrfL=ait&wFr)PKY;9`<{nDb#3phstBANYK}`}%*?L!zRa7xw5`ymaRO<6^evR?_`n6ZhpO#mb zM!@w**3sHUv+X(Noc;V0>Pp?_j+S}-_rL#&GadCSDH6EUoZ#>*=#Pe`=s3cjpi6VV z!@Z5`Uakz2gU`|{mHkr7@{|K{K#JB*5HqmHNQdvPG&*w#MuO`A#~`G~7F_bGjS&dp zL{X5N(XL8nOjR`_W&$6gP!ZG&xZZ9p*D9Ez>{b;r^4K94FZQacS&@6kkgdq^)SGp; zIbI;X75&=P&=MSEAXNqDY0M>I8*nB z{ORj)PbHB2oF-Oo8I z$A0PQ#%Gbfm6ZyNAXS+7JlBSTf9pp78_GG7!4eaOSfY3Lx}MZ=b-V4hf$(l)AUzgS z#ag>j(Ee(f`h&Nz)x-Joxk94>ciW8JfkUCz+%O<~5bG(`l22aNn4u2_+f(92nltBF zO)t+fe;GR=njUwWrey}{tfGH2wqrv6F*_X+6^9YEB82Zvkbd+KgsRNa_&N9~H9n~Y zcR3e0KcbYDyz+5+W!c^T3I?t|O4bR*)MM_WoYcgz<8vv`b^}`wKY#PCk47ChHmbXp z6nmEWu69`!y$C^*?i?GThVc&*GjEPK-R2tyNZ(eE@5QC@ZVdG0! zh|X!#b*}RjSb5B~Pb|che!Va5645oYa${gR1u2*ETp=VN^~B6Q^U{xZW7VlT?pnpq zQIFYx}A$fwxvm`j`#Zq<%CpEEBz!u0(*|aTd;3YWVP_m~|Uu zR-sQr7A-DTau5^66WL=^?KYzINg_5a8qu0IEr0N)lqr#k!tE%(zsJErJxS?z5K+j; z={r+x6HO-Fi8Wn|XezT&B2~BqP-3_?2j6+o4T7I{jzkntrqSS7&!oW=acq&IuGz&O zBez8u3_riqm>XE-QkHH+0_3B$!VYULqRr(O9OcDdt?h9^#-N%V?})hHX>+6jzLCUD zunaqsEH@`CN`d#ZY4raEZt_$}w}>y*Vy(|I)^=i* zlf6PF7|t*1a`!H8C6!_xNi7Q#clE~qR9@)U^ z*)&oOrBPBk`dw3NA)gfZco^VY+Z^V=K8#V|;Lb32qBSSBWvCnYJ*NyVQ|>F$q|$-8 zt8UcgoVJ|xycq3~Mkg%kwKC2SdYaF*a{p1Ix1)fM+0J=|mJI0|FFPMhl>|+*TIC&E z!V=a%00Egx1iGZ+-^M!d4&})^Z>axzH*J4T2%SewqhAht=Y+1uV)6^dW7f!U(RVSBdf1Yo%mH*8T6y@F0t9nZvf6sr zN>p9AOcZpmN7dm=TKWDJz8*wa3>u|{g$rgFvVjNbKar|{?%<5fCRsB*_);-0;-&6hfdpUoUCYe$3}P2PDImr$$?{NUdKjfJ zg(R#Fg9?oGHXTleRL68llwsV4%rZB5I(Hl|-#XX6>YG>ie<|w?rQn_-$$HpXSxbCM z3Jx3>U)?jSqjAtH;OM zl-Sb*>&*t>?;-hU!*Hc$TrFZk9B=$+<1i-0MI^n0;J7;|%qi0B>46A;raTnMGx+7O zDUKLaW5IB(dAHk$6UmQwhc;BE#@HfeX)tz(yd02$r|#&C+rmUtHd;HwKTNhoz99=F zTKx?U5Iy(MRE1A+chM5?l`o;BhmDr1&=y3}PDG_V%>mWi=2f7&9_8k6{^)&pA7!oQ zpEsWeE34G9@B|5_O`zG`_3K^5^{rjZ8WKsH>QU&j&L0~tduK~6zxUm?w$gPF>g}UC z<{Dd55(_tXmK`OCO!|l-J@#9+$s4JvCUyE4bI19E15dl5Vws=bu|NuO3wpxeUwat9eLuK zwKx!E+B#$UG1yHoc8VRm8bKwpGT}o4{;uXvu#JS@(08@GJ;@ldwFO?~Aisovoff_a z-0B|34(riU>Ln$xvSf+M|Lw~&?NK(B;~Uy`QBODmaM5_)7o2#9 zGQv$h(S)#EC&~5tK*ZZrg;!?k*#nHU9KcpZu662&xc!zYu}0H)Wzqp|eD6>kA?85t zKW!v|R?@9)mLN6(CpJ>T6(|$b&LN}QgUPJ*uv^#f%6=d0|EOj;!osC}FGs`sDa0Kc z1@tfSK>kM?Wg=jss#@@p=7~J=Ib?q4Z2ThzD8;;*I@1`Lw9^E!%?yReBgYt{{9BWG zAc`A2Qa4g~V(e^MrROGdN%q2s-jAK-0nZC}X~Ht03>R#N@?o1yFhN+92+9XU7hYd} zIG!vcwfi*w@f3;Z)}pBpa4N6p*=2 zicwiBv-*mqDT?1B>|YtNiL zky4X<;=Ph2pls_WMh~rYAoC(2)+WA$Id^`X9ATOVNon44*vm5A$Pxwepg>w<7YvP$ z6p*sL-yrj zlKb}QNH*Jz)RNjdX`YW`TgZ)(W?0*-Wdp8BEt)y$I$eojk=XS?uQRHZS;Oa}dx=Mr zGfTcp%l34HdJr9N(e_>vu&WjG!-h>nVZmKB-;#9n2$uYWynC2((*9>QIVhV$QM4#r zkCySBeRmI$(kCzkN zrpWeD-q>HF`QGMf02aLur(I{9iU0WIH$LcSIysKXJr3y^Sv?93j}H~$KGWJ1jlZ!6 zJf34t>#Vt~z>ogo{KshBTm0%oU2P9`;LcEA;_L@b`Z+kmyq`A7YLT<8^P`=NY#?v} zch{*y6P0j;6_EtjCdJh22(@DrPEd=GLf-gHm9viuBZf7mM5f);m zzAa}MFdPkziztc^cR{#}PL8tk67k3pTs7F?6Ik(7HV6HH4h1kGVW z7enRUx>%py`q=qF)kd0!C$5}Yaf>D{f4A*Mo2GjU|_!4sQ>IOD#o zLl7jMk-_%j#F2^FW_~DE*lQeiHi$tRj6U&rfX+n8Ll{s1%fq8%#WL~Lauyt&iq zp<+^ip?s_iK-T$2{zJ-;cvjjNB}2@|Z)h3-cg*99g(yglg1HA()Gax&K!?=3t9!}m zyv=~O@uWaGi?2x#LM?N~Yc~zg+Hy7uaW-{QP91k{2I_eh7-TX$G52YW8?;cDpeK9j zr;H5PB3ZEPk40L@>|2g{@jR%FnmG)f@x}&D!+J=odT*as%R)*sN7oO-SjZr4xRYy; zZow}{5j5FdC(?URoII?;V_OGdGP1iNr;juhW>lcI9I*Ka9I2#0+{)*ELx)IAXnA8~ zya2r5no)Iry>(PdOPYAUxeB!B%0G>PlTFn$=Mq%#jzOM1v%#pK)E*JSIqq7A&37=J zI>2iJVm_H@7G_IL3Yickh`lz_qMG#hjG5lXei~D(k=av>j1&vmc_L=Uc3LrnH^+7X zKR&v#4crGh_>WrfIJK|mVr0A>aYqg|^^ET~OL$7guEN?1Mb@DJ*)})NxO9$C#grQa zvkneMI$KN*pf1B924zoF9ITi_!%_#y$;7dl^x5VI1+wPcHp7!V^MZzG zaO!Q|Dpa1d{`I^wPF?IpkQfw>g3U)NS~QMD%5ro+|8~L-YFSI1Z0r$lDs$g^=fgqL165!r=E|E3}iypbg;W^vw1l5xcL)Lv`;>Q7=1bsFRmb>l_9e zHR*fxY$E(&#=X>cm{ifwV9z)P%JDA=v>d18@kK8o6T^XJa$c)Z`# z78QWy#vRTF4@zHUCVgs>1O0rFy-pJee!ofTq}zy1eXE@oD#XBFKvEGJr#8&%73qUq zT9(2Tzvr@{$F-_iGt!ktL%2(H41p{T6X0@wMlRvxfv2Ip8aj;k}0 z(VVt%1{z5uMA|O%oaU6CtHC%4rxS1_0wDt>&0+Qv`O~QZBScFlO(Q5BW=P6t1R=)c z0F9XzIXU@WGH7l^cdcz-l05F4R9RYF zK)Dzev0_as^w1;{9zcC0M8%=L@_l=fW0m5e{R^HHU+^>t2`6%K1$mF8dq6-;usw{5KJPsUZWKD zn{JNAcOW50TDK0SPxH>7`V-(ZT5Mk8TQ7M zM$18tuuZ`=HH&m_F@dRMkT56{zE2APlcp6;Jin504Z=s<>A?Ty=433+Wp=K zi=t~E9W8w+Dxe}}HD{uINz6&r;Vf3TC)a!BmEKpg;p$S2?%JWx_+BhLo#^g-{rV)k z8jk6=B93c1C2HTaN4|1yV8Y`}hfE6h);$wO8W=HVKy@c2<2tR3+smgm(=V3lDi$$cB-2afT z)L1mshM^%Yod4_0YeWpw=JEr>N$e<*Fml=TI9+DWXc~QpylA^2 zs;Gv!udESNOoGS>>106fzyS3!X>8QV2rMFDqSas$8obdsd~l|W&{RipN?|NG^XUl_ z>CizjcR}y)Sr_C=$hzZ4LSsb|qDoSS->@bYGOi--&KSKp$KAqsDNIpss{&JOgOq%g z&W21vI!48%%ss)|Gus0qqtCdm7CfN>NxHsR3W*4cV+&{+90*xYSiqXacw|
aNbjm2T9IbQ~2) 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 IJdS~Q0B6I;_5c6? literal 0 HcmV?d00001 diff --git a/example/my_module.py b/example/my_module.py index fa889f6..ce8a223 100644 --- a/example/my_module.py +++ b/example/my_module.py @@ -116,8 +116,8 @@ class MyModule(Module): def flatpak_packages(self) -> list[str]: return ["org.mozilla.firefox"] - def flatpak_user_packages(self) -> list[tuple[str,str]]: - return [("username", "io.github.kolunmi.Bazaar")] + def flatpak_user_packages(self) -> dict[str, list[str]]: + return {"username": ["io.github.kolunmi.Bazaar"]} def systemd_units(self) -> list[str]: return ["reflector.timer"] diff --git a/example/source.py b/example/source.py index e2742af..473dc9d 100644 --- a/example/source.py +++ b/example/source.py @@ -38,9 +38,10 @@ decman.flatpak_packages += ["dev.qwery.AddWater"] decman.ignored_flatpak_packages += ["org.signal.Signal"] # You can also install them to your user installation instead of the system installation -decman.flatpak_user_packages += [ - ("username", "dev.zed.Zed") -] +# Ensure that previous user installed flatpak declarations aren't overwritten and they are initialized. +decman.flatpak_user_packages["kk"] = decman.flatpak_user_packages.get("kk", []) +# Now add the package. +decman.flatpak_user_packages["kk"].append("dev.zed.Zed") # To import GPG keys, set the GNUPGHOME environment variable. # It can easily be done with python as well. diff --git a/src/decman/__init__.py b/src/decman/__init__.py index ede1412..7c818cd 100644 --- a/src/decman/__init__.py +++ b/src/decman/__init__.py @@ -387,12 +387,12 @@ class Module: """ return [] - def flatpak_user_packages(self) -> list[tuple[str, str]]: + def flatpak_user_packages(self) -> dict[str, list[str]]: """ Override this method to return flatpak packages that should be installed to the user installation as a part of this Module. """ - return [] + return {} def systemd_units(self) -> list[str]: """ @@ -427,5 +427,5 @@ files: dict[str, File] = {} directories: dict[str, Directory] = {} modules: list[Module] = [] flatpak_packages: list[str] = [] -flatpak_user_packages: list[tuple[str, str]] = [] +flatpak_user_packages: dict[str, list[str]] = {} ignored_flatpak_packages: list[str] = [] diff --git a/src/decman/app.py b/src/decman/app.py index 7a5271f..3edd658 100644 --- a/src/decman/app.py +++ b/src/decman/app.py @@ -238,6 +238,7 @@ class Core: """ Run the main logic of decman. """ + if self.update_units: self._disable_units() @@ -305,16 +306,33 @@ class Core: self._remove_user_flatpaks() def _remove_user_flatpaks(self, only_print: bool = False): + """ # get all users through a command instead of pwd because pwd also lists all 'virtual' users. add root since they can also have user installed packages - users = subprocess.run(["users"], check=True, stdout=subprocess.PIPE).stdout.decode().strip().split('\n') - users.append("root") + users = ( + subprocess.run(["users"], check=True, stdout=subprocess.PIPE) + .stdout.decode() + .strip() + .split("\n") + ) + print() + users.append("root")""" + users = pwd.getpwall() for user in users: - currently_installed_flatpak = self.flatpak.get_installed(as_user=True, which_user=user) - to_remove_flatpak = self.source.flatpak_packages_to_remove(currently_installed_flatpak, as_user=True, which_user=user) - l.print_list(f"Removing flatpak packages from user installation for user {user}", to_remove_flatpak) + user = user.pw_name + currently_installed_flatpak = self.flatpak.get_installed( + as_user=True, which_user=user + ) + to_remove_flatpak = self.source.flatpak_packages_to_remove( + currently_installed_flatpak, as_user=True, which_user=user + ) + l.print_list( + f"Removing flatpak packages from user installation for user {user}", + to_remove_flatpak, + ) - if only_print: continue + if only_print: + continue self.flatpak.remove(to_remove_flatpak, True, user) @@ -373,16 +391,32 @@ class Core: self._install_user_flatpaks() def _install_user_flatpaks(self, only_print: bool = False): - # get all users through a command instead of pwd because pwd also lists all 'virtual' users. add root since they can also have user installed packages - users = subprocess.run(["users"], check=True, stdout=subprocess.PIPE).stdout.decode().strip().split('\n') - users.append("root") + """# get all users through a command instead of pwd because pwd also lists all 'virtual' users. add root since they can also have user installed packages + users = ( + subprocess.run(["users"], check=True, stdout=subprocess.PIPE) + .stdout.decode() + .strip() + .split("\n") + ) + users.append("root")""" + + users = pwd.getpwall() for user in users: - currently_installed_flatpak = self.flatpak.get_installed(as_user=True, which_user=user) - to_install_flatpak = self.source.flatpak_packages_to_install(currently_installed_flatpak, as_user=True, which_user=user) - l.print_list(f"Installing flatpak packages to user installation for user {user}", to_install_flatpak) + user = user.pw_name + currently_installed_flatpak = self.flatpak.get_installed( + as_user=True, which_user=user + ) + to_install_flatpak = self.source.flatpak_packages_to_install( + currently_installed_flatpak, as_user=True, which_user=user + ) - if only_print: continue + if only_print: + l.print_list( + f"Installing flatpak packages to user installation for user {user}", + to_install_flatpak, + ) + continue self.flatpak.install(to_install_flatpak, True, user) @@ -444,6 +478,10 @@ def _resolve_source() -> l.Source: for user, units in decman.enabled_systemd_user_units.items(): enabled_systemd_user_units[user] = set(units) + flatpak_user_packages = {} + for user, pkgs in decman.flatpak_user_packages.items(): + flatpak_user_packages[user] = set(pkgs) + return l.Source( pacman_packages=set(decman.packages), aur_packages=set(decman.aur_packages), @@ -455,7 +493,7 @@ def _resolve_source() -> l.Source: directories=decman.directories, modules=set(decman.modules), flatpak_packages=set(decman.flatpak_packages), - flatpak_user_packages=set(decman.flatpak_user_packages), + flatpak_user_packages=flatpak_user_packages, ignored_flatpak_packages=set(decman.ignored_flatpak_packages), ) diff --git a/src/decman/config.py b/src/decman/config.py index 5bd2ca8..78719db 100644 --- a/src/decman/config.py +++ b/src/decman/config.py @@ -65,7 +65,7 @@ class Commands: """ Running this command installs all listed packages, and their dependencies/runtimes automatically. """ - return ["flatpak", "install", "--user" if as_user else "--system"] + pkgs + return ["flatpak", "install", "-y", "--user" if as_user else "--system"] + pkgs def install_files(self, pkg_files: list[str]) -> list[str]: """ @@ -102,7 +102,7 @@ class Commands: """ Updates all installed flatpak REFs including runtimes and dependencies. """ - return ["flatpak", "update", "--user" if as_user else "--system"] + return ["flatpak", "update", "-y", "--user" if as_user else "--system"] def remove(self, pkgs: list[str]) -> list[str]: """ @@ -115,13 +115,19 @@ class Commands: """ Running this command will remove the listed REFs. Unused dependencies might be kept, but to remove them another command needs to be run. """ - return ["flatpak", "remove", "--user" if as_user else "--system"] + pkgs + return ["flatpak", "remove", "-y", "--user" if as_user else "--system"] + pkgs def remove_unused_flatpak(self, as_user: bool = False) -> list[str]: """ This will remove all unused flatpak dependencies and runtimes. """ - return ["flatpak", "remove", "--unused", "--user" if as_user else "--system"] + return [ + "flatpak", + "remove", + "-y", + "--unused", + "--user" if as_user else "--system", + ] def enable_units(self, units: list[str]) -> list[str]: """ diff --git a/src/decman/lib/__init__.py b/src/decman/lib/__init__.py index 6b8c6d3..8e1b1e7 100644 --- a/src/decman/lib/__init__.py +++ b/src/decman/lib/__init__.py @@ -36,6 +36,7 @@ def print_continuation(msg: str, level: int = SUMMARY): if level == SUMMARY or conf.debug_output or not conf.quiet_output: print(f"{_CONTINUATION_PREFIX}{msg}") + def print_error(error_msg: str): """ Prints an error message to the user. @@ -43,6 +44,7 @@ def print_error(error_msg: str): print(f"{_DECMAN_MSG_TAG} {_RED_PREFIX}ERROR{_RESET_SUFFIX}: {error_msg}") + def print_warning(msg: str): """ Prints a warning to the user. @@ -50,6 +52,7 @@ def print_warning(msg: str): print(f"{_DECMAN_MSG_TAG} {_YELLOW_PREFIX}WARNING{_RESET_SUFFIX}: {msg}") + def print_summary(msg: str): """ Prints a summary message to the user. @@ -57,6 +60,7 @@ def print_summary(msg: str): print(f"{_DECMAN_MSG_TAG} {_CYAN_PREFIX}SUMMARY{_RESET_SUFFIX}: {msg}") + def print_list( msg: str, l: list[str], @@ -116,6 +120,7 @@ def print_list( print_continuation("", level=level) + def print_info(msg: str): """ Prints a detailed message to the user if verbose output is not disabled. @@ -123,6 +128,7 @@ def print_info(msg: str): if conf.debug_output or not conf.quiet_output: print(f"{_DECMAN_MSG_TAG} INFO: {msg}") + def print_debug(msg: str): """ Prints a detailed message to the user if debug messages are enabled. @@ -130,6 +136,7 @@ def print_debug(msg: str): if conf.debug_output: print(f"{_DECMAN_MSG_TAG} {_GRAY_PREFIX}DEBUG{_RESET_SUFFIX}: {msg}") + def prompt_number( msg: str, min_num: int, max_num: int, default: typing.Optional[int] = None ) -> int: @@ -412,7 +419,7 @@ class Source: directories: dict[str, decman.Directory], modules: set[decman.Module], flatpak_packages: set[str], - flatpak_user_packages: set[tuple[str,str]], + flatpak_user_packages: dict[str, set[str]], ignored_flatpak_packages: set[str], ): self.pacman_packages = pacman_packages @@ -638,7 +645,10 @@ class Source: return result def flatpak_packages_to_install( - self, currently_installed_packages: list[str], as_user: bool = False, which_user: str = "" + self, + currently_installed_packages: list[str], + as_user: bool = False, + which_user: str = "", ) -> list[str]: """ Returns all flatpak packages, that are not installed or ignored @@ -653,7 +663,10 @@ class Source: return result def flatpak_packages_to_remove( - self, currently_installed_packages: list[str], as_user: bool = False, which_user: str = "" + self, + currently_installed_packages: list[str], + as_user: bool = False, + which_user: str = "", ) -> list[str]: """ This returns a list of flatpak app ids, that need to be removed since they are installed but not found in either the list of ignored packages, @@ -697,29 +710,24 @@ class Source: result.update(module.pacman_packages()) return result - def _all_flatpak_packages(self, as_user: bool = False, which_user: str = "") -> set[str]: + def _all_flatpak_packages( + self, as_user: bool = False, which_user: str = "" + ) -> set[str]: # loop through all the user packages and save which ones are owned by the currently selected user - current_user_flatpak_packages = [] - for pkg in self.flatpak_user_packages: - if not pkg[0] == which_user: continue - current_user_flatpak_packages.append(pkg[1]) + current_user_flatpak_packages = self.flatpak_user_packages.get(which_user, []) result = set() result.update( self.flatpak_packages if not as_user else current_user_flatpak_packages ) for module in self.modules: - if module.enabled: - module_current_user_flatpak_packages = [] - for pkg in module.flatpak_user_packages(): - if not pkg[0] == which_user: continue - module_current_user_flatpak_packages.append(pkg[1]) - - result.update( - module.flatpak_packages() - if not as_user - else module_current_user_flatpak_packages - ) + if not module.enabled: + continue + result.update( + module.flatpak_packages() + if not as_user + else module.flatpak_user_packages().get(which_user, []) + ) return result @@ -956,6 +964,7 @@ def print_highlighted_pacman_messages(output: str): # Break, as to not print the same line again if it contains multiple keywords break + def echo_and_capture_command(program: list[str]) -> tuple[int, str]: """ Runs the given CLI program and arguments. @@ -975,10 +984,12 @@ def echo_and_capture_command(program: list[str]) -> tuple[int, str]: return (returncode, output) + def get_user_info(username: str) -> tuple[int, int]: info = pwd.getpwnam(username) return (info.pw_uid, info.pw_gid) + class Flatpak: def __init__(self) -> None: pass @@ -988,19 +999,28 @@ class Flatpak: Return all of the installed applications. Dependencies and runtimes are exluded since they will not be explicitly installed and thus flatpak will manage them. """ try: - uinfo = get_user_info(which_user) - packages = ( - subprocess.run( - conf.commands.list_flatpak_pkgs(as_user), - check=True, - stdout=subprocess.PIPE, - user=uinfo[0] if as_user else 0, - group=uinfo[1] if as_user else 0, - ) - .stdout.decode() - .strip() - .split("\n") + uinfo: tuple[int, int] = (0, 0) + + env = os.environ.copy() + user_env = env.copy() + user_env["HOME"] = os.path.expanduser(f"~{which_user}") + + if as_user: + uinfo = get_user_info(which_user) + + proc = subprocess.run( + conf.commands.list_flatpak_pkgs(as_user), + check=True, + stdout=subprocess.PIPE, + user=uinfo[0], + group=uinfo[1], + env=user_env if as_user else env, ) + packages = proc.stdout.decode().strip().split("\n") + + # print( + # f"as_user: {as_user}, which_user: {which_user}, uinfo: {uinfo}, stdout: {proc.stdout.decode()}, packages: {packages}" + # ) # The header might be included. It might also not. This will make sure that it is not present. if "Application ID" in packages: @@ -1015,14 +1035,17 @@ class Flatpak: user_facing_msg=f"Failed to get installed flatpak packages using '{error.cmd}'. Output: {error.stdout}." ) from error - def install(self, packages: list[str], as_user: bool = False, which_user: str = ""): + def install(self, packages: list[str], as_user: bool = False, which_user: str = ""): """ Install the listed flatpak packages. """ if not packages: return - uinfo = get_user_info(which_user) + uinfo: tuple[int, int] = (0, 0) + if as_user: + uinfo = get_user_info(which_user) + env = os.environ.copy() user_env = env.copy() user_env["HOME"] = os.path.expanduser(f"~{which_user}") @@ -1031,9 +1054,9 @@ class Flatpak: conf.commands.install_flatpak_pkgs(packages, as_user), check=True, stdout=subprocess.PIPE, - user=uinfo[0] if as_user else 0, - group=uinfo[1] if as_user else 0, - env=user_env if as_user else env + user=uinfo[0], + group=uinfo[1], + env=user_env if as_user else env, ) if proc.returncode != 0: @@ -1045,10 +1068,12 @@ class Flatpak: """ Upgrade all flatpak packages. """ - returncode, _output = echo_and_capture_command(conf.commands.upgrade_flatpak()) - if not returncode == 0: + proc = subprocess.run( + conf.commands.upgrade_flatpak(), check=True, stdout=subprocess.PIPE + ) + if not proc.returncode == 0: raise err.UserFacingError( - f"Failed to upgrade flatpak packages. Process exited with code {returncode}." + f"Failed to upgrade flatpak packages. Process exited with code {proc.returncode}." ) def remove(self, packages: list[str], as_user: bool = False, which_user: str = ""): @@ -1058,7 +1083,10 @@ class Flatpak: if not packages: return - uinfo = get_user_info(which_user) + uinfo: tuple[int, int] = (0, 0) + if as_user: + uinfo = get_user_info(which_user) + env = os.environ.copy() user_env = env.copy() user_env["HOME"] = os.path.expanduser(f"~{which_user}") @@ -1067,9 +1095,9 @@ class Flatpak: conf.commands.remove_flatpak(packages, as_user), check=True, stdout=subprocess.PIPE, - user=uinfo[0] if as_user else 0, - group=uinfo[1] if as_user else 0, - env=user_env if as_user else env + user=uinfo[0], + group=uinfo[1], + env=user_env if as_user else env, ) if not proc.returncode == 0: @@ -1083,7 +1111,7 @@ class Flatpak: stdout=subprocess.PIPE, user=uinfo[0] if as_user else 0, group=uinfo[1] if as_user else 0, - env=user_env if as_user else env + env=user_env if as_user else env, ) if not proc.returncode == 0: @@ -1091,6 +1119,7 @@ class Flatpak: f"Failed to remove unused flatpak packages. Process exited with code {proc.returncode}." ) + class Systemd: """ Interface for interacting with systemd. From 2378f49676e437163f73612ba5f1d880897f82f1 Mon Sep 17 00:00:00 2001 From: Kivi Kaitaniemi Date: Wed, 10 Dec 2025 00:29:15 +0200 Subject: [PATCH 13/13] Run flatpak commands on users with UID>=1000 --- README.md | 16 ++++----- example/my_module.py | 4 +-- example/source.py | 4 +-- src/decman/app.py | 69 ++++++++++++++++++++------------------ src/decman/config.py | 17 ++++++++-- src/decman/lib/__init__.py | 29 +++++++++++----- 6 files changed, 85 insertions(+), 54 deletions(-) diff --git a/README.md b/README.md index 733cfe1..80451ab 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,16 @@ # Decman -> 🎉 Support for Flatpaks was just added! 🎉 -> By default flatpak management this feature is disabled. Take a look at the example to learn how to use flatpaks. You can install to the user as well as the system installation. +> 🎉 Early support for Flatpaks was just added! 🎉 +> By default flatpak management is disabled. Support is in early stages so expect bugs. -> 🎉 Decman now has a AUR package! 🎉 -> -> To start using the AUR package simply add `decman` to `decman.aur_packages`. To ensure a smooth change, add decman-git to ignored_packages during the conversion. -> > ```py > import decman -> decman.aur_packages += ["decman"] -> decman.ignored_packages += ["decman-git"] +> import decman.config +> decman.config.enable_flatpak = True +> # You can add system wide packages as well as user packages +> decman.flatpak_packages += ["org.signal.Signal"] +> decman.flatpak_user_packages["user"] = decman.flatpak_user_packages.get("user", []) +> decman.flatpak_user_packages["user"].append("dev.zed.Zed") > ``` Decman is a declarative package & configuration manager for Arch Linux. It allows you to manage installed packages, your dotfiles, enabled systemd units, and run commands automatically. Your system is configured using python so your configuration can be very adaptive. diff --git a/example/my_module.py b/example/my_module.py index ce8a223..05df809 100644 --- a/example/my_module.py +++ b/example/my_module.py @@ -1,7 +1,7 @@ # from import is ok for importing classes and functions # just remember to not import variables this way -from typing import override -from decman import Module, File, Directory, UserPackage, sh, prg + +from decman import Directory, File, Module, UserPackage, prg, sh class MyModule(Module): diff --git a/example/source.py b/example/source.py index 473dc9d..1d6e33f 100644 --- a/example/source.py +++ b/example/source.py @@ -1,8 +1,8 @@ # This example covers all decman features and many useful ways of configuring a system. # Configuration can be as simple or as complex as is needed. -import socket import os +import socket # Note: Do NOT use from imports for global variables # BAD: from decman import packages/modules/etc @@ -10,7 +10,7 @@ import decman import decman.config # This is fine since the thing being imported is a class and not a global variable. -from decman import UserPackage, File, Directory, UserRaisedError +from decman import Directory, File, UserPackage, UserRaisedError # Flatpaks are disabled by default. This way no already installed flatpaks will get suddenly deleted. decman.config.enable_flatpak = True diff --git a/src/decman/app.py b/src/decman/app.py index 3edd658..578d168 100644 --- a/src/decman/app.py +++ b/src/decman/app.py @@ -5,11 +5,10 @@ Module containing the CLI Application. import argparse import os +import pwd import shutil -import subprocess import sys import traceback -import pwd import decman import decman.config as conf @@ -291,7 +290,7 @@ class Core: l.print_list("Removing pacman packages:", to_remove) - if conf.enable_flatpak: + if conf.enable_flatpak and self.update_flatpaks: l.print_list("Removing flatpak packages:", to_remove_flatpak) self._remove_user_flatpaks(only_print=True) @@ -306,20 +305,15 @@ class Core: self._remove_user_flatpaks() def _remove_user_flatpaks(self, only_print: bool = False): - """ - # get all users through a command instead of pwd because pwd also lists all 'virtual' users. add root since they can also have user installed packages - users = ( - subprocess.run(["users"], check=True, stdout=subprocess.PIPE) - .stdout.decode() - .strip() - .split("\n") - ) - print() - users.append("root")""" - - users = pwd.getpwall() + # Get all non system users (users that have uid >= 1000), also ignore nobody + users = [ + u.pw_name + for u in pwd.getpwall() + if u.pw_uid >= 1000 and u.pw_name not in ("nobody",) + ] + # Add root to users + users.append("root") for user in users: - user = user.pw_name currently_installed_flatpak = self.flatpak.get_installed( as_user=True, which_user=user ) @@ -353,7 +347,18 @@ class Core: # flatpak if conf.enable_flatpak and self.update_flatpaks: + l.print_summary("Upgrading flatpak packages.") self.flatpak.upgrade() + users = [ + u.pw_name + for u in pwd.getpwall() + if u.pw_uid >= 1000 and u.pw_name not in ("nobody",) + ] + # Add root to users + users.append("root") + for user in users: + l.print_summary(f"Upgrading flatpak packages for {user}.") + self.flatpak.upgrade(True, user) def _install_pkgs(self): """ @@ -373,13 +378,15 @@ class Core: l.print_list("Installing pacman packages:", to_install_pacman) - if conf.enable_flatpak: - l.print_list("Installing flatpak packages:", to_install_flatpak) - self._install_user_flatpaks(only_print=True) - # fpm prints a summary so no need to print it twice if self.only_print: l.print_list("Installing foreign packages:", to_install_fpm) + + if conf.enable_flatpak and self.update_flatpaks: + l.print_list("Installing flatpak packages:", to_install_flatpak) + + if self.only_print: + self._install_user_flatpaks(only_print=True) return self.pacman.install(to_install_pacman) @@ -388,22 +395,20 @@ class Core: if conf.enable_flatpak and self.update_flatpaks: self.flatpak.install(to_install_flatpak) + # Print summary before the action + self._install_user_flatpaks(only_print=True) self._install_user_flatpaks() def _install_user_flatpaks(self, only_print: bool = False): - """# get all users through a command instead of pwd because pwd also lists all 'virtual' users. add root since they can also have user installed packages - users = ( - subprocess.run(["users"], check=True, stdout=subprocess.PIPE) - .stdout.decode() - .strip() - .split("\n") - ) - users.append("root")""" - - users = pwd.getpwall() - + # Get all non system users (users that have uid >= 1000), also ignore nobody + users = [ + u.pw_name + for u in pwd.getpwall() + if u.pw_uid >= 1000 and u.pw_name not in ("nobody",) + ] + # Add root to users + users.append("root") for user in users: - user = user.pw_name currently_installed_flatpak = self.flatpak.get_installed( as_user=True, which_user=user ) diff --git a/src/decman/config.py b/src/decman/config.py index 78719db..057d7ef 100644 --- a/src/decman/config.py +++ b/src/decman/config.py @@ -102,7 +102,13 @@ class Commands: """ Updates all installed flatpak REFs including runtimes and dependencies. """ - return ["flatpak", "update", "-y", "--user" if as_user else "--system"] + return [ + "flatpak", + "update", + "--noninteractive", + "-y", + "--user" if as_user else "--system", + ] def remove(self, pkgs: list[str]) -> list[str]: """ @@ -115,7 +121,13 @@ class Commands: """ Running this command will remove the listed REFs. Unused dependencies might be kept, but to remove them another command needs to be run. """ - return ["flatpak", "remove", "-y", "--user" if as_user else "--system"] + pkgs + return [ + "flatpak", + "remove", + "--noninteractive", + "-y", + "--user" if as_user else "--system", + ] + pkgs def remove_unused_flatpak(self, as_user: bool = False) -> list[str]: """ @@ -124,6 +136,7 @@ class Commands: return [ "flatpak", "remove", + "--noninteractive", "-y", "--unused", "--user" if as_user else "--system", diff --git a/src/decman/lib/__init__.py b/src/decman/lib/__init__.py index 8e1b1e7..2c1478b 100644 --- a/src/decman/lib/__init__.py +++ b/src/decman/lib/__init__.py @@ -5,11 +5,11 @@ Library module for decman. import json import os import pty +import pwd import shutil import subprocess import time import typing -import pwd import decman import decman.config as conf @@ -1035,7 +1035,9 @@ class Flatpak: user_facing_msg=f"Failed to get installed flatpak packages using '{error.cmd}'. Output: {error.stdout}." ) from error - def install(self, packages: list[str], as_user: bool = False, which_user: str = ""): + def install( + self, packages: list[str], as_user: bool = False, which_user: str = "root" + ): """ Install the listed flatpak packages. """ @@ -1053,7 +1055,6 @@ class Flatpak: proc = subprocess.run( conf.commands.install_flatpak_pkgs(packages, as_user), check=True, - stdout=subprocess.PIPE, user=uinfo[0], group=uinfo[1], env=user_env if as_user else env, @@ -1064,19 +1065,33 @@ class Flatpak: f"Failed to install flatpak packages. Process exited with code {proc.returncode}." ) - def upgrade(self) -> None: + def upgrade(self, as_user: bool = False, which_user: str = "root") -> None: """ Upgrade all flatpak packages. """ + uinfo: tuple[int, int] = (0, 0) + if as_user: + uinfo = get_user_info(which_user) + + env = os.environ.copy() + user_env = env.copy() + user_env["HOME"] = os.path.expanduser(f"~{which_user}") + proc = subprocess.run( - conf.commands.upgrade_flatpak(), check=True, stdout=subprocess.PIPE + conf.commands.upgrade_flatpak(as_user=True), + check=True, + user=uinfo[0], + group=uinfo[1], + env=user_env if as_user else env, ) if not proc.returncode == 0: raise err.UserFacingError( f"Failed to upgrade flatpak packages. Process exited with code {proc.returncode}." ) - def remove(self, packages: list[str], as_user: bool = False, which_user: str = ""): + def remove( + self, packages: list[str], as_user: bool = False, which_user: str = "root" + ): """ Remove all the listed packages and their unused dependecies. This has to happen in two steps. """ @@ -1094,7 +1109,6 @@ class Flatpak: proc = subprocess.run( conf.commands.remove_flatpak(packages, as_user), check=True, - stdout=subprocess.PIPE, user=uinfo[0], group=uinfo[1], env=user_env if as_user else env, @@ -1108,7 +1122,6 @@ class Flatpak: proc = subprocess.run( conf.commands.remove_unused_flatpak(as_user), check=True, - stdout=subprocess.PIPE, user=uinfo[0] if as_user else 0, group=uinfo[1] if as_user else 0, env=user_env if as_user else env,