Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,10 @@ def install_deps_command( # noqa: PLR0913
] = None,
allow_unlisted: Annotated[
bool,
typer.Option('--allow-unlisted', help='dev 环境允许未命中 allowlist 的依赖仅告警'),
typer.Option('--allow-unlisted', help='允许未命中 allowlist 的依赖仅告警'),
] = False,
lockfile: Annotated[str, typer.Option('--lockfile', help='指定插件依赖锁文件路径')] = '',
allowlist: Annotated[str, typer.Option('--allowlist', help='指定插件依赖允许列表路径')] = '',
offline_dir: Annotated[str, typer.Option('--offline-dir', help='指定插件离线依赖制品目录')] = '',
require_lockfile: Annotated[
bool | None,
Expand All @@ -112,8 +113,9 @@ def install_deps_command( # noqa: PLR0913
:param yes: 是否跳过确认
:param dry_run: 是否仅预演
:param policy_mode: 临时覆盖策略模式
:param allow_unlisted: dev 环境是否允许未命中 allowlist 的依赖仅告警
:param allow_unlisted: 是否允许未命中 allowlist 的依赖仅告警
:param lockfile: 锁文件路径
:param allowlist: 依赖允许列表路径
:param offline_dir: 离线制品目录
:param require_lockfile: 是否要求锁文件
:return: None
Expand All @@ -129,6 +131,7 @@ def install_deps_command( # noqa: PLR0913
policy_mode=policy_mode,
allow_unlisted=allow_unlisted,
lockfile=lockfile,
allowlist=allowlist,
offline_dir=offline_dir,
require_lockfile=require_lockfile,
),
Expand Down
9 changes: 5 additions & 4 deletions ruoyi-fastapi-backend/cli/groups/plugin/controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -408,27 +408,28 @@ def install_plugin_dependencies(
options.yes,
options.dry_run,
)
policy_config = DependencyInstallPolicyConfig.from_environment(
policy_config = DependencyInstallPolicyConfig.from_cli_environment(
env=env,
mode=options.policy_mode,
allow_prod=options.allow_prod,
allow_unlisted=options.allow_unlisted,
lockfile_path=options.lockfile or None,
allowlist_path=options.allowlist or None,
offline_dir=options.offline_dir or None,
require_lockfile=options.require_lockfile,
)
core_runtime = self.core_runtime
output_callback = self._build_dependency_install_output_callback(ctx)
if self._should_interactive_confirm_dependency_install(ctx, options):
preview_payload = core_runtime.install_plugin_dependencies(
preview_payload = core_runtime.install_plugin_dependencies_from_cli(
plugin_id,
dry_run=True,
policy_config=policy_config,
confirmed=True,
)
preview_payload['env'] = ctx.env
if self._dependency_install_policy_blocked(preview_payload):
payload = core_runtime.install_plugin_dependencies(
payload = core_runtime.install_plugin_dependencies_from_cli(
plugin_id,
dry_run=False,
policy_config=policy_config,
Expand All @@ -455,7 +456,7 @@ def install_plugin_dependencies(
else:
confirmed = options.yes

payload = core_runtime.install_plugin_dependencies(
payload = core_runtime.install_plugin_dependencies_from_cli(
plugin_id,
dry_run=options.dry_run,
policy_config=policy_config,
Expand Down
4 changes: 3 additions & 1 deletion ruoyi-fastapi-backend/cli/groups/plugin/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,9 @@ class PluginDependencyInstallCommandOptions:
:param yes: 是否跳过确认
:param dry_run: 是否仅预演
:param policy_mode: 临时覆盖策略模式
:param allow_unlisted: dev 环境是否允许未命中 allowlist 的依赖仅告警
:param allow_unlisted: 是否允许未命中 allowlist 的依赖仅告警
:param lockfile: 锁文件路径
:param allowlist: 依赖允许列表路径
:param offline_dir: 离线制品目录
:param require_lockfile: 是否要求锁文件
"""
Expand All @@ -52,6 +53,7 @@ class PluginDependencyInstallCommandOptions:
policy_mode: str | None = None
allow_unlisted: bool = False
lockfile: str = ''
allowlist: str = ''
offline_dir: str = ''
require_lockfile: bool | None = None

Expand Down
3 changes: 2 additions & 1 deletion ruoyi-fastapi-backend/cli/runtime/app/support.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,8 @@ def build_app_env_snapshot(self) -> dict[str, Any]:
env_module = self.infrastructure_gateway.get_env_module()
app_config = env_module.AppConfig
backend_dir = Path(self.runtime_environment.get_backend_dir())
resolved_env = os.environ.get('APP_ENV', '') or 'dev'
run_env = getattr(getattr(env_module, 'get_config', None), 'run_env', None)
resolved_env = run_env or os.environ.get('APP_ENV', '') or 'dev'
env_file_name = f'.env.{resolved_env}'
env_file_path = backend_dir / env_file_name
return {
Expand Down
29 changes: 15 additions & 14 deletions ruoyi-fastapi-backend/config/env.py
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ class GetConfig:
"""

def __init__(self) -> None:
self.parse_cli_args()
self.run_env = self.parse_cli_args()

def get_app_config(self) -> AppSettings:
"""
Expand Down Expand Up @@ -331,18 +331,23 @@ def get_upload_config(self) -> UploadSettings:
return UploadSettings()

@staticmethod
def parse_cli_args() -> None:
def parse_cli_args() -> str:
"""
解析命令行参数
解析命令行参数并加载对应环境配置。

``run_env`` 用于选择 ``.env.*`` 配置文件,实际应用环境以配置
文件中的 ``APP_ENV`` 为准。

:return: 当前加载的运行环境配置名称
"""
run_env = os.environ.get('APP_ENV', '')
# 检查是否在alembic环境中运行,如果是则跳过参数解析
if 'alembic' in sys.argv[0] or any('alembic' in arg for arg in sys.argv):
ini_config = configparser.ConfigParser()
ini_config.read('alembic.ini', encoding='utf-8')
if 'settings' in ini_config:
# 获取env选项
env_value = ini_config['settings'].get('env')
os.environ['APP_ENV'] = env_value if env_value else 'dev'
run_env = ini_config['settings'].get('env') or run_env
elif 'uvicorn' in sys.argv[0]:
# 使用uvicorn启动时,命令行参数需要按照uvicorn的文档进行配置,无法自定义参数
pass
Expand All @@ -352,17 +357,13 @@ def parse_cli_args() -> None:
parser.add_argument('--env', type=str, default='', help='运行环境')
# 解析命令行参数
args, _ = parser.parse_known_args()
# 设置环境变量,如果未设置命令行参数,默认APP_ENV为dev
os.environ['APP_ENV'] = args.env if args.env else 'dev'
# 读取运行环境
run_env = os.environ.get('APP_ENV', '')
run_env = args.env or run_env
# 运行环境未指定时默认加载.env.dev
env_file = '.env.dev'
# 运行环境不为空时按命令行参数加载对应.env文件
if run_env != '':
env_file = f'.env.{run_env}'
# 加载配置
run_env = run_env.strip() or 'dev'
env_file = f'.env.{run_env}'
# 加载配置,已通过外部命令设置的环境变量保持优先
load_dotenv(env_file)
return run_env


# 实例化获取配置类
Expand Down
78 changes: 73 additions & 5 deletions ruoyi-fastapi-backend/plugins/core/runtime/service/dependencies.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ def install_plugin_dependencies(
output_callback: PluginCommandOutputCallback | None = None,
) -> PluginDependencyInstallResponse:
"""
安装插件依赖
从 Web/应用运行时入口安装插件依赖

:param plugin_id: 插件ID
:param dry_run: 是否仅预演
Expand All @@ -131,20 +131,88 @@ def install_plugin_dependencies(
)
if blocked_payload:
return blocked_payload
return self._install_discovered_plugin_dependencies(
plugin_id,
discovered_plugin,
dry_run=dry_run,
policy_config=policy_config,
confirmed=confirmed,
output_callback=output_callback,
)
except Exception as exc:
return PluginRuntimePayloadBuilder.build_exception_payload('安装插件依赖失败', exc)

def install_plugin_dependencies_from_cli(
self,
plugin_id: str,
*,
dry_run: bool = False,
policy_config: DependencyInstallPolicyConfig | None = None,
confirmed: bool = False,
output_callback: PluginCommandOutputCallback | None = None,
) -> PluginDependencyInstallResponse:
"""
从 CLI 入口安装插件依赖。

CLI 不受 Web 运行时能力限制,但仍与 Web 共用同一套依赖检查、
安装计划和策略判定。

dependency_result = self.dependencies.dependency_checker.check_manifest(discovered_plugin.manifest)
return self.install_plugin_dependencies_from_result(
:param plugin_id: 插件ID
:param dry_run: 是否仅预演
:param policy_config: 依赖安装策略配置
:param confirmed: 是否已显式确认
:param output_callback: 依赖安装实时输出回调
:return: 插件依赖安装负载
"""
try:
discovered_plugin = self._get_discovered_plugin(plugin_id)
if not discovered_plugin:
return PluginPayloadBuilder.build_plugin_not_found_payload(plugin_id, dry_run=dry_run)
payload = self._install_discovered_plugin_dependencies(
plugin_id,
dependency_result,
discovered_plugin,
dry_run=dry_run,
discovered_plugin=discovered_plugin,
policy_config=policy_config,
confirmed=confirmed,
output_callback=output_callback,
)
cast('dict[str, object]', payload).pop('capability', None)
return payload
except Exception as exc:
return PluginRuntimePayloadBuilder.build_exception_payload('安装插件依赖失败', exc)

def _install_discovered_plugin_dependencies(
self,
plugin_id: str,
discovered_plugin: DiscoveredPlugin,
*,
dry_run: bool,
policy_config: DependencyInstallPolicyConfig | None,
confirmed: bool,
output_callback: PluginCommandOutputCallback | None,
) -> PluginDependencyInstallResponse:
"""
对已发现插件执行共用的依赖检查、计划、策略判定和安装流程。

:param plugin_id: 插件ID
:param discovered_plugin: 已发现插件
:param dry_run: 是否仅预演
:param policy_config: 依赖安装策略配置
:param confirmed: 是否已显式确认
:param output_callback: 依赖安装实时输出回调
:return: 插件依赖安装负载
"""
dependency_result = self.dependencies.dependency_checker.check_manifest(discovered_plugin.manifest)
return self.install_plugin_dependencies_from_result(
plugin_id,
dependency_result,
dry_run=dry_run,
discovered_plugin=discovered_plugin,
policy_config=policy_config,
confirmed=confirmed,
output_callback=output_callback,
)

def install_plugin_dependencies_from_result(
self,
plugin_id: str,
Expand Down
71 changes: 69 additions & 2 deletions ruoyi-fastapi-backend/plugins/core/runtime/service/facade.py
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,73 @@ def install_plugin_dependencies(
"""
安装插件依赖。

:param plugin_id: 插件ID
:param dry_run: 是否仅预演
:param policy_config: 依赖安装策略配置
:param confirmed: 是否已显式确认
:param record_operation_log: 是否记录插件操作审计日志
:param output_callback: 依赖安装实时输出回调
:return: 插件依赖安装负载
"""
return self._execute_plugin_dependency_install(
self.dependency.install_plugin_dependencies,
plugin_id,
dry_run=dry_run,
policy_config=policy_config,
confirmed=confirmed,
record_operation_log=record_operation_log,
output_callback=output_callback,
)

def install_plugin_dependencies_from_cli(
self,
plugin_id: str,
*,
dry_run: bool = False,
policy_config: DependencyInstallPolicyConfig | None = None,
confirmed: bool = False,
record_operation_log: bool = True,
output_callback: PluginCommandOutputCallback | None = None,
) -> PluginDependencyInstallResponse:
"""
从 CLI 受控入口安装插件依赖。

该入口可在非开发模式越过运行时能力拦截,但仍必须通过依赖安装
策略、生产确认及允许列表/锁文件等校验。

:param plugin_id: 插件ID
:param dry_run: 是否仅预演
:param policy_config: 依赖安装策略配置
:param confirmed: 是否已显式确认
:param record_operation_log: 是否记录插件操作审计日志
:param output_callback: 依赖安装实时输出回调
:return: 插件依赖安装负载
"""
return self._execute_plugin_dependency_install(
self.dependency.install_plugin_dependencies_from_cli,
plugin_id,
dry_run=dry_run,
policy_config=policy_config,
confirmed=confirmed,
record_operation_log=record_operation_log,
output_callback=output_callback,
)

def _execute_plugin_dependency_install(
self,
installer: Callable[..., PluginDependencyInstallResponse],
plugin_id: str,
*,
dry_run: bool,
policy_config: DependencyInstallPolicyConfig | None,
confirmed: bool,
record_operation_log: bool,
output_callback: PluginCommandOutputCallback | None,
) -> PluginDependencyInstallResponse:
"""
调用指定依赖安装入口并统一处理审计日志。

:param installer: Web/运行时或 CLI 依赖安装入口
:param plugin_id: 插件ID
:param dry_run: 是否仅预演
:param policy_config: 依赖安装策略配置
Expand All @@ -396,14 +463,14 @@ def install_plugin_dependencies(
:return: 插件依赖安装负载
"""
if output_callback is None:
dependency_payload = self.dependency.install_plugin_dependencies(
dependency_payload = installer(
plugin_id,
dry_run=dry_run,
policy_config=policy_config,
confirmed=confirmed,
)
else:
dependency_payload = self.dependency.install_plugin_dependencies(
dependency_payload = installer(
plugin_id,
dry_run=dry_run,
policy_config=policy_config,
Expand Down
Loading
Loading