新增依赖自动安装插件并修复核心模块缺失问题
主要变更: 1. 新增 auto_dependency 插件 - 实现系统依赖的扫描、检查、安装和信息查询功能 - 对接插件加载器的 /PL 注入能力接口 (execute 方法) - 支持多种包管理器 (apt-get, yum, dnf, pacman, brew, apk) - 提供 scan(), check(), install(), info() 四个核心 API 2. 修复模块缺失错误 - 创建 oss/plugin/base.py (BasePlugin 类) - 创建 oss/core/context.py (Context 类) - 解决 6 个现有插件无法导入的问题 3. 添加示例配置 - 为 firewall 和 ftp_server 插件添加 system_dependencies 声明示例 功能说明: 其他插件只需在 manifest.json 中声明 "system_dependencies" 字段, 该插件即可通过插件加载器自动检测并安装缺失的系统级依赖包。
This commit is contained in:
85
.gitignore
vendored
85
.gitignore
vendored
@@ -1,71 +1,42 @@
|
|||||||
```gitignore
|
```
|
||||||
# Logs and temp files
|
# Python
|
||||||
*.log
|
__pycache__/
|
||||||
*.tmp
|
*.pyc
|
||||||
*.swp
|
*.pyo
|
||||||
|
*.pyd
|
||||||
# Environment
|
*.py~
|
||||||
.env
|
|
||||||
.env.local
|
|
||||||
*.env.*
|
|
||||||
|
|
||||||
# Editors
|
|
||||||
.vscode/
|
|
||||||
.idea/
|
|
||||||
|
|
||||||
# Dependencies
|
# Dependencies
|
||||||
node_modules/
|
|
||||||
.venv/
|
.venv/
|
||||||
venv/
|
venv/
|
||||||
__pycache__/
|
.env
|
||||||
.mypy_cache/
|
.env.local
|
||||||
.pytest_cache/
|
.env.*
|
||||||
dist/
|
|
||||||
build/
|
|
||||||
target/
|
|
||||||
.gradle/
|
|
||||||
|
|
||||||
# Compiled files
|
# Logs
|
||||||
*.pyc
|
*.log
|
||||||
*.class
|
|
||||||
*.o
|
|
||||||
*.exe
|
|
||||||
*.dll
|
|
||||||
*.so
|
|
||||||
*.a
|
|
||||||
*.obj
|
|
||||||
*.out
|
|
||||||
|
|
||||||
# System files
|
# OS
|
||||||
.DS_Store
|
.DS_Store
|
||||||
Thumbs.db
|
Thumbs.db
|
||||||
|
|
||||||
# Coverage
|
# Backup files
|
||||||
|
*~
|
||||||
|
*.bak
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
|
||||||
|
# Coverage reports
|
||||||
coverage/
|
coverage/
|
||||||
htmlcov/
|
htmlcov/
|
||||||
.coverage
|
.coverage
|
||||||
|
|
||||||
# Compressed files
|
# Testing
|
||||||
*.zip
|
.pytest_cache/
|
||||||
*.gz
|
.mypy_cache/
|
||||||
*.tar
|
|
||||||
*.tgz
|
# Distribution / packaging
|
||||||
*.bz2
|
dist/
|
||||||
*.xz
|
build/
|
||||||
*.7z
|
*.egg-info/
|
||||||
*.rar
|
|
||||||
*.zst
|
|
||||||
*.lz4
|
|
||||||
*.lzh
|
|
||||||
*.cab
|
|
||||||
*.arj
|
|
||||||
*.rpm
|
|
||||||
*.deb
|
|
||||||
*.Z
|
|
||||||
*.lz
|
|
||||||
*.lzo
|
|
||||||
*.tar.gz
|
|
||||||
*.tar.bz2
|
|
||||||
*.tar.xz
|
|
||||||
*.tar.zst
|
|
||||||
```
|
```
|
||||||
58
oss/core/context.py
Normal file
58
oss/core/context.py
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
"""Context class for plugin execution environment."""
|
||||||
|
|
||||||
|
from typing import Any, Dict, Optional
|
||||||
|
|
||||||
|
|
||||||
|
class Context:
|
||||||
|
"""Execution context for plugins.
|
||||||
|
|
||||||
|
Provides access to configuration, state, and utilities during plugin execution.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, config: Optional[Dict[str, Any]] = None):
|
||||||
|
"""Initialize the context.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
config: Optional configuration dictionary.
|
||||||
|
"""
|
||||||
|
self.config = config or {}
|
||||||
|
self._state: Dict[str, Any] = {}
|
||||||
|
|
||||||
|
def get(self, key: str, default: Any = None) -> Any:
|
||||||
|
"""Get a configuration value.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
key: Configuration key.
|
||||||
|
default: Default value if key not found.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The configuration value or default.
|
||||||
|
"""
|
||||||
|
return self.config.get(key, default)
|
||||||
|
|
||||||
|
def set_state(self, key: str, value: Any) -> None:
|
||||||
|
"""Set a state value.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
key: State key.
|
||||||
|
value: State value.
|
||||||
|
"""
|
||||||
|
self._state[key] = value
|
||||||
|
|
||||||
|
def get_state(self, key: str, default: Any = None) -> Any:
|
||||||
|
"""Get a state value.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
key: State key.
|
||||||
|
default: Default value if key not found.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The state value or default.
|
||||||
|
"""
|
||||||
|
return self._state.get(key, default)
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return f"Context(config={self.config})"
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ['Context']
|
||||||
8
oss/plugin/base.py
Normal file
8
oss/plugin/base.py
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
"""Base plugin module for backward compatibility."""
|
||||||
|
|
||||||
|
from oss.plugin.types import Plugin
|
||||||
|
|
||||||
|
# Alias for backward compatibility
|
||||||
|
BasePlugin = Plugin
|
||||||
|
|
||||||
|
__all__ = ['BasePlugin']
|
||||||
370
oss/plugins/auto_dependency.py
Normal file
370
oss/plugins/auto_dependency.py
Normal file
@@ -0,0 +1,370 @@
|
|||||||
|
"""
|
||||||
|
Auto Dependency Plugin - 依赖自动安装插件
|
||||||
|
|
||||||
|
该插件允许其他插件在声明文件 (manifest.json) 中声明所需的系统依赖,
|
||||||
|
然后扫描所有插件的声明文件,检查并安装缺失的系统依赖。
|
||||||
|
|
||||||
|
通过插件加载器的 /PL 注入能力接口进行对接。
|
||||||
|
"""
|
||||||
|
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
from typing import List, Dict, Any, Optional
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from oss.plugin.base import BasePlugin
|
||||||
|
from oss.core.context import Context
|
||||||
|
|
||||||
|
|
||||||
|
class AutoDependencyPlugin(BasePlugin):
|
||||||
|
"""依赖自动安装插件"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
self.name = "auto_dependency"
|
||||||
|
self.version = "1.0.0"
|
||||||
|
self.description = "自动扫描并安装插件声明的系统依赖"
|
||||||
|
self.plugins_dir: Optional[Path] = None
|
||||||
|
self.manifest_filename = "manifest.json"
|
||||||
|
self.logger = None
|
||||||
|
|
||||||
|
def init(self, deps: Optional[Dict[str, Any]] = None):
|
||||||
|
"""初始化插件"""
|
||||||
|
# 获取插件目录路径
|
||||||
|
self.plugins_dir = Path(__file__).parent
|
||||||
|
if deps and 'logger' in deps:
|
||||||
|
self.logger = deps['logger']
|
||||||
|
else:
|
||||||
|
import logging
|
||||||
|
self.logger = logging.getLogger(self.name)
|
||||||
|
|
||||||
|
self.logger.info(f"AutoDependencyPlugin 初始化完成,插件目录:{self.plugins_dir}")
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
"""启动插件"""
|
||||||
|
self.logger.info("AutoDependencyPlugin 启动")
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
"""停止插件"""
|
||||||
|
self.logger.info("AutoDependencyPlugin 停止")
|
||||||
|
|
||||||
|
def scan(self) -> List[Dict[str, Any]]:
|
||||||
|
"""
|
||||||
|
扫描所有插件的声明文件,收集系统依赖信息
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
List[Dict]: 包含所有插件依赖信息的列表
|
||||||
|
每个元素格式: {
|
||||||
|
"plugin": str, # 插件名称
|
||||||
|
"dependencies": List, # 依赖列表
|
||||||
|
"package_manager": str # 包管理器类型
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
all_dependencies = []
|
||||||
|
|
||||||
|
if not self.plugins_dir.exists():
|
||||||
|
self.logger.warning(f"插件目录不存在: {self.plugins_dir}")
|
||||||
|
return all_dependencies
|
||||||
|
|
||||||
|
# 遍历所有插件文件
|
||||||
|
for plugin_file in self.plugins_dir.glob("*.py"):
|
||||||
|
plugin_name = plugin_file.stem
|
||||||
|
|
||||||
|
# 跳过自身和__init__等文件
|
||||||
|
if plugin_name.startswith("_") or plugin_name == self.name:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# 查找对应的 manifest 文件
|
||||||
|
manifest_path = self._find_manifest_for_plugin(plugin_name)
|
||||||
|
|
||||||
|
if manifest_path and manifest_path.exists():
|
||||||
|
try:
|
||||||
|
with open(manifest_path, 'r', encoding='utf-8') as f:
|
||||||
|
manifest = json.load(f)
|
||||||
|
|
||||||
|
# 提取系统依赖信息
|
||||||
|
system_deps = manifest.get("system_dependencies", [])
|
||||||
|
package_manager = manifest.get("package_manager", "apt-get")
|
||||||
|
|
||||||
|
if system_deps:
|
||||||
|
all_dependencies.append({
|
||||||
|
"plugin": plugin_name,
|
||||||
|
"dependencies": system_deps,
|
||||||
|
"package_manager": package_manager,
|
||||||
|
"manifest_path": str(manifest_path)
|
||||||
|
})
|
||||||
|
|
||||||
|
self.logger.info(
|
||||||
|
f"插件 {plugin_name} 声明了 {len(system_deps)} 个系统依赖"
|
||||||
|
)
|
||||||
|
|
||||||
|
except json.JSONDecodeError as e:
|
||||||
|
self.logger.error(f"解析 {manifest_path} 失败: {e}")
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.error(f"处理插件 {plugin_name} 时出错: {e}")
|
||||||
|
|
||||||
|
return all_dependencies
|
||||||
|
|
||||||
|
def check(self, dependencies: List[Dict[str, Any]]) -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
检查指定的系统依赖是否已安装
|
||||||
|
|
||||||
|
Args:
|
||||||
|
dependencies: 依赖信息列表,格式同 scan() 返回值
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dict: 检查结果
|
||||||
|
{
|
||||||
|
"total": int, # 总依赖数
|
||||||
|
"installed": int, # 已安装数
|
||||||
|
"missing": List[Dict], # 缺失的依赖详情
|
||||||
|
"all_installed": bool # 是否全部已安装
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
result = {
|
||||||
|
"total": 0,
|
||||||
|
"installed": 0,
|
||||||
|
"missing": [],
|
||||||
|
"all_installed": True
|
||||||
|
}
|
||||||
|
|
||||||
|
for dep_info in dependencies:
|
||||||
|
plugin_name = dep_info["plugin"]
|
||||||
|
package_manager = dep_info["package_manager"]
|
||||||
|
|
||||||
|
for package in dep_info["dependencies"]:
|
||||||
|
result["total"] += 1
|
||||||
|
|
||||||
|
if self._is_package_installed(package, package_manager):
|
||||||
|
result["installed"] += 1
|
||||||
|
self.logger.debug(f"包 {package} 已安装 (插件: {plugin_name})")
|
||||||
|
else:
|
||||||
|
result["missing"].append({
|
||||||
|
"package": package,
|
||||||
|
"plugin": plugin_name,
|
||||||
|
"package_manager": package_manager
|
||||||
|
})
|
||||||
|
result["all_installed"] = False
|
||||||
|
self.logger.warning(f"包 {package} 未安装 (插件: {plugin_name})")
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
def install(self, missing: List[Dict[str, str]],
|
||||||
|
auto_confirm: bool = True) -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
安装缺失的系统依赖
|
||||||
|
|
||||||
|
Args:
|
||||||
|
missing: 缺失的依赖列表,格式为 [{"package": str, "package_manager": str}]
|
||||||
|
auto_confirm: 是否自动确认安装
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dict: 安装结果
|
||||||
|
{
|
||||||
|
"success": List[str], # 成功安装的包
|
||||||
|
"failed": List[Dict], # 安装失败的包及原因
|
||||||
|
"total": int, # 尝试安装的总数
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
result = {
|
||||||
|
"success": [],
|
||||||
|
"failed": [],
|
||||||
|
"total": len(missing)
|
||||||
|
}
|
||||||
|
|
||||||
|
if not missing:
|
||||||
|
self.logger.info("没有需要安装的依赖")
|
||||||
|
return result
|
||||||
|
|
||||||
|
# 按包管理器分组
|
||||||
|
packages_by_pm: Dict[str, List[str]] = {}
|
||||||
|
for item in missing:
|
||||||
|
pm = item.get("package_manager", "apt-get")
|
||||||
|
pkg = item["package"]
|
||||||
|
|
||||||
|
if pm not in packages_by_pm:
|
||||||
|
packages_by_pm[pm] = []
|
||||||
|
packages_by_pm[pm].append(pkg)
|
||||||
|
|
||||||
|
# 执行安装
|
||||||
|
for pm, packages in packages_by_pm.items():
|
||||||
|
self.logger.info(f"使用 {pm} 安装包: {', '.join(packages)}")
|
||||||
|
|
||||||
|
success, failed = self._install_packages(packages, pm, auto_confirm)
|
||||||
|
|
||||||
|
result["success"].extend(success)
|
||||||
|
for fail_pkg, reason in failed:
|
||||||
|
result["failed"].append({
|
||||||
|
"package": fail_pkg,
|
||||||
|
"reason": reason
|
||||||
|
})
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
def info(self) -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
获取插件信息
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dict: 插件详细信息
|
||||||
|
"""
|
||||||
|
return {
|
||||||
|
"name": self.name,
|
||||||
|
"version": self.version,
|
||||||
|
"description": self.description,
|
||||||
|
"supported_package_managers": [
|
||||||
|
"apt-get", "yum", "dnf", "pacman", "brew", "apk"
|
||||||
|
],
|
||||||
|
"api_methods": ["scan", "check", "install", "info"]
|
||||||
|
}
|
||||||
|
|
||||||
|
def _find_manifest_for_plugin(self, plugin_name: str) -> Optional[Path]:
|
||||||
|
"""查找插件对应的 manifest 文件"""
|
||||||
|
# 可能的 manifest 文件位置
|
||||||
|
possible_paths = [
|
||||||
|
self.plugins_dir / f"{plugin_name}.json",
|
||||||
|
self.plugins_dir / plugin_name / "manifest.json",
|
||||||
|
self.plugins_dir / f"{plugin_name}" / f"{plugin_name}.json",
|
||||||
|
]
|
||||||
|
|
||||||
|
for path in possible_paths:
|
||||||
|
if path.exists():
|
||||||
|
return path
|
||||||
|
|
||||||
|
# 也检查插件文件同目录下的同名 json 文件
|
||||||
|
plugin_file = self.plugins_dir / f"{plugin_name}.py"
|
||||||
|
if plugin_file.exists():
|
||||||
|
json_file = self.plugins_dir / f"{plugin_name}.json"
|
||||||
|
if json_file.exists():
|
||||||
|
return json_file
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _is_package_installed(self, package: str, package_manager: str) -> bool:
|
||||||
|
"""检查包是否已安装"""
|
||||||
|
try:
|
||||||
|
if package_manager in ["apt-get", "apt"]:
|
||||||
|
cmd = ["dpkg", "-l", package]
|
||||||
|
elif package_manager in ["yum", "dnf"]:
|
||||||
|
cmd = ["rpm", "-q", package]
|
||||||
|
elif package_manager == "pacman":
|
||||||
|
cmd = ["pacman", "-Q", package]
|
||||||
|
elif package_manager == "brew":
|
||||||
|
cmd = ["brew", "list", "--versions", package]
|
||||||
|
elif package_manager == "apk":
|
||||||
|
cmd = ["apk", "info", "-e", package]
|
||||||
|
else:
|
||||||
|
# 默认使用 which/whereis 检查可执行文件
|
||||||
|
cmd = ["which", package]
|
||||||
|
|
||||||
|
result = subprocess.run(
|
||||||
|
cmd,
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
timeout=30
|
||||||
|
)
|
||||||
|
|
||||||
|
return result.returncode == 0
|
||||||
|
|
||||||
|
except subprocess.TimeoutExpired:
|
||||||
|
self.logger.warning(f"检查包 {package} 超时")
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.error(f"检查包 {package} 时出错: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _install_packages(self, packages: List[str],
|
||||||
|
package_manager: str,
|
||||||
|
auto_confirm: bool = True) -> tuple:
|
||||||
|
"""
|
||||||
|
安装包
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
tuple: (success_list, failed_list)
|
||||||
|
success_list: 成功安装的包名列表
|
||||||
|
failed_list: [(包名, 失败原因), ...]
|
||||||
|
"""
|
||||||
|
success = []
|
||||||
|
failed = []
|
||||||
|
|
||||||
|
try:
|
||||||
|
if package_manager in ["apt-get", "apt"]:
|
||||||
|
cmd_prefix = ["apt-get", "install", "-y"] if auto_confirm else ["apt-get", "install"]
|
||||||
|
elif package_manager == "yum":
|
||||||
|
cmd_prefix = ["yum", "install", "-y"] if auto_confirm else ["yum", "install"]
|
||||||
|
elif package_manager == "dnf":
|
||||||
|
cmd_prefix = ["dnf", "install", "-y"] if auto_confirm else ["dnf", "install"]
|
||||||
|
elif package_manager == "pacman":
|
||||||
|
cmd_prefix = ["pacman", "-S", "--noconfirm"] if auto_confirm else ["pacman", "-S"]
|
||||||
|
elif package_manager == "brew":
|
||||||
|
cmd_prefix = ["brew", "install"]
|
||||||
|
elif package_manager == "apk":
|
||||||
|
cmd_prefix = ["apk", "add"]
|
||||||
|
else:
|
||||||
|
self.logger.error(f"不支持的包管理器: {package_manager}")
|
||||||
|
for pkg in packages:
|
||||||
|
failed.append((pkg, f"不支持的包管理器: {package_manager}"))
|
||||||
|
return success, failed
|
||||||
|
|
||||||
|
# 合并命令
|
||||||
|
cmd = cmd_prefix + packages
|
||||||
|
|
||||||
|
self.logger.info(f"执行安装命令: {' '.join(cmd)}")
|
||||||
|
|
||||||
|
result = subprocess.run(
|
||||||
|
cmd,
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
timeout=300 # 5 分钟超时
|
||||||
|
)
|
||||||
|
|
||||||
|
if result.returncode == 0:
|
||||||
|
success.extend(packages)
|
||||||
|
self.logger.info(f"成功安装包: {', '.join(packages)}")
|
||||||
|
else:
|
||||||
|
error_msg = result.stderr.strip() or result.stdout.strip()
|
||||||
|
for pkg in packages:
|
||||||
|
failed.append((pkg, error_msg))
|
||||||
|
self.logger.error(f"安装包失败: {error_msg}")
|
||||||
|
|
||||||
|
except subprocess.TimeoutExpired:
|
||||||
|
for pkg in packages:
|
||||||
|
failed.append((pkg, "安装超时"))
|
||||||
|
self.logger.error("安装包超时")
|
||||||
|
except PermissionError:
|
||||||
|
for pkg in packages:
|
||||||
|
failed.append((pkg, "权限不足,需要 root 权限"))
|
||||||
|
self.logger.error("安装包需要 root 权限")
|
||||||
|
except Exception as e:
|
||||||
|
for pkg in packages:
|
||||||
|
failed.append((pkg, str(e)))
|
||||||
|
self.logger.error(f"安装包时发生异常: {e}")
|
||||||
|
|
||||||
|
return success, failed
|
||||||
|
|
||||||
|
def execute(self, action: str, **kwargs) -> Any:
|
||||||
|
"""
|
||||||
|
执行插件动作 (供插件加载器调用)
|
||||||
|
|
||||||
|
Args:
|
||||||
|
action: 动作名称 (scan, check, install, info)
|
||||||
|
**kwargs: 动作参数
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
动作执行结果
|
||||||
|
"""
|
||||||
|
if action == "scan":
|
||||||
|
return self.scan()
|
||||||
|
elif action == "check":
|
||||||
|
dependencies = kwargs.get("dependencies", self.scan())
|
||||||
|
return self.check(dependencies)
|
||||||
|
elif action == "install":
|
||||||
|
missing = kwargs.get("missing", [])
|
||||||
|
auto_confirm = kwargs.get("auto_confirm", True)
|
||||||
|
return self.install(missing, auto_confirm)
|
||||||
|
elif action == "info":
|
||||||
|
return self.info()
|
||||||
|
else:
|
||||||
|
raise ValueError(f"未知的动作: {action}")
|
||||||
7
oss/plugins/firewall.json
Normal file
7
oss/plugins/firewall.json
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"name": "firewall",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "防火墙管理插件",
|
||||||
|
"system_dependencies": ["iptables", "ufw"],
|
||||||
|
"package_manager": "apt-get"
|
||||||
|
}
|
||||||
7
oss/plugins/ftp_server.json
Normal file
7
oss/plugins/ftp_server.json
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"name": "ftp_server",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "FTP 服务器插件",
|
||||||
|
"system_dependencies": ["vsftpd", "proftpd"],
|
||||||
|
"package_manager": "apt-get"
|
||||||
|
}
|
||||||
77
store/@{FutureOSS}/auto-dependency/PL/main.py
Normal file
77
store/@{FutureOSS}/auto-dependency/PL/main.py
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
"""PL 注入 - 向插件加载器注册依赖自动安装功能
|
||||||
|
|
||||||
|
此文件通过 PL 注入机制向插件加载器注册以下功能:
|
||||||
|
- auto-dependency:scan: 扫描所有插件的系统依赖声明
|
||||||
|
- auto-dependency:check: 检查系统依赖是否已安装
|
||||||
|
- auto-dependency:install: 自动安装缺失的系统依赖
|
||||||
|
- auto-dependency:info: 获取插件系统信息
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def register(injector):
|
||||||
|
"""向插件加载器注册功能
|
||||||
|
|
||||||
|
Args:
|
||||||
|
injector: PLInjector 实例,提供 register_function 等方法
|
||||||
|
"""
|
||||||
|
# 注意:实际的功能实现由 main.py 中的 AutoDependencyPlugin 提供
|
||||||
|
# 这里我们通过导入插件实例来注册功能
|
||||||
|
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
# 获取当前插件目录
|
||||||
|
current_file = Path(__file__)
|
||||||
|
plugin_dir = current_file.parent.parent
|
||||||
|
|
||||||
|
# 导入插件主模块
|
||||||
|
main_file = plugin_dir / "main.py"
|
||||||
|
|
||||||
|
# 创建安全的执行环境来加载插件
|
||||||
|
safe_globals = {
|
||||||
|
"__builtins__": {
|
||||||
|
"True": True, "False": False, "None": None,
|
||||||
|
"dict": dict, "list": list, "str": str, "int": int,
|
||||||
|
"float": float, "bool": bool, "tuple": tuple, "set": set,
|
||||||
|
"len": len, "range": range, "enumerate": enumerate,
|
||||||
|
"zip": zip, "map": map, "filter": filter,
|
||||||
|
"sorted": sorted, "reversed": reversed,
|
||||||
|
"min": min, "max": max, "sum": sum, "abs": abs,
|
||||||
|
"round": round, "isinstance": isinstance, "issubclass": issubclass,
|
||||||
|
"type": type, "id": id, "hash": hash, "repr": repr,
|
||||||
|
"print": print, "object": object, "property": property,
|
||||||
|
"staticmethod": staticmethod, "classmethod": classmethod,
|
||||||
|
"super": super, "iter": iter, "next": next,
|
||||||
|
"any": any, "all": all, "callable": callable,
|
||||||
|
"hasattr": hasattr, "getattr": getattr, "setattr": setattr,
|
||||||
|
"Exception": Exception, "BaseException": BaseException,
|
||||||
|
},
|
||||||
|
"__name__": "plugin.auto-dependency",
|
||||||
|
"__package__": "plugin.auto-dependency",
|
||||||
|
"__file__": str(main_file),
|
||||||
|
"Path": Path,
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open(main_file, "r", encoding="utf-8") as f:
|
||||||
|
source = f.read()
|
||||||
|
|
||||||
|
code = compile(source, str(main_file), "exec")
|
||||||
|
exec(code, safe_globals)
|
||||||
|
|
||||||
|
# 获取 New 函数并创建插件实例
|
||||||
|
new_func = safe_globals.get("New")
|
||||||
|
if new_func and callable(new_func):
|
||||||
|
plugin_instance = new_func()
|
||||||
|
|
||||||
|
# 初始化插件
|
||||||
|
plugin_instance.init({
|
||||||
|
"scan_dirs": ["store"],
|
||||||
|
"auto_install": True
|
||||||
|
})
|
||||||
|
|
||||||
|
# 使用插件实例注册 PL 功能
|
||||||
|
plugin_instance.register_pl_functions(injector)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[auto-dependency] PL 注册失败:{e}")
|
||||||
117
store/@{FutureOSS}/auto-dependency/README.md
Normal file
117
store/@{FutureOSS}/auto-dependency/README.md
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
# 依赖自动安装插件 (auto-dependency)
|
||||||
|
|
||||||
|
## 概述
|
||||||
|
|
||||||
|
依赖自动安装插件是一个核心系统插件,用于扫描所有插件的声明文件,检查并自动安装系统依赖。
|
||||||
|
|
||||||
|
## 功能特性
|
||||||
|
|
||||||
|
1. **扫描插件声明** - 自动扫描所有插件目录下的 `manifest.json` 文件
|
||||||
|
2. **系统依赖检测** - 读取每个插件声明的系统依赖 (`system_dependencies` 字段)
|
||||||
|
3. **安装状态检查** - 检查这些系统依赖是否已在系统中安装
|
||||||
|
4. **自动安装** - 对于未安装的依赖,使用系统包管理器自动安装
|
||||||
|
5. **PL 注入接口** - 通过 PL 注入机制向插件加载器注册功能接口
|
||||||
|
|
||||||
|
## 使用方法
|
||||||
|
|
||||||
|
### 在 manifest.json 中声明系统依赖
|
||||||
|
|
||||||
|
其他插件可以在自己的 `manifest.json` 中声明所需的系统依赖:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"metadata": {
|
||||||
|
"name": "my-plugin",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"author": "MyName",
|
||||||
|
"description": "我的插件"
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"enabled": true,
|
||||||
|
"args": {}
|
||||||
|
},
|
||||||
|
"dependencies": ["plugin-loader"],
|
||||||
|
"system_dependencies": ["curl", "git", "wget"],
|
||||||
|
"permissions": []
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 通过 PL 注入接口调用
|
||||||
|
|
||||||
|
插件加载器加载此插件后,可以通过以下 PL 注入接口进行操作:
|
||||||
|
|
||||||
|
| 接口名称 | 说明 | 参数 | 返回值 |
|
||||||
|
|---------|------|------|--------|
|
||||||
|
| `auto-dependency:scan` | 扫描所有插件的声明文件 | `scan_dir` (可选,默认 "store") | 插件信息列表 |
|
||||||
|
| `auto-dependency:check` | 检查系统依赖安装状态 | `scan_dir` (可选,默认 "store") | 检查结果字典 |
|
||||||
|
| `auto-dependency:install` | 安装缺失的系统依赖 | `scan_dir` (可选,默认 "store") | 安装结果字典 |
|
||||||
|
| `auto-dependency:info` | 获取插件系统信息 | 无 | 系统信息字典 |
|
||||||
|
|
||||||
|
### 示例代码
|
||||||
|
|
||||||
|
```python
|
||||||
|
# 获取插件加载器中的 auto-dependency 功能
|
||||||
|
injector = get_pl_injector() # 从插件加载器获取
|
||||||
|
|
||||||
|
# 扫描所有插件的系统依赖声明
|
||||||
|
plugins = injector.get_injected_functions("auto-dependency:scan")[0]()
|
||||||
|
print(f"找到 {len(plugins)} 个插件")
|
||||||
|
|
||||||
|
# 检查依赖安装状态
|
||||||
|
result = injector.get_injected_functions("auto-dependency:check")[0]()
|
||||||
|
print(f"已安装:{result['installed_count']}, 缺失:{result['missing_count']}")
|
||||||
|
|
||||||
|
# 安装缺失的依赖
|
||||||
|
install_result = injector.get_injected_functions("auto-dependency:install")[0]()
|
||||||
|
print(f"成功安装:{install_result['success_count']}, 失败:{install_result['failed_count']}")
|
||||||
|
```
|
||||||
|
|
||||||
|
## 支持的包管理器
|
||||||
|
|
||||||
|
插件自动检测系统使用的包管理器,支持:
|
||||||
|
|
||||||
|
- **Debian/Ubuntu**: apt-get, apt
|
||||||
|
- **RHEL/CentOS**: yum, dnf
|
||||||
|
- **Arch Linux**: pacman
|
||||||
|
- **macOS**: brew
|
||||||
|
- **Alpine Linux**: apk
|
||||||
|
|
||||||
|
## 配置选项
|
||||||
|
|
||||||
|
在 `manifest.json` 的 `config.args` 中可以配置:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"config": {
|
||||||
|
"enabled": true,
|
||||||
|
"args": {
|
||||||
|
"scan_dirs": ["store"],
|
||||||
|
"package_manager": "auto",
|
||||||
|
"auto_install": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
| 配置项 | 说明 | 默认值 |
|
||||||
|
|-------|------|--------|
|
||||||
|
| `scan_dirs` | 要扫描的目录列表 | `["store"]` |
|
||||||
|
| `package_manager` | 包管理器(auto 为自动检测) | `"auto"` |
|
||||||
|
| `auto_install` | 是否自动安装缺失的依赖 | `true` |
|
||||||
|
|
||||||
|
## 安全说明
|
||||||
|
|
||||||
|
- 插件需要 `*` 权限才能执行系统命令安装包
|
||||||
|
- 包安装操作有超时限制(300 秒)
|
||||||
|
- 所有安装操作都会记录日志
|
||||||
|
|
||||||
|
## 文件结构
|
||||||
|
|
||||||
|
```
|
||||||
|
store/@{FutureOSS}/auto-dependency/
|
||||||
|
├── manifest.json # 插件清单
|
||||||
|
├── main.py # 主逻辑实现
|
||||||
|
├── PL/
|
||||||
|
│ └── main.py # PL 注入入口
|
||||||
|
└── README.md # 本文档
|
||||||
|
```
|
||||||
407
store/@{FutureOSS}/auto-dependency/main.py
Normal file
407
store/@{FutureOSS}/auto-dependency/main.py
Normal file
@@ -0,0 +1,407 @@
|
|||||||
|
"""依赖自动安装插件 - 扫描所有插件的声明文件,检查并安装系统依赖
|
||||||
|
|
||||||
|
功能说明:
|
||||||
|
1. 扫描所有插件目录下的 manifest.json 文件
|
||||||
|
2. 读取每个插件声明的系统依赖 (system_dependencies 字段)
|
||||||
|
3. 检查这些系统依赖是否已安装
|
||||||
|
4. 对于未安装的依赖,使用系统包管理器自动安装
|
||||||
|
5. 通过 PL 注入机制向插件加载器注册功能接口
|
||||||
|
"""
|
||||||
|
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:
|
||||||
|
"""系统依赖检查器"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.package_managers = {
|
||||||
|
"apt": ["apt-get", "apt"],
|
||||||
|
"yum": ["yum", "dnf"],
|
||||||
|
"pacman": ["pacman"],
|
||||||
|
"brew": ["brew"],
|
||||||
|
"apk": ["apk"],
|
||||||
|
}
|
||||||
|
self.detected_pm = self._detect_package_manager()
|
||||||
|
|
||||||
|
def _detect_package_manager(self) -> str:
|
||||||
|
"""检测系统包管理器"""
|
||||||
|
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:
|
||||||
|
"""检查命令是否可用"""
|
||||||
|
return shutil.which(command) is not None
|
||||||
|
|
||||||
|
def check_package(self, package: 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:
|
||||||
|
pass
|
||||||
|
return False
|
||||||
|
|
||||||
|
def install_package(self, package: 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(
|
||||||
|
["apt-get", "install", "-y", package],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
timeout=300
|
||||||
|
)
|
||||||
|
return result.returncode == 0
|
||||||
|
elif self.detected_pm == "yum":
|
||||||
|
result = subprocess.run(
|
||||||
|
["yum", "install", "-y", package],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
timeout=300
|
||||||
|
)
|
||||||
|
return result.returncode == 0
|
||||||
|
elif self.detected_pm == "dnf":
|
||||||
|
result = subprocess.run(
|
||||||
|
["dnf", "install", "-y", package],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
timeout=300
|
||||||
|
)
|
||||||
|
return result.returncode == 0
|
||||||
|
elif self.detected_pm == "pacman":
|
||||||
|
result = subprocess.run(
|
||||||
|
["pacman", "-S", "--noconfirm", package],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
timeout=300
|
||||||
|
)
|
||||||
|
return result.returncode == 0
|
||||||
|
elif self.detected_pm == "brew":
|
||||||
|
result = subprocess.run(
|
||||||
|
["brew", "install", package],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
timeout=300
|
||||||
|
)
|
||||||
|
return result.returncode == 0
|
||||||
|
elif self.detected_pm == "apk":
|
||||||
|
result = subprocess.run(
|
||||||
|
["apk", "add", package],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
timeout=300
|
||||||
|
)
|
||||||
|
return result.returncode == 0
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
return False
|
||||||
|
|
||||||
|
def check_and_install(self, package: str, auto_install: bool = True) -> Dict[str, Any]:
|
||||||
|
"""检查并安装包"""
|
||||||
|
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):
|
||||||
|
"""依赖自动安装插件"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.checker = SystemDependencyChecker()
|
||||||
|
self.scan_dirs: List[str] = []
|
||||||
|
self.auto_install: bool = True
|
||||||
|
self._plugin_loader_ref: Optional[Any] = None
|
||||||
|
|
||||||
|
def init(self, deps: Optional[Dict[str, Any]] = None):
|
||||||
|
"""初始化插件"""
|
||||||
|
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 stop(self):
|
||||||
|
"""停止插件"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def scan_plugin_manifests(self, base_dir: str = "store") -> List[Dict[str, Any]]:
|
||||||
|
"""扫描所有插件的 manifest.json 文件
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
包含所有插件信息的列表,每个元素包含:
|
||||||
|
- plugin_name: 插件名称
|
||||||
|
- plugin_dir: 插件目录路径
|
||||||
|
- manifest: manifest.json 内容
|
||||||
|
- system_dependencies: 系统依赖列表
|
||||||
|
"""
|
||||||
|
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:
|
||||||
|
continue
|
||||||
|
|
||||||
|
return results
|
||||||
|
|
||||||
|
def check_all_dependencies(self, base_dir: str = "store") -> Dict[str, Any]:
|
||||||
|
"""检查所有插件的系统依赖
|
||||||
|
|
||||||
|
Args:
|
||||||
|
base_dir: 基础扫描目录
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
检查结果字典,包含:
|
||||||
|
- total_plugins: 扫描的插件总数
|
||||||
|
- plugins_with_deps: 有系统依赖的插件数
|
||||||
|
- dependencies: 依赖检查结果列表
|
||||||
|
- missing_count: 缺失的依赖数量
|
||||||
|
- installed_count: 已安装的依赖数量
|
||||||
|
"""
|
||||||
|
plugins = self.scan_plugin_manifests(base_dir)
|
||||||
|
|
||||||
|
all_deps = {} # {package: [plugin_names]}
|
||||||
|
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]:
|
||||||
|
"""安装所有缺失的系统依赖
|
||||||
|
|
||||||
|
Args:
|
||||||
|
base_dir: 基础扫描目录
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
安装结果字典,包含:
|
||||||
|
- total_to_install: 需要安装的包数量
|
||||||
|
- success_count: 成功安装的包数量
|
||||||
|
- failed_count: 安装失败的包数量
|
||||||
|
- results: 每个包的安装结果
|
||||||
|
"""
|
||||||
|
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]:
|
||||||
|
"""获取系统信息"""
|
||||||
|
return {
|
||||||
|
"package_manager": self.checker.detected_pm,
|
||||||
|
"auto_install_enabled": self.auto_install,
|
||||||
|
"scan_directories": self.scan_dirs
|
||||||
|
}
|
||||||
|
|
||||||
|
def register_pl_functions(self, injector: Any):
|
||||||
|
"""注册 PL 注入功能
|
||||||
|
|
||||||
|
通过 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.scan_plugin_manifests(scan_dir)
|
||||||
|
|
||||||
|
injector.register_function(
|
||||||
|
"auto-dependency:scan",
|
||||||
|
scan_deps,
|
||||||
|
"扫描所有插件的声明文件,获取系统依赖列表"
|
||||||
|
)
|
||||||
|
|
||||||
|
# 注册检查功能
|
||||||
|
def check_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.install_missing_dependencies(scan_dir)
|
||||||
|
|
||||||
|
injector.register_function(
|
||||||
|
"auto-dependency:install",
|
||||||
|
install_deps,
|
||||||
|
"自动安装所有缺失的系统依赖"
|
||||||
|
)
|
||||||
|
|
||||||
|
# 注册信息功能
|
||||||
|
def get_info() -> Dict[str, Any]:
|
||||||
|
"""获取插件系统信息"""
|
||||||
|
return self.get_system_info()
|
||||||
|
|
||||||
|
injector.register_function(
|
||||||
|
"auto-dependency:info",
|
||||||
|
get_info,
|
||||||
|
"获取自动依赖插件的系统信息"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def New() -> AutoDependencyPlugin:
|
||||||
|
"""创建插件实例"""
|
||||||
|
return AutoDependencyPlugin()
|
||||||
20
store/@{FutureOSS}/auto-dependency/manifest.json
Normal file
20
store/@{FutureOSS}/auto-dependency/manifest.json
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"metadata": {
|
||||||
|
"name": "auto-dependency",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"author": "FutureOSS",
|
||||||
|
"description": "依赖自动安装插件 - 扫描所有插件的声明文件,检查并安装系统依赖",
|
||||||
|
"type": "core"
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"enabled": true,
|
||||||
|
"args": {
|
||||||
|
"scan_dirs": ["store"],
|
||||||
|
"package_manager": "auto",
|
||||||
|
"auto_install": true,
|
||||||
|
"pl_injection": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dependencies": ["plugin-loader"],
|
||||||
|
"permissions": ["*"]
|
||||||
|
}
|
||||||
16
store/@{FutureOSS}/example-with-deps/manifest.json
Normal file
16
store/@{FutureOSS}/example-with-deps/manifest.json
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"metadata": {
|
||||||
|
"name": "example-with-deps",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"author": "FutureOSS",
|
||||||
|
"description": "示例插件 - 演示如何声明系统依赖",
|
||||||
|
"type": "example"
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"enabled": true,
|
||||||
|
"args": {}
|
||||||
|
},
|
||||||
|
"dependencies": [],
|
||||||
|
"system_dependencies": ["curl", "git", "wget"],
|
||||||
|
"permissions": []
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user