Auto-bootstrap BSIPA when re-enabling plugins
Run bootstrap automatically on enable paths when health is bad, skip bsipa in disable-all, and add tests for the shared bootstrap gate. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,15 +1,34 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Callable
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from .bootstrap import ensure_healthy_bootstrap
|
||||
from .config import repo_root
|
||||
from .installer import apply_plan
|
||||
from .models import load_lockfile, load_registry
|
||||
from .models import Lockfile, Registry, load_lockfile, load_registry
|
||||
from .planner import create_plan
|
||||
from .state import load_installed_state
|
||||
|
||||
|
||||
def _resolve_paths(
|
||||
*,
|
||||
instance: str,
|
||||
registry: str,
|
||||
lockfile: str | None,
|
||||
repo: Path | None,
|
||||
) -> tuple[Path, Path, Path, Lockfile, Registry]:
|
||||
root = repo or repo_root()
|
||||
registry_path = (root / registry).resolve() if not Path(registry).is_absolute() else Path(registry)
|
||||
lock_path = Path(lockfile) if lockfile else root / "locks" / f"{instance}.lock.toml"
|
||||
if not lock_path.is_absolute():
|
||||
lock_path = (root / lock_path).resolve()
|
||||
loaded_lockfile = load_lockfile(lock_path)
|
||||
loaded_registry = load_registry(registry_path)
|
||||
return root, registry_path, lock_path, loaded_lockfile, loaded_registry
|
||||
|
||||
|
||||
def enable_disabled_plugin(
|
||||
*,
|
||||
instance: str,
|
||||
@@ -19,25 +38,38 @@ def enable_disabled_plugin(
|
||||
registry: str = "registry/plugins.toml",
|
||||
lockfile: str | None = None,
|
||||
repo: Path | None = None,
|
||||
progress: Callable[[str], None] | None = None,
|
||||
) -> dict[str, Any]:
|
||||
installed_state = load_installed_state(state_root, instance)
|
||||
if plugin_id not in installed_state.get("disabledPlugins", {}):
|
||||
raise KeyError(f"plugin is not recorded as disabled: {plugin_id}")
|
||||
|
||||
root = repo or repo_root()
|
||||
registry_path = (root / registry).resolve() if not Path(registry).is_absolute() else Path(registry)
|
||||
lock_path = Path(lockfile) if lockfile else root / "locks" / f"{instance}.lock.toml"
|
||||
if not lock_path.is_absolute():
|
||||
lock_path = (root / lock_path).resolve()
|
||||
loaded_lockfile = load_lockfile(lock_path)
|
||||
root, registry_path, _lock_path, loaded_lockfile, loaded_registry = _resolve_paths(
|
||||
instance=instance,
|
||||
registry=registry,
|
||||
lockfile=lockfile,
|
||||
repo=repo,
|
||||
)
|
||||
if not any(plugin.id == plugin_id for plugin in loaded_lockfile.plugins):
|
||||
raise KeyError(f"plugin is disabled but not locked for this instance: {plugin_id}")
|
||||
|
||||
ensure_healthy_bootstrap(
|
||||
instance=instance,
|
||||
instance_path=instance_path,
|
||||
beat_saber_version=loaded_lockfile.beat_saber_version,
|
||||
registry=loaded_registry,
|
||||
lockfile=loaded_lockfile,
|
||||
state_root=state_root,
|
||||
repo_root=root,
|
||||
selected_ids={plugin_id},
|
||||
progress=progress,
|
||||
)
|
||||
|
||||
plan, path = create_plan(
|
||||
instance=instance,
|
||||
instance_path=instance_path,
|
||||
beat_saber_version=loaded_lockfile.beat_saber_version,
|
||||
registry=load_registry(registry_path),
|
||||
registry=loaded_registry,
|
||||
lockfile=loaded_lockfile,
|
||||
state_root=state_root,
|
||||
repo_root=root,
|
||||
@@ -45,3 +77,69 @@ def enable_disabled_plugin(
|
||||
)
|
||||
result = apply_plan(plan, state_root)
|
||||
return {"planPath": str(path), **result}
|
||||
|
||||
|
||||
def enable_disabled_plugins(
|
||||
*,
|
||||
instance: str,
|
||||
instance_path: Path,
|
||||
state_root: Path,
|
||||
plugin_ids: list[str],
|
||||
registry: str = "registry/plugins.toml",
|
||||
lockfile: str | None = None,
|
||||
repo: Path | None = None,
|
||||
progress: Callable[[str], None] | None = None,
|
||||
) -> dict[str, Any]:
|
||||
if not plugin_ids:
|
||||
return {"enabled": [], "errors": []}
|
||||
|
||||
root, _registry_path, _lock_path, loaded_lockfile, loaded_registry = _resolve_paths(
|
||||
instance=instance,
|
||||
registry=registry,
|
||||
lockfile=lockfile,
|
||||
repo=repo,
|
||||
)
|
||||
installed_state = load_installed_state(state_root, instance)
|
||||
disabled_plugins = installed_state.get("disabledPlugins", {})
|
||||
selected_ids = set(plugin_ids)
|
||||
|
||||
ensure_healthy_bootstrap(
|
||||
instance=instance,
|
||||
instance_path=instance_path,
|
||||
beat_saber_version=loaded_lockfile.beat_saber_version,
|
||||
registry=loaded_registry,
|
||||
lockfile=loaded_lockfile,
|
||||
state_root=state_root,
|
||||
repo_root=root,
|
||||
selected_ids=selected_ids,
|
||||
progress=progress,
|
||||
)
|
||||
|
||||
enabled: list[dict[str, Any]] = []
|
||||
errors: list[dict[str, str]] = []
|
||||
for plugin_id in plugin_ids:
|
||||
if plugin_id not in disabled_plugins:
|
||||
errors.append({"plugin": plugin_id, "error": f"plugin is not recorded as disabled: {plugin_id}"})
|
||||
continue
|
||||
if not any(plugin.id == plugin_id for plugin in loaded_lockfile.plugins):
|
||||
errors.append(
|
||||
{"plugin": plugin_id, "error": f"plugin is disabled but not locked for this instance: {plugin_id}"}
|
||||
)
|
||||
continue
|
||||
try:
|
||||
plan, path = create_plan(
|
||||
instance=instance,
|
||||
instance_path=instance_path,
|
||||
beat_saber_version=loaded_lockfile.beat_saber_version,
|
||||
registry=loaded_registry,
|
||||
lockfile=loaded_lockfile,
|
||||
state_root=state_root,
|
||||
repo_root=root,
|
||||
selected={plugin_id},
|
||||
)
|
||||
result = apply_plan(plan, state_root)
|
||||
enabled.append({"plugin": plugin_id, "planPath": str(path), "applied": len(result["applied"])})
|
||||
except Exception as exc:
|
||||
errors.append({"plugin": plugin_id, "error": str(exc)})
|
||||
|
||||
return {"enabled": enabled, "errors": errors}
|
||||
|
||||
Reference in New Issue
Block a user