Remove plugin system
This commit is contained in:
parent
d6de03ab76
commit
1b92b93f97
|
|
@ -1,3 +0,0 @@
|
|||
from archinstall.lib.plugins import plugin
|
||||
|
||||
__all__ = ['plugin']
|
||||
|
|
@ -25,7 +25,6 @@ from archinstall.lib.models.packages import Repository
|
|||
from archinstall.lib.models.profile import ProfileConfiguration
|
||||
from archinstall.lib.models.users import Password, User, UserSerialization
|
||||
from archinstall.lib.output import debug, error, logger, warn
|
||||
from archinstall.lib.plugins import load_plugin
|
||||
from archinstall.lib.translationhandler import Language, tr, translation_handler
|
||||
from archinstall.lib.version import get_version
|
||||
from archinstall.tui.ui.components import tui
|
||||
|
|
@ -48,7 +47,6 @@ class Arguments:
|
|||
debug: bool = False
|
||||
offline: bool = False
|
||||
no_pkg_lookups: bool = False
|
||||
plugin: str | None = None
|
||||
skip_version_check: bool = False
|
||||
skip_wifi_check: bool = False
|
||||
advanced: bool = False
|
||||
|
|
@ -395,13 +393,6 @@ class ArchConfigHandler:
|
|||
default=False,
|
||||
help='Disabled package validation specifically prior to starting installation.',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--plugin',
|
||||
nargs='?',
|
||||
type=str,
|
||||
default=None,
|
||||
help='File path to a plugin to load',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--skip-version-check',
|
||||
action='store_true',
|
||||
|
|
@ -441,10 +432,6 @@ class ArchConfigHandler:
|
|||
if args.debug:
|
||||
warn(f'Warning: --debug mode will write certain credentials to {logger.path}!')
|
||||
|
||||
if args.plugin:
|
||||
plugin_path = Path(args.plugin)
|
||||
load_plugin(plugin_path)
|
||||
|
||||
if args.creds_decryption_key is None:
|
||||
if os.environ.get('ARCHINSTALL_CREDS_DECRYPTION_KEY'):
|
||||
args.creds_decryption_key = os.environ.get('ARCHINSTALL_CREDS_DECRYPTION_KEY')
|
||||
|
|
|
|||
|
|
@ -54,7 +54,6 @@ from archinstall.lib.packages.packages import installed_package
|
|||
from archinstall.lib.pacman.config import PacmanConfig
|
||||
from archinstall.lib.pacman.pacman import Pacman
|
||||
from archinstall.lib.pathnames import MIRRORLIST, PACMAN_CONF
|
||||
from archinstall.lib.plugins import plugins
|
||||
from archinstall.lib.translationhandler import tr
|
||||
|
||||
# Any package that the Installer() is responsible for (optional and the default ones)
|
||||
|
|
@ -560,11 +559,6 @@ class Installer:
|
|||
"""
|
||||
debug('Setting mirrors on ' + ('target' if on_target else 'live system'))
|
||||
|
||||
for plugin in plugins.values():
|
||||
if hasattr(plugin, 'on_mirrors'):
|
||||
if result := plugin.on_mirrors(mirror_config):
|
||||
mirror_config = result
|
||||
|
||||
if on_target:
|
||||
mirrorlist_config = self.target / MIRRORLIST.relative_to_root()
|
||||
pacman_config = self.target / PACMAN_CONF.relative_to_root()
|
||||
|
|
@ -606,11 +600,6 @@ class Installer:
|
|||
if not fstab_path.is_file():
|
||||
raise RequirementError('Could not create fstab file')
|
||||
|
||||
for plugin in plugins.values():
|
||||
if hasattr(plugin, 'on_genfstab'):
|
||||
if plugin.on_genfstab(self) is True:
|
||||
break
|
||||
|
||||
with open(fstab_path, 'a') as fp:
|
||||
for entry in self._fstab_entries:
|
||||
fp.write(f'{entry}\n')
|
||||
|
|
@ -673,11 +662,6 @@ class Installer:
|
|||
if not len(zone):
|
||||
return True # Redundant
|
||||
|
||||
for plugin in plugins.values():
|
||||
if hasattr(plugin, 'on_timezone'):
|
||||
if result := plugin.on_timezone(zone):
|
||||
zone = result
|
||||
|
||||
if (Path('/usr') / 'share' / 'zoneinfo' / zone).exists():
|
||||
(Path(self.target) / 'etc' / 'localtime').unlink(missing_ok=True)
|
||||
self.arch_chroot(f'ln -s /usr/share/zoneinfo/{zone} /etc/localtime')
|
||||
|
|
@ -713,10 +697,6 @@ class Installer:
|
|||
except SysCallError as err:
|
||||
raise ServiceException(f'Unable to start service {service}: {err}')
|
||||
|
||||
for plugin in plugins.values():
|
||||
if hasattr(plugin, 'on_service'):
|
||||
plugin.on_service(service)
|
||||
|
||||
def disable_service(self, services_disable: str | list[str]) -> None:
|
||||
if isinstance(services_disable, str):
|
||||
services_disable = [services_disable]
|
||||
|
|
@ -744,19 +724,6 @@ class Installer:
|
|||
def configure_nic(self, nic: Nic) -> None:
|
||||
conf = nic.as_systemd_config()
|
||||
|
||||
for plugin in plugins.values():
|
||||
if hasattr(plugin, 'on_configure_nic'):
|
||||
conf = (
|
||||
plugin.on_configure_nic(
|
||||
nic.iface,
|
||||
nic.dhcp,
|
||||
nic.ip,
|
||||
nic.gateway,
|
||||
nic.dns,
|
||||
)
|
||||
or conf
|
||||
)
|
||||
|
||||
with open(f'{self.target}/etc/systemd/network/10-{nic.iface}.network', 'a') as netconf:
|
||||
netconf.write(str(conf))
|
||||
|
||||
|
|
@ -818,12 +785,6 @@ class Installer:
|
|||
return True
|
||||
|
||||
def mkinitcpio(self, flags: list[str]) -> bool:
|
||||
for plugin in plugins.values():
|
||||
if hasattr(plugin, 'on_mkinitcpio'):
|
||||
# Allow plugins to override the usage of mkinitcpio altogether.
|
||||
if plugin.on_mkinitcpio(self):
|
||||
return True
|
||||
|
||||
with open(f'{self.target}/etc/mkinitcpio.conf', 'r+') as mkinit:
|
||||
content = mkinit.read()
|
||||
content = re.sub('\nMODULES=(.*)', f'\nMODULES=({" ".join(self._modules)})', content)
|
||||
|
|
@ -963,10 +924,6 @@ class Installer:
|
|||
info(f'Running post-installation hook: {function}')
|
||||
function(self)
|
||||
|
||||
for plugin in plugins.values():
|
||||
if hasattr(plugin, 'on_install'):
|
||||
plugin.on_install(self)
|
||||
|
||||
def setup_btrfs_snapshot(
|
||||
self,
|
||||
snapshot_type: SnapshotType,
|
||||
|
|
@ -1812,14 +1769,6 @@ class Installer:
|
|||
:param uki_enabled: Whether to use unified kernel images
|
||||
:param bootloader_removable: Whether to install to removable media location (UEFI only, for GRUB and Limine)
|
||||
"""
|
||||
|
||||
for plugin in plugins.values():
|
||||
if hasattr(plugin, 'on_add_bootloader'):
|
||||
# Allow plugins to override the boot-loader handling.
|
||||
# This allows for boot configuring and installing bootloaders.
|
||||
if plugin.on_add_bootloader(self):
|
||||
return
|
||||
|
||||
efi_partition = self._get_efi_partition()
|
||||
boot_partition = self._get_boot_partition()
|
||||
root = self._get_root()
|
||||
|
|
@ -1902,33 +1851,19 @@ class Installer:
|
|||
self._create_user(user)
|
||||
|
||||
def _create_user(self, user: User) -> None:
|
||||
# This plugin hook allows for the plugin to handle the creation of the user.
|
||||
# Password and Group management is still handled by user_create()
|
||||
handled_by_plugin = False
|
||||
for plugin in plugins.values():
|
||||
if hasattr(plugin, 'on_user_create'):
|
||||
if result := plugin.on_user_create(self, user):
|
||||
handled_by_plugin = result
|
||||
info(f'Creating user {user.username}')
|
||||
|
||||
if not handled_by_plugin:
|
||||
info(f'Creating user {user.username}')
|
||||
cmd = 'useradd -m'
|
||||
|
||||
cmd = 'useradd -m'
|
||||
if user.sudo:
|
||||
cmd += ' -G wheel'
|
||||
|
||||
if user.sudo:
|
||||
cmd += ' -G wheel'
|
||||
cmd += f' {user.username}'
|
||||
|
||||
cmd += f' {user.username}'
|
||||
|
||||
try:
|
||||
self.arch_chroot(cmd)
|
||||
except SysCallError as err:
|
||||
raise SystemError(f'Could not create user inside installation: {err}')
|
||||
|
||||
for plugin in plugins.values():
|
||||
if hasattr(plugin, 'on_user_created'):
|
||||
if result := plugin.on_user_created(self, user):
|
||||
handled_by_plugin = result
|
||||
try:
|
||||
self.arch_chroot(cmd)
|
||||
except SysCallError as err:
|
||||
raise SystemError(f'Could not create user inside installation: {err}')
|
||||
|
||||
self.set_user_password(user)
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ from archinstall.lib.command import SysCommand
|
|||
from archinstall.lib.exceptions import RequirementError
|
||||
from archinstall.lib.output import error, info, warn
|
||||
from archinstall.lib.pathnames import PACMAN_CONF
|
||||
from archinstall.lib.plugins import plugins
|
||||
from archinstall.lib.translationhandler import tr
|
||||
|
||||
|
||||
|
|
@ -67,11 +66,6 @@ class Pacman:
|
|||
if isinstance(packages, str):
|
||||
packages = [packages]
|
||||
|
||||
for plugin in plugins.values():
|
||||
if hasattr(plugin, 'on_pacstrap'):
|
||||
if result := plugin.on_pacstrap(packages):
|
||||
packages = result
|
||||
|
||||
info(f'Installing packages: {packages}')
|
||||
|
||||
self.ask(
|
||||
|
|
|
|||
|
|
@ -1,120 +0,0 @@
|
|||
import hashlib
|
||||
import importlib.util
|
||||
import os
|
||||
import sys
|
||||
import urllib.parse
|
||||
import urllib.request
|
||||
from importlib import metadata
|
||||
from pathlib import Path
|
||||
|
||||
from archinstall.lib.output import error, info, warn
|
||||
from archinstall.lib.version import get_version
|
||||
|
||||
plugins = {}
|
||||
|
||||
|
||||
# 1: List archinstall.plugin definitions
|
||||
# 2: Load the plugin entrypoint
|
||||
# 3: Initiate the plugin and store it as .name in plugins
|
||||
for plugin_definition in metadata.entry_points().select(group='archinstall.plugin'):
|
||||
plugin_entrypoint = plugin_definition.load()
|
||||
|
||||
try:
|
||||
plugins[plugin_definition.name] = plugin_entrypoint()
|
||||
except Exception as err:
|
||||
error(
|
||||
f'Error: {err}',
|
||||
f'The above error was detected when loading the plugin: {plugin_definition}',
|
||||
)
|
||||
|
||||
|
||||
# @archinstall.plugin decorator hook to programmatically add
|
||||
# plugins in runtime. Useful in profiles_bck and other things.
|
||||
def plugin(f, *args, **kwargs) -> None: # type: ignore[no-untyped-def]
|
||||
plugins[f.__name__] = f
|
||||
|
||||
|
||||
def _localize_path(path: Path) -> Path:
|
||||
"""
|
||||
Support structures for load_plugin()
|
||||
"""
|
||||
url = urllib.parse.urlparse(str(path))
|
||||
|
||||
if url.scheme and url.scheme in ('https', 'http'):
|
||||
converted_path = Path(f'/tmp/{path.stem}_{hashlib.md5(os.urandom(12)).hexdigest()}.py')
|
||||
|
||||
with open(converted_path, 'w') as temp_file:
|
||||
temp_file.write(urllib.request.urlopen(url.geturl()).read().decode('utf-8'))
|
||||
|
||||
return converted_path
|
||||
else:
|
||||
return path
|
||||
|
||||
|
||||
def _import_via_path(path: Path, namespace: str | None = None) -> str:
|
||||
if not namespace:
|
||||
namespace = os.path.basename(path)
|
||||
|
||||
if namespace == '__init__.py':
|
||||
namespace = path.parent.name
|
||||
|
||||
try:
|
||||
spec = importlib.util.spec_from_file_location(namespace, path)
|
||||
if spec and spec.loader:
|
||||
imported = importlib.util.module_from_spec(spec)
|
||||
sys.modules[namespace] = imported
|
||||
spec.loader.exec_module(sys.modules[namespace])
|
||||
|
||||
return namespace
|
||||
except Exception as err:
|
||||
error(
|
||||
f'Error: {err}',
|
||||
f'The above error was detected when loading the plugin: {path}',
|
||||
)
|
||||
|
||||
try:
|
||||
del sys.modules[namespace]
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return namespace
|
||||
|
||||
|
||||
def load_plugin(path: Path) -> None:
|
||||
namespace: str | None = None
|
||||
parsed_url = urllib.parse.urlparse(str(path))
|
||||
info(f'Loading plugin from url {parsed_url}')
|
||||
|
||||
# The Profile was not a direct match on a remote URL
|
||||
if not parsed_url.scheme:
|
||||
# Path was not found in any known examples, check if it's an absolute path
|
||||
if os.path.isfile(path):
|
||||
namespace = _import_via_path(path)
|
||||
elif parsed_url.scheme in ('https', 'http'):
|
||||
localized = _localize_path(path)
|
||||
namespace = _import_via_path(localized)
|
||||
|
||||
if namespace and namespace in sys.modules:
|
||||
# Version dependency via __archinstall__version__ variable (if present) in the plugin
|
||||
# Any errors in version inconsistency will be handled through normal error handling if not defined.
|
||||
version = get_version()
|
||||
|
||||
if version is not None:
|
||||
version_major_and_minor = version.rsplit('.', 1)[0]
|
||||
|
||||
if sys.modules[namespace].__archinstall__version__ < float(version_major_and_minor):
|
||||
error(f'Plugin {sys.modules[namespace]} does not support the current Archinstall version.')
|
||||
|
||||
# Locate the plugin entry-point called Plugin()
|
||||
# This in accordance with the entry_points() from setup.cfg above
|
||||
if hasattr(sys.modules[namespace], 'Plugin'):
|
||||
try:
|
||||
plugins[namespace] = sys.modules[namespace].Plugin()
|
||||
info(f'Plugin {plugins[namespace]} has been loaded.')
|
||||
except Exception as err:
|
||||
error(
|
||||
f'Error: {err}',
|
||||
f'The above error was detected when initiating the plugin: {path}',
|
||||
)
|
||||
else:
|
||||
warn(f"Plugin '{path}' is missing a valid entry-point or is corrupt.")
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
.. _archinstall.Plugins:
|
||||
|
||||
Python Plugins
|
||||
==============
|
||||
|
||||
``archinstall`` supports plugins via two methods.
|
||||
|
||||
First method is directly via the ``--plugin`` parameter when running as a CLI tool. This will load a specific plugin locally or remotely via a path.
|
||||
|
||||
The second method is via Python's built in `plugin discovery`_ using `entry points`_ categorized as ``archinstall.plugin``.
|
||||
|
||||
``--plugin`` parameter
|
||||
----------------------
|
||||
|
||||
The parameter has the benefit of being stored in the ``--conf`` state, meaning when re-running an installation — the plugin will automatically be loaded.
|
||||
It's limitation is that it requires an initial path to be known and written and be cumbersome.
|
||||
|
||||
Plugin Discovery
|
||||
----------------
|
||||
|
||||
This method allows for multiple plugins to be loaded with the drawback that they have to be installed beforehand on the system running ``archinstall``.
|
||||
This mainly targets those who build their own ISO's and package specific setups for their needs.
|
||||
|
||||
|
||||
What's supported?
|
||||
-----------------
|
||||
|
||||
Currently the documentation for this is scarse. Until that is resolved, the best way to find supported features is to search the source code for `plugin.on_ <https://github.com/search?q=repo%3Aarchlinux%2Farchinstall+%22plugin.on_%22&type=code>`_ as this will give a clear indication of which calls are made to plugins.
|
||||
|
||||
How does it work?
|
||||
-----------------
|
||||
|
||||
``archinstall`` plugins use a discovery-driven approach where plugins are queried for certain functions.
|
||||
As an example, if a plugin has the following function:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def on_pacstrap(*packages):
|
||||
...
|
||||
|
||||
The function :code:`archinstall.Pacman().strap(["some packages"])` is hardcoded to iterate plugins and look for :code:`on_pacstrap` in the plugin.
|
||||
If the function exists, :code:`.strap()` will call the plugin's function and replace the initial package list with the result from the plugin.
|
||||
|
||||
The best way to document these calls is currently undecided, as it's hard to document this behavior dynamically.
|
||||
|
||||
Writing your own?
|
||||
-----------------
|
||||
|
||||
The simplest way currently is to look at a reference implementation or the community. Two of these are:
|
||||
|
||||
* `torxed/archinstall-aur <https://github.com/torxed/archinstall-aur>`_
|
||||
* `phisch/archinstall-aur <https://github.com/phisch/archinstall-aur>`_
|
||||
|
||||
And search for `plugin.on_ <https://github.com/search?q=repo%3Aarchlinux%2Farchinstall+%22plugin.on_%22&type=code>`_ in the code base to find what ``archinstall`` will look for. PR's are welcome to widen the support for this.
|
||||
|
||||
.. _plugin discovery: https://packaging.python.org/en/latest/specifications/entry-points/
|
||||
.. _entry points: https://docs.python.org/3/library/importlib.metadata.html#entry-points
|
||||
|
|
@ -100,26 +100,6 @@ This is also a catch-all issue.
|
|||
|
||||
This means that feature requests like supporting filesystems such as `ZFS`_ can not be added, and issues cannot be solved by using AUR packages either.
|
||||
|
||||
.. note::
|
||||
|
||||
But in spirit of giving the community options, ``archinstall`` supports :ref:`archinstall.Plugins`, which means you can run ``archinstall --plugin <url>`` and source an AUR plugin.
|
||||
|
||||
`torxed/archinstall-aur <https://github.com/torxed/archinstall-aur>`_ is a reference implementation for plugins:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
# archinstall --plugin https://archlinux.life/aur-plugin
|
||||
|
||||
`phisch/archinstall-aur <https://github.com/phisch/archinstall-aur>`_ is another alternative:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
# archinstall --plugin https://raw.githubusercontent.com/phisch/archinstall-aur/master/archinstall-aur.py
|
||||
|
||||
.. warning::
|
||||
|
||||
This will allow for unsupported usage of AUR during installation.
|
||||
|
||||
.. _#1686: https://github.com/archlinux/archinstall/issues/1686
|
||||
.. _#2002: https://github.com/archlinux/archinstall/issues/2002
|
||||
.. _#2144: https://github.com/archlinux/archinstall/issues/2144
|
||||
|
|
|
|||
|
|
@ -32,7 +32,6 @@ Some of the features of Archinstall are:
|
|||
|
||||
installing/python
|
||||
examples/python
|
||||
archinstall/plugins
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 3
|
||||
|
|
|
|||
|
|
@ -48,7 +48,6 @@ def test_default_args(monkeypatch: MonkeyPatch) -> None:
|
|||
debug=False,
|
||||
offline=False,
|
||||
no_pkg_lookups=False,
|
||||
plugin=None,
|
||||
skip_version_check=False,
|
||||
advanced=False,
|
||||
)
|
||||
|
|
@ -79,8 +78,6 @@ def test_correct_parsing_args(
|
|||
'--debug',
|
||||
'--offline',
|
||||
'--no-pkg-lookups',
|
||||
'--plugin',
|
||||
'pytest_plugin.py',
|
||||
'--skip-version-check',
|
||||
'--advanced',
|
||||
'--dry-run',
|
||||
|
|
@ -105,7 +102,6 @@ def test_correct_parsing_args(
|
|||
debug=True,
|
||||
offline=True,
|
||||
no_pkg_lookups=True,
|
||||
plugin='pytest_plugin.py',
|
||||
skip_version_check=True,
|
||||
advanced=True,
|
||||
)
|
||||
|
|
|
|||
Loading…
Reference in New Issue