Files
NebulaShell/store/NebulaShell/auto-dependency/main.py

232 lines
7.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import subprocess
import shutil
import json
from pathlib import Path
from typing import Any, Optional, List, Dict
from oss.plugin.types import Plugin
class SystemDependencyChecker:
for pm, commands in self.package_managers.items():
for cmd in commands:
if shutil.which(cmd):
return pm
return "unknown"
def check_command(self, command: str) -> bool:
if not self.detected_pm or self.detected_pm == "unknown":
return False
try:
if self.detected_pm in ["apt", "apt-get"]:
result = subprocess.run(
["dpkg", "-l", package],
capture_output=True,
text=True,
timeout=30
)
return result.returncode == 0 and "ii" in result.stdout
elif self.detected_pm in ["yum", "dnf"]:
result = subprocess.run(
["rpm", "-q", package],
capture_output=True,
text=True,
timeout=30
)
return result.returncode == 0
elif self.detected_pm == "pacman":
result = subprocess.run(
["pacman", "-Q", package],
capture_output=True,
text=True,
timeout=30
)
return result.returncode == 0
elif self.detected_pm == "brew":
result = subprocess.run(
["brew", "list", package],
capture_output=True,
text=True,
timeout=30
)
return result.returncode == 0
elif self.detected_pm == "apk":
result = subprocess.run(
["apk", "info", "-e", package],
capture_output=True,
text=True,
timeout=30
)
return result.returncode == 0
except Exception as e:
import traceback; print(f"[main.py] 错误:{type(e).__name__}:{e}"); traceback.print_exc()
pass
return False
def install_package(self, package: str) -> bool:
result = {
"package": package,
"installed": self.check_package(package),
"action": "none",
"success": True,
"message": ""
}
if result["installed"]:
result["message"] = f"'{package}' 已安装"
return result
if not auto_install:
result["action"] = "skipped"
result["message"] = f"'{package}' 未安装,但自动安装已禁用"
result["success"] = False
return result
result["action"] = "installing"
if self.install_package(package):
result["installed"] = True
result["success"] = True
result["message"] = f"'{package}' 安装成功"
else:
result["success"] = False
result["message"] = f"'{package}' 安装失败"
return result
class AutoDependencyPlugin(Plugin):
if deps:
self.scan_dirs = deps.get("scan_dirs", ["store"])
self.auto_install = deps.get("auto_install", True)
if "plugin-loader" in deps:
self._plugin_loader_ref = deps["plugin-loader"]
def start(self):
pass
def scan_plugin_manifests(self, base_dir: str = "store") -> List[Dict[str, Any]]:
results = []
base_path = Path(base_dir)
if not base_path.exists():
return results
for vendor_dir in base_path.iterdir():
if not vendor_dir.is_dir():
continue
for plugin_dir in vendor_dir.iterdir():
if not plugin_dir.is_dir():
continue
manifest_file = plugin_dir / "manifest.json"
if not manifest_file.exists():
continue
try:
with open(manifest_file, "r", encoding="utf-8") as f:
manifest = json.load(f)
system_deps = manifest.get("system_dependencies", [])
results.append({
"plugin_name": plugin_dir.name.rstrip("}"),
"plugin_dir": str(plugin_dir),
"manifest": manifest,
"system_dependencies": system_deps
})
except Exception as e:
import traceback; print(f"[main.py] 错误:{type(e).__name__}:{e}"); traceback.print_exc()
continue
return results
def check_all_dependencies(self, base_dir: str = "store") -> Dict[str, Any]:
plugins = self.scan_plugin_manifests(base_dir)
all_deps = {} for plugin in plugins:
for dep in plugin["system_dependencies"]:
if dep not in all_deps:
all_deps[dep] = []
all_deps[dep].append(plugin["plugin_name"])
results = []
installed_count = 0
missing_count = 0
for package, plugin_names in all_deps.items():
is_installed = self.checker.check_package(package)
if is_installed:
installed_count += 1
else:
missing_count += 1
results.append({
"package": package,
"installed": is_installed,
"required_by": plugin_names
})
return {
"total_plugins": len(plugins),
"plugins_with_deps": sum(1 for p in plugins if p["system_dependencies"]),
"dependencies": results,
"missing_count": missing_count,
"installed_count": installed_count
}
def install_missing_dependencies(self, base_dir: str = "store") -> Dict[str, Any]:
check_result = self.check_all_dependencies(base_dir)
to_install = [dep for dep in check_result["dependencies"] if not dep["installed"]]
install_results = []
success_count = 0
failed_count = 0
for dep in to_install:
result = self.checker.check_and_install(dep["package"], auto_install=True)
result["required_by"] = dep["required_by"]
install_results.append(result)
if result["success"]:
success_count += 1
else:
failed_count += 1
return {
"total_to_install": len(to_install),
"success_count": success_count,
"failed_count": failed_count,
"results": install_results
}
def get_system_info(self) -> Dict[str, Any]:
通过 PL 注入机制向插件加载器注册以下功能
- auto-dependency:scan: 扫描所有插件的系统依赖
- auto-dependency:check: 检查依赖安装状态
- auto-dependency:install: 安装缺失的依赖
- auto-dependency:info: 获取插件系统信息
def scan_deps(scan_dir: str = "store") -> Dict[str, Any]:
return self.check_all_dependencies(scan_dir)
injector.register_function(
"auto-dependency:check",
check_deps,
"检查所有插件声明的系统依赖是否已安装"
)
def install_deps(scan_dir: str = "store") -> Dict[str, Any]:
return self.get_system_info()
injector.register_function(
"auto-dependency:info",
get_info,
"获取自动依赖插件的系统信息"
)
def New() -> AutoDependencyPlugin: