Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 37 additions & 3 deletions archinstall/lib/args.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import json
import os
import sys
import tempfile
import urllib.error
import urllib.parse
from argparse import ArgumentParser, Namespace
Expand Down Expand Up @@ -56,7 +57,8 @@ class Arguments:
debug: bool = False
offline: 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_wifi_check: bool = False
advanced: bool = False
Expand Down Expand Up @@ -517,10 +519,17 @@ def _define_arguments(self) -> ArgumentParser:
parser.add_argument(
'--plugin',
nargs='?',
type=str,
type=Path,
default=None,
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(
'--skip-version-check',
action='store_true',
Expand Down Expand Up @@ -560,7 +569,11 @@ def _parse_args(self) -> Arguments:
warn(f'Warning: --debug mode will write certain credentials to {logger.path}!')

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)

if args.creds_decryption_key is None:
Expand Down Expand Up @@ -652,6 +665,27 @@ def _fetch_from_url(self, url: str) -> str:

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:
if not path.exists():
error(f'Could not find file {path}')
Expand Down
35 changes: 4 additions & 31 deletions archinstall/lib/plugins.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
import hashlib
import importlib.util
import os
import sys
import urllib.parse
import urllib.request
from importlib import metadata
from pathlib import Path

Expand Down Expand Up @@ -34,23 +31,6 @@ 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)
Expand Down Expand Up @@ -82,17 +62,10 @@ def _import_via_path(path: Path, namespace: str | None = None) -> str:

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)
info(f'Loading plugin from {path}')

if os.path.isfile(path):
namespace = _import_via_path(path)

if namespace and namespace in sys.modules:
# Version dependency via __archinstall__version__ variable (if present) in the plugin
Expand Down
4 changes: 3 additions & 1 deletion tests/test_args.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ def test_default_args(monkeypatch: MonkeyPatch) -> None:
offline=False,
no_pkg_lookups=False,
plugin=None,
plugin_url=None,
skip_version_check=False,
advanced=False,
)
Expand Down Expand Up @@ -106,7 +107,8 @@ def test_correct_parsing_args(
debug=True,
offline=True,
no_pkg_lookups=True,
plugin='pytest_plugin.py',
plugin=Path('pytest_plugin.py'),
plugin_url=None,
skip_version_check=True,
advanced=True,
)
Expand Down