fix(plugins): preserve dependency manifest sources (#6370)

This commit is contained in:
InfinityPacer
2026-08-20 20:51:35 +08:00
committed by GitHub
parent 638ea6f01e
commit 2d80f9f04f
7 changed files with 189 additions and 61 deletions
+8 -4
View File
@@ -10,10 +10,10 @@ from urllib.parse import urlsplit, urlunsplit
@dataclass(frozen=True)
class PackageInstallRequest:
"""
Python 包安装请求,集中描述依赖文件、工具缓存、代理和本地 wheels 候选源。
Python 包安装请求,集中描述依赖清单、工具缓存、代理和本地 wheels 候选源。
"""
dependency_file: Path
dependency_files: tuple[Path, ...]
python_bin: Path
find_links_dirs: list[Path] = field(default_factory=list)
constraints_file: Path | None = None
@@ -92,7 +92,8 @@ def _base_install_args(request: PackageInstallRequest) -> list[str]:
args.extend(["--find-links", str(directory)])
if request.constraints_file:
args.extend(["-c", str(request.constraints_file)])
args.extend(["-r", str(request.dependency_file)])
for dependency_file in request.dependency_files:
args.extend(["-r", str(dependency_file)])
return args
@@ -119,11 +120,14 @@ def _build_uv_command(uv_bin: Path, request: PackageInstallRequest, use_index: b
def _build_uv_sync_command(uv_bin: Path, request: PackageInstallRequest, use_index: bool) -> list[str]:
if len(request.dependency_files) != 1:
raise ValueError("主项目锁定依赖恢复只接受一个 pyproject.toml")
project_file = request.dependency_files[0]
command = [
str(uv_bin),
"sync",
"--project",
str(request.dependency_file.parent),
str(project_file.parent),
"--locked",
"--no-dev",
"--no-install-project",
+16 -18
View File
@@ -248,9 +248,9 @@ class PluginDependencyInstaller:
merged.append(Requirement(target))
return merged
def _plugin_dependencies(self) -> list[Requirement]:
"""扫描已安装插件生效依赖清单并合并版本约束"""
dependencies: list[Requirement] = []
def _plugin_manifests(self) -> list[Any]:
"""返回已安装插件当前生效依赖清单。"""
manifests = []
installed_plugins = {
plugin_id.lower()
for plugin_id in self._installed_plugins_provider() or []
@@ -259,7 +259,7 @@ class PluginDependencyInstaller:
plugin_dirs = list(self._plugin_dir.iterdir())
except (FileNotFoundError, OSError):
return []
for plugin_dir in plugin_dirs:
for plugin_dir in sorted(plugin_dirs, key=lambda item: item.name):
if not plugin_dir.is_dir():
continue
if plugin_dir.name not in installed_plugins:
@@ -268,6 +268,13 @@ class PluginDependencyInstaller:
manifest = load_dependency_manifest(plugin_dir)
if manifest is None:
continue
manifests.append(manifest)
return manifests
def _plugin_dependencies(self) -> list[Requirement]:
"""扫描已安装插件的生效依赖清单并合并版本约束。"""
dependencies: list[Requirement] = []
for manifest in self._plugin_manifests():
for requirement in manifest.dependencies:
if requirement.marker and not requirement.marker.evaluate():
continue
@@ -304,29 +311,20 @@ class PluginDependencyInstaller:
return list(dict.fromkeys(result))
def install(self, dependencies: list[str]) -> tuple[bool, str]:
"""依赖写入临时 requirements 并调用统一包安装策略"""
"""已安装插件的原始清单交给一次统一包安装。"""
if not dependencies:
return False, "没有传入需要安装的依赖项"
requirements_file = (
Path(settings.TEMP_PATH)
/ "plugin_dependencies"
/ "requirements.txt"
)
try:
requirements_file.parent.mkdir(parents=True, exist_ok=True)
requirements_file.write_text(
"".join(f"{dependency}\n" for dependency in dependencies),
encoding="utf-8",
)
manifest_paths = [manifest.path for manifest in self._plugin_manifests()]
if not manifest_paths:
return False, "没有找到已安装插件的依赖清单"
return self._helper.install_packages_with_fallback(
requirements_file,
manifest_paths,
self._wheels_dirs(),
)
except Exception as err:
logger.error(f"安装依赖项时发生错误:{err}")
return False, f"安装依赖项时发生错误:{err}"
finally:
requirements_file.unlink(missing_ok=True)
async def async_find_missing(self) -> list[str]:
"""在线程池中扫描缺失依赖,避免阻塞事件循环。"""