mirror of
https://github.com/AstrBotDevs/AstrBot
synced 2026-07-15 17:30:13 +08:00
refactor: bundled webui static files into wheel and replace astrbot cli log with English (#5665)
* refactor: bundled webui static files into wheel and replace astrbot cli log with English - Translated and standardized log messages in cmd_conf.py for better clarity. - Updated initialization logic in cmd_init.py to provide clearer user prompts and error handling. - Improved plugin management commands in cmd_plug.py with consistent language and error messages. - Enhanced run command in cmd_run.py with clearer status messages and error handling. - Updated utility functions in basic.py and plugin.py to improve readability and maintainability. - Added version comparison logic in version_comparator.py with clearer comments. - Enhanced logging configuration in log.py to suppress noisy loggers. - Updated the updater logic in updator.py to provide clearer error messages for users. - Improved IO utility functions in io.py to handle dashboard versioning more effectively. - Enhanced dashboard server logic in server.py to prioritize bundled assets and improve user feedback. - Updated pyproject.toml to include bundled dashboard assets and custom build hooks. - Added a custom build script (hatch_build.py) to automate dashboard builds during package creation. * refactor: improve exception messages and formatting in CLI command validation * perf: change npm install to npm ci for consistent dependency installation * fix
This commit is contained in:
@@ -13,22 +13,22 @@ from .version_comparator import VersionComparator
|
||||
|
||||
|
||||
class PluginStatus(str, Enum):
|
||||
INSTALLED = "已安装"
|
||||
NEED_UPDATE = "需更新"
|
||||
NOT_INSTALLED = "未安装"
|
||||
NOT_PUBLISHED = "未发布"
|
||||
INSTALLED = "installed"
|
||||
NEED_UPDATE = "needs-update"
|
||||
NOT_INSTALLED = "not-installed"
|
||||
NOT_PUBLISHED = "unpublished"
|
||||
|
||||
|
||||
def get_git_repo(url: str, target_path: Path, proxy: str | None = None) -> None:
|
||||
"""从 Git 仓库下载代码并解压到指定路径"""
|
||||
"""Download code from a Git repository and extract to the specified path"""
|
||||
temp_dir = Path(tempfile.mkdtemp())
|
||||
try:
|
||||
# 解析仓库信息
|
||||
# Parse repository info
|
||||
repo_namespace = url.split("/")[-2:]
|
||||
author = repo_namespace[0]
|
||||
repo = repo_namespace[1]
|
||||
|
||||
# 尝试获取最新的 release
|
||||
# Try to get the latest release
|
||||
release_url = f"https://api.github.com/repos/{author}/{repo}/releases"
|
||||
try:
|
||||
with httpx.Client(
|
||||
@@ -40,21 +40,21 @@ def get_git_repo(url: str, target_path: Path, proxy: str | None = None) -> None:
|
||||
releases = resp.json()
|
||||
|
||||
if releases:
|
||||
# 使用最新的 release
|
||||
# Use the latest release
|
||||
download_url = releases[0]["zipball_url"]
|
||||
else:
|
||||
# 没有 release,使用默认分支
|
||||
click.echo(f"正在从默认分支下载 {author}/{repo}")
|
||||
# No release found, use default branch
|
||||
click.echo(f"Downloading {author}/{repo} from default branch")
|
||||
download_url = f"https://github.com/{author}/{repo}/archive/refs/heads/master.zip"
|
||||
except Exception as e:
|
||||
click.echo(f"获取 release 信息失败: {e},将直接使用提供的 URL")
|
||||
click.echo(f"Failed to get release info: {e}. Using provided URL directly")
|
||||
download_url = url
|
||||
|
||||
# 应用代理
|
||||
# Apply proxy
|
||||
if proxy:
|
||||
download_url = f"{proxy}/{download_url}"
|
||||
|
||||
# 下载并解压
|
||||
# Download and extract
|
||||
with httpx.Client(
|
||||
proxy=proxy if proxy else None,
|
||||
follow_redirects=True,
|
||||
@@ -65,7 +65,7 @@ def get_git_repo(url: str, target_path: Path, proxy: str | None = None) -> None:
|
||||
and "archive/refs/heads/master.zip" in download_url
|
||||
):
|
||||
alt_url = download_url.replace("master.zip", "main.zip")
|
||||
click.echo("master 分支不存在,尝试下载 main 分支")
|
||||
click.echo("Branch 'master' not found, trying 'main' branch")
|
||||
resp = client.get(alt_url)
|
||||
resp.raise_for_status()
|
||||
else:
|
||||
@@ -84,13 +84,13 @@ def get_git_repo(url: str, target_path: Path, proxy: str | None = None) -> None:
|
||||
|
||||
|
||||
def load_yaml_metadata(plugin_dir: Path) -> dict:
|
||||
"""从 metadata.yaml 文件加载插件元数据
|
||||
"""Load plugin metadata from metadata.yaml file
|
||||
|
||||
Args:
|
||||
plugin_dir: 插件目录路径
|
||||
plugin_dir: Plugin directory path
|
||||
|
||||
Returns:
|
||||
dict: 包含元数据的字典,如果读取失败则返回空字典
|
||||
dict: Dictionary containing metadata, or empty dict if loading fails
|
||||
|
||||
"""
|
||||
yaml_path = plugin_dir / "metadata.yaml"
|
||||
@@ -98,33 +98,33 @@ def load_yaml_metadata(plugin_dir: Path) -> dict:
|
||||
try:
|
||||
return yaml.safe_load(yaml_path.read_text(encoding="utf-8")) or {}
|
||||
except Exception as e:
|
||||
click.echo(f"读取 {yaml_path} 失败: {e}", err=True)
|
||||
click.echo(f"Failed to read {yaml_path}: {e}", err=True)
|
||||
return {}
|
||||
|
||||
|
||||
def build_plug_list(plugins_dir: Path) -> list:
|
||||
"""构建插件列表,包含本地和在线插件信息
|
||||
"""Build plugin list containing local and online plugin information
|
||||
|
||||
Args:
|
||||
plugins_dir (Path): 插件目录路径
|
||||
plugins_dir (Path): Plugin directory path
|
||||
|
||||
Returns:
|
||||
list: 包含插件信息的字典列表
|
||||
list: List of dicts containing plugin information
|
||||
|
||||
"""
|
||||
# 获取本地插件信息
|
||||
# Get local plugin info
|
||||
result = []
|
||||
if plugins_dir.exists():
|
||||
for plugin_name in [d.name for d in plugins_dir.glob("*") if d.is_dir()]:
|
||||
plugin_dir = plugins_dir / plugin_name
|
||||
|
||||
# 从 metadata.yaml 加载元数据
|
||||
# Load metadata from metadata.yaml
|
||||
metadata = load_yaml_metadata(plugin_dir)
|
||||
|
||||
if "desc" not in metadata and "description" in metadata:
|
||||
metadata["desc"] = metadata["description"]
|
||||
|
||||
# 如果成功加载元数据,添加到结果列表
|
||||
# If metadata loaded successfully, add to result list
|
||||
if metadata and all(
|
||||
k in metadata for k in ["name", "desc", "version", "author", "repo"]
|
||||
):
|
||||
@@ -140,7 +140,7 @@ def build_plug_list(plugins_dir: Path) -> list:
|
||||
},
|
||||
)
|
||||
|
||||
# 获取在线插件列表
|
||||
# Get online plugin list
|
||||
online_plugins = []
|
||||
try:
|
||||
with httpx.Client() as client:
|
||||
@@ -160,13 +160,13 @@ def build_plug_list(plugins_dir: Path) -> list:
|
||||
},
|
||||
)
|
||||
except Exception as e:
|
||||
click.echo(f"获取在线插件列表失败: {e}", err=True)
|
||||
click.echo(f"Failed to get online plugin list: {e}", err=True)
|
||||
|
||||
# 与在线插件比对,更新状态
|
||||
# Compare with online plugins and update status
|
||||
online_plugin_names = {plugin["name"] for plugin in online_plugins}
|
||||
for local_plugin in result:
|
||||
if local_plugin["name"] in online_plugin_names:
|
||||
# 查找对应的在线插件
|
||||
# Find the corresponding online plugin
|
||||
online_plugin = next(
|
||||
p for p in online_plugins if p["name"] == local_plugin["name"]
|
||||
)
|
||||
@@ -179,10 +179,10 @@ def build_plug_list(plugins_dir: Path) -> list:
|
||||
):
|
||||
local_plugin["status"] = PluginStatus.NEED_UPDATE
|
||||
else:
|
||||
# 本地插件未在线上发布
|
||||
# Local plugin is not published online
|
||||
local_plugin["status"] = PluginStatus.NOT_PUBLISHED
|
||||
|
||||
# 添加未安装的在线插件
|
||||
# Add uninstalled online plugins
|
||||
for online_plugin in online_plugins:
|
||||
if not any(plugin["name"] == online_plugin["name"] for plugin in result):
|
||||
result.append(online_plugin)
|
||||
@@ -196,19 +196,19 @@ def manage_plugin(
|
||||
is_update: bool = False,
|
||||
proxy: str | None = None,
|
||||
) -> None:
|
||||
"""安装或更新插件
|
||||
"""Install or update a plugin
|
||||
|
||||
Args:
|
||||
plugin (dict): 插件信息字典
|
||||
plugins_dir (Path): 插件目录
|
||||
is_update (bool, optional): 是否为更新操作. 默认为 False
|
||||
proxy (str, optional): 代理服务器地址
|
||||
plugin (dict): Plugin info dict
|
||||
plugins_dir (Path): Plugins directory
|
||||
is_update (bool, optional): Whether this is an update operation. Defaults to False
|
||||
proxy (str, optional): Proxy server address
|
||||
|
||||
"""
|
||||
plugin_name = plugin["name"]
|
||||
repo_url = plugin["repo"]
|
||||
|
||||
# 如果是更新且有本地路径,直接使用本地路径
|
||||
# If updating and local path exists, use it directly
|
||||
if is_update and plugin.get("local_path"):
|
||||
target_path = Path(plugin["local_path"])
|
||||
else:
|
||||
@@ -216,11 +216,13 @@ def manage_plugin(
|
||||
|
||||
backup_path = Path(f"{target_path}_backup") if is_update else None
|
||||
|
||||
# 检查插件是否存在
|
||||
# Check if plugin exists
|
||||
if is_update and not target_path.exists():
|
||||
raise click.ClickException(f"插件 {plugin_name} 未安装,无法更新")
|
||||
raise click.ClickException(
|
||||
f"Plugin {plugin_name} is not installed and cannot be updated"
|
||||
)
|
||||
|
||||
# 备份现有插件
|
||||
# Backup existing plugin
|
||||
if is_update and backup_path is not None and backup_path.exists():
|
||||
shutil.rmtree(backup_path)
|
||||
if is_update and backup_path is not None:
|
||||
@@ -228,19 +230,21 @@ def manage_plugin(
|
||||
|
||||
try:
|
||||
click.echo(
|
||||
f"正在从 {repo_url} {'更新' if is_update else '下载'}插件 {plugin_name}...",
|
||||
f"{'Updating' if is_update else 'Downloading'} plugin {plugin_name} from {repo_url}...",
|
||||
)
|
||||
get_git_repo(repo_url, target_path, proxy)
|
||||
|
||||
# 更新成功,删除备份
|
||||
# Update succeeded, delete backup
|
||||
if is_update and backup_path is not None and backup_path.exists():
|
||||
shutil.rmtree(backup_path)
|
||||
click.echo(f"插件 {plugin_name} {'更新' if is_update else '安装'}成功")
|
||||
click.echo(
|
||||
f"Plugin {plugin_name} {'updated' if is_update else 'installed'} successfully"
|
||||
)
|
||||
except Exception as e:
|
||||
if target_path.exists():
|
||||
shutil.rmtree(target_path, ignore_errors=True)
|
||||
if is_update and backup_path is not None and backup_path.exists():
|
||||
shutil.move(backup_path, target_path)
|
||||
raise click.ClickException(
|
||||
f"{'更新' if is_update else '安装'}插件 {plugin_name} 时出错: {e}",
|
||||
f"Error {'updating' if is_update else 'installing'} plugin {plugin_name}: {e}",
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user