Fix plugin (#4594)
* change behaviour to load plugin * add checks in _write_plugin_to_temp_file * modify test * fixed test
This commit is contained in:
parent
c6a5a130a8
commit
cce0c47e11
|
|
@ -3,6 +3,7 @@ import json
|
||||||
import os
|
import os
|
||||||
import stat
|
import stat
|
||||||
import sys
|
import sys
|
||||||
|
import tempfile
|
||||||
import urllib.error
|
import urllib.error
|
||||||
import urllib.parse
|
import urllib.parse
|
||||||
from argparse import ArgumentParser, Namespace
|
from argparse import ArgumentParser, Namespace
|
||||||
|
|
@ -60,7 +61,8 @@ class Arguments:
|
||||||
debug: bool = False
|
debug: bool = False
|
||||||
offline: bool = False
|
offline: bool = False
|
||||||
no_pkg_lookups: bool = False
|
no_pkg_lookups: bool = False
|
||||||
plugin: str | None = None
|
plugin: Path | None = None
|
||||||
|
plugin_url: str | None = None
|
||||||
skip_version_check: bool = False
|
skip_version_check: bool = False
|
||||||
skip_wifi_check: bool = False
|
skip_wifi_check: bool = False
|
||||||
advanced: bool = False
|
advanced: bool = False
|
||||||
|
|
@ -614,10 +616,17 @@ class ArchConfigHandler:
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--plugin',
|
'--plugin',
|
||||||
nargs='?',
|
nargs='?',
|
||||||
type=str,
|
type=Path,
|
||||||
default=None,
|
default=None,
|
||||||
help='File path to a plugin to load',
|
help='File path to a plugin to load',
|
||||||
)
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--plugin-url',
|
||||||
|
type=str,
|
||||||
|
nargs='?',
|
||||||
|
default=None,
|
||||||
|
help='Url to a plugin file to load',
|
||||||
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--skip-version-check',
|
'--skip-version-check',
|
||||||
action='store_true',
|
action='store_true',
|
||||||
|
|
@ -657,7 +666,11 @@ class ArchConfigHandler:
|
||||||
warn(f'Warning: --debug mode will write certain credentials to {logger.path}!')
|
warn(f'Warning: --debug mode will write certain credentials to {logger.path}!')
|
||||||
|
|
||||||
if args.plugin:
|
if args.plugin:
|
||||||
plugin_path = Path(args.plugin)
|
load_plugin(args.plugin)
|
||||||
|
|
||||||
|
if args.plugin_url:
|
||||||
|
plugin_data = self._fetch_from_url(args.plugin_url)
|
||||||
|
plugin_path = self._write_plugin_to_temp_file(plugin_data)
|
||||||
load_plugin(plugin_path)
|
load_plugin(plugin_path)
|
||||||
|
|
||||||
if args.creds_decryption_key is None:
|
if args.creds_decryption_key is None:
|
||||||
|
|
@ -749,6 +762,27 @@ class ArchConfigHandler:
|
||||||
|
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
def _write_plugin_to_temp_file(self, plugin_data: str) -> Path:
|
||||||
|
if not plugin_data.strip():
|
||||||
|
error('The downloaded plugin is empty')
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
tmp_file = tempfile.NamedTemporaryFile(
|
||||||
|
mode='w',
|
||||||
|
suffix='.py',
|
||||||
|
prefix='archinstall_plugin_',
|
||||||
|
delete=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
with tmp_file as f:
|
||||||
|
f.write(plugin_data)
|
||||||
|
except OSError as err:
|
||||||
|
error(f'Could not write the downloaded plugin to a temporary file: {err}')
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
return Path(tmp_file.name)
|
||||||
|
|
||||||
def _read_file(self, path: Path) -> str:
|
def _read_file(self, path: Path) -> str:
|
||||||
if not path.exists():
|
if not path.exists():
|
||||||
error(f'Could not find file {path}')
|
error(f'Could not find file {path}')
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,6 @@
|
||||||
import hashlib
|
|
||||||
import importlib.util
|
import importlib.util
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import urllib.parse
|
|
||||||
import urllib.request
|
|
||||||
from importlib import metadata
|
from importlib import metadata
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
@ -34,23 +31,6 @@ def plugin(f, *args, **kwargs) -> None: # type: ignore[no-untyped-def]
|
||||||
plugins[f.__name__] = f
|
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:
|
def _import_via_path(path: Path, namespace: str | None = None) -> str:
|
||||||
if not namespace:
|
if not namespace:
|
||||||
namespace = os.path.basename(path)
|
namespace = os.path.basename(path)
|
||||||
|
|
@ -82,17 +62,10 @@ def _import_via_path(path: Path, namespace: str | None = None) -> str:
|
||||||
|
|
||||||
def load_plugin(path: Path) -> None:
|
def load_plugin(path: Path) -> None:
|
||||||
namespace: str | None = None
|
namespace: str | None = None
|
||||||
parsed_url = urllib.parse.urlparse(str(path))
|
info(f'Loading plugin from {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):
|
if os.path.isfile(path):
|
||||||
namespace = _import_via_path(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:
|
if namespace and namespace in sys.modules:
|
||||||
# Version dependency via __archinstall__version__ variable (if present) in the plugin
|
# Version dependency via __archinstall__version__ variable (if present) in the plugin
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,7 @@ def test_default_args(monkeypatch: MonkeyPatch) -> None:
|
||||||
offline=False,
|
offline=False,
|
||||||
no_pkg_lookups=False,
|
no_pkg_lookups=False,
|
||||||
plugin=None,
|
plugin=None,
|
||||||
|
plugin_url=None,
|
||||||
skip_version_check=False,
|
skip_version_check=False,
|
||||||
advanced=False,
|
advanced=False,
|
||||||
)
|
)
|
||||||
|
|
@ -106,7 +107,8 @@ def test_correct_parsing_args(
|
||||||
debug=True,
|
debug=True,
|
||||||
offline=True,
|
offline=True,
|
||||||
no_pkg_lookups=True,
|
no_pkg_lookups=True,
|
||||||
plugin='pytest_plugin.py',
|
plugin=Path('pytest_plugin.py'),
|
||||||
|
plugin_url=None,
|
||||||
skip_version_check=True,
|
skip_version_check=True,
|
||||||
advanced=True,
|
advanced=True,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue