Refactor plugin registry into separate files per plugin
This commit is contained in:
@@ -106,7 +106,7 @@ def build_parser() -> argparse.ArgumentParser:
|
||||
parents=[_common_parent()],
|
||||
)
|
||||
installed.add_argument("--instance", required=True)
|
||||
installed.add_argument("--registry", default="registry/plugins.toml")
|
||||
installed.add_argument("--registry", default="registry/plugins")
|
||||
installed.add_argument("--lockfile")
|
||||
installed.add_argument("--json", action="store_true", help="Print full JSON output")
|
||||
|
||||
@@ -116,7 +116,7 @@ def build_parser() -> argparse.ArgumentParser:
|
||||
parents=[_common_parent()],
|
||||
)
|
||||
check.add_argument("--instance", required=True)
|
||||
check.add_argument("--registry", default="registry/plugins.toml")
|
||||
check.add_argument("--registry", default="registry/plugins")
|
||||
check.add_argument("--lockfile")
|
||||
check.add_argument("--json", action="store_true", help="Print full JSON check output")
|
||||
|
||||
@@ -126,7 +126,7 @@ def build_parser() -> argparse.ArgumentParser:
|
||||
parents=[_common_parent()],
|
||||
)
|
||||
bootstrap.add_argument("--instance", required=True)
|
||||
bootstrap.add_argument("--registry", default="registry/plugins.toml")
|
||||
bootstrap.add_argument("--registry", default="registry/plugins")
|
||||
bootstrap.add_argument("--lockfile")
|
||||
bootstrap.add_argument("--proton", help="Path to Proton executable (Linux/Proton installs)")
|
||||
bootstrap.add_argument("--native", action="store_true", help="Run IPA.exe -n natively instead of through Proton")
|
||||
@@ -146,7 +146,7 @@ def build_parser() -> argparse.ArgumentParser:
|
||||
parents=[_common_parent()],
|
||||
)
|
||||
updates.add_argument("--instance", required=True)
|
||||
updates.add_argument("--registry", default="registry/plugins.toml")
|
||||
updates.add_argument("--registry", default="registry/plugins")
|
||||
updates.add_argument("--lockfile")
|
||||
updates.add_argument("--plugin", action="append", help="Check only this locked plugin id; repeatable")
|
||||
updates.add_argument("--include-prerelease", action="store_true", help="Include prerelease GitHub releases")
|
||||
@@ -158,7 +158,7 @@ def build_parser() -> argparse.ArgumentParser:
|
||||
parents=[_common_parent()],
|
||||
)
|
||||
plan.add_argument("--instance", required=True)
|
||||
plan.add_argument("--registry", default="registry/plugins.toml")
|
||||
plan.add_argument("--registry", default="registry/plugins")
|
||||
plan.add_argument("--lockfile")
|
||||
plan.add_argument("--plugin", "--update", action="append", help="Plan only this locked plugin id; repeatable")
|
||||
|
||||
@@ -193,7 +193,7 @@ def build_parser() -> argparse.ArgumentParser:
|
||||
parents=[_common_parent()],
|
||||
)
|
||||
enable.add_argument("--instance", required=True)
|
||||
enable.add_argument("--registry", default="registry/plugins.toml")
|
||||
enable.add_argument("--registry", default="registry/plugins")
|
||||
enable.add_argument("--lockfile")
|
||||
enable.add_argument("plugin")
|
||||
|
||||
|
||||
+40
-22
@@ -58,33 +58,51 @@ def _load_toml(path: Path) -> dict[str, Any]:
|
||||
return tomllib.load(handle)
|
||||
|
||||
|
||||
def _registry_plugin_from_item(item: dict[str, Any], source: Path) -> RegistryPlugin:
|
||||
dependencies = tuple(
|
||||
Dependency(
|
||||
id=dep["id"],
|
||||
constraint=dep.get("constraint"),
|
||||
required=dep.get("required", True),
|
||||
)
|
||||
for dep in item.get("dependencies", [])
|
||||
)
|
||||
strategy = item.get("install_strategy", "manual")
|
||||
if strategy not in VALID_STRATEGIES:
|
||||
raise ValueError(f"{source}: invalid install_strategy for {item['id']}: {strategy}")
|
||||
return RegistryPlugin(
|
||||
id=item["id"],
|
||||
name=item.get("name", item["id"]),
|
||||
repo=item.get("repo"),
|
||||
asset_patterns=tuple(item.get("asset_patterns", [])),
|
||||
install_strategy=strategy,
|
||||
category=item.get("category"),
|
||||
dependencies=dependencies,
|
||||
)
|
||||
|
||||
|
||||
def _add_registry_plugin(plugins: dict[str, RegistryPlugin], plugin: RegistryPlugin, source: Path) -> None:
|
||||
if plugin.id in plugins:
|
||||
raise ValueError(f"{source}: duplicate plugin id: {plugin.id}")
|
||||
plugins[plugin.id] = plugin
|
||||
|
||||
|
||||
def load_registry(path: Path) -> Registry:
|
||||
if not path.exists():
|
||||
legacy_path = path.with_suffix(".toml")
|
||||
if path.name == "plugins" and legacy_path.exists():
|
||||
return load_registry(legacy_path)
|
||||
return Registry()
|
||||
data = _load_toml(path)
|
||||
plugins: dict[str, RegistryPlugin] = {}
|
||||
if path.is_dir():
|
||||
for item_path in sorted(path.glob("*.toml")):
|
||||
data = _load_toml(item_path)
|
||||
_add_registry_plugin(plugins, _registry_plugin_from_item(data, item_path), item_path)
|
||||
return Registry(plugins)
|
||||
|
||||
data = _load_toml(path)
|
||||
for item in data.get("plugins", []):
|
||||
dependencies = tuple(
|
||||
Dependency(
|
||||
id=dep["id"],
|
||||
constraint=dep.get("constraint"),
|
||||
required=dep.get("required", True),
|
||||
)
|
||||
for dep in item.get("dependencies", [])
|
||||
)
|
||||
strategy = item.get("install_strategy", "manual")
|
||||
if strategy not in VALID_STRATEGIES:
|
||||
raise ValueError(f"{path}: invalid install_strategy for {item['id']}: {strategy}")
|
||||
plugin = RegistryPlugin(
|
||||
id=item["id"],
|
||||
name=item.get("name", item["id"]),
|
||||
repo=item.get("repo"),
|
||||
asset_patterns=tuple(item.get("asset_patterns", [])),
|
||||
install_strategy=strategy,
|
||||
category=item.get("category"),
|
||||
dependencies=dependencies,
|
||||
)
|
||||
plugins[plugin.id] = plugin
|
||||
_add_registry_plugin(plugins, _registry_plugin_from_item(item, path), path)
|
||||
return Registry(plugins)
|
||||
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ def enable_disabled_plugin(
|
||||
instance_path: Path,
|
||||
state_root: Path,
|
||||
plugin_id: str,
|
||||
registry: str = "registry/plugins.toml",
|
||||
registry: str = "registry/plugins",
|
||||
lockfile: str | None = None,
|
||||
repo: Path | None = None,
|
||||
progress: Callable[[str], None] | None = None,
|
||||
@@ -85,7 +85,7 @@ def enable_disabled_plugins(
|
||||
instance_path: Path,
|
||||
state_root: Path,
|
||||
plugin_ids: list[str],
|
||||
registry: str = "registry/plugins.toml",
|
||||
registry: str = "registry/plugins",
|
||||
lockfile: str | None = None,
|
||||
repo: Path | None = None,
|
||||
progress: Callable[[str], None] | None = None,
|
||||
|
||||
@@ -65,6 +65,26 @@ def _asset_matches_patterns(name: str, patterns: tuple[str, ...]) -> bool:
|
||||
return not patterns or any(fnmatch.fnmatch(name, pattern) for pattern in patterns)
|
||||
|
||||
|
||||
def _expand_required_dependencies(selected_ids: set[str], registry: Registry, lockfile: Lockfile) -> set[str]:
|
||||
locked_ids = {plugin.id for plugin in lockfile.plugins}
|
||||
expanded = set(selected_ids)
|
||||
pending = list(selected_ids)
|
||||
while pending:
|
||||
plugin_id = pending.pop()
|
||||
registry_plugin = registry.get(plugin_id)
|
||||
if not registry_plugin:
|
||||
continue
|
||||
for dependency in registry_plugin.dependencies:
|
||||
if not dependency.required:
|
||||
continue
|
||||
if dependency.id not in locked_ids:
|
||||
raise ValueError(f"{plugin_id}: required dependency is not locked for this instance: {dependency.id}")
|
||||
if dependency.id not in expanded:
|
||||
expanded.add(dependency.id)
|
||||
pending.append(dependency.id)
|
||||
return expanded
|
||||
|
||||
|
||||
def create_plan(
|
||||
*,
|
||||
instance: str,
|
||||
@@ -77,7 +97,11 @@ def create_plan(
|
||||
selected: set[str] | None = None,
|
||||
require_bootstrap: bool = True,
|
||||
) -> tuple[dict[str, Any], Path]:
|
||||
selected_ids = selected or {plugin.id for plugin in lockfile.plugins}
|
||||
selected_ids = (
|
||||
_expand_required_dependencies(selected, registry, lockfile)
|
||||
if selected is not None
|
||||
else {plugin.id for plugin in lockfile.plugins}
|
||||
)
|
||||
changes: list[dict[str, Any]] = []
|
||||
warnings: list[str] = []
|
||||
|
||||
|
||||
@@ -269,7 +269,7 @@ class PluginHelperTui(App[int]):
|
||||
lockfile = load_lockfile(self.repo_root / "locks" / f"{target.instance_name}.lock.toml")
|
||||
report = installed_plugins_report(
|
||||
installed_state=load_installed_state(target.state_root, target.instance_name),
|
||||
registry=load_registry(self.repo_root / "registry" / "plugins.toml"),
|
||||
registry=load_registry(self.repo_root / "registry" / "plugins"),
|
||||
lockfile=lockfile,
|
||||
)
|
||||
self.plugin_rows = report["plugins"]
|
||||
|
||||
Reference in New Issue
Block a user