Skip to content

fix(数据备份与恢复): 解决备份恢复和插件安装过程中的路径冲突及自愈问题#7737

Open
SXP-Simon wants to merge 2 commits intoAstrBotDevs:masterfrom
SXP-Simon:fix/plugin-migration
Open

fix(数据备份与恢复): 解决备份恢复和插件安装过程中的路径冲突及自愈问题#7737
SXP-Simon wants to merge 2 commits intoAstrBotDevs:masterfrom
SXP-Simon:fix/plugin-migration

Conversation

@SXP-Simon
Copy link
Copy Markdown
Contributor

@SXP-Simon SXP-Simon commented Apr 23, 2026

fix: 解决备份恢复和插件安装过程中的路径冲突及自愈问题

动机:

解决在数据迁移/恢复后插件无法加载或安装的问题。

在某些场景下(特别是从旧版本迁移或使用手动创建的备份时),备份导入器会将 ZIP 文件中的目录条目误识别为普通文件,导致它在 data/plugin_data/data/plugins/ 中创建了 0 字节的空文件 而不是文件夹。由于该同名文件占用了路径,后续插件或系统试图创建同名目录时会抛出 FileExistsError: [Errno 17] File exists。这种“路径占用”导致迁移后的插件数据无法访问,并触发插件加载失败。

Modifications / 改动点

  • 备份恢复 (importer.py):改进了目录条目识别逻辑,并在恢复目录前增加了冲突文件自动清理逻辑,确保数据能正确恢复。

  • 插件加载 (star_tools.py):在 get_data_dir 中增加了自愈逻辑,若发现目标路径被文件占用,将自动删除冲突文件并重建目录,确保插件正常初始化。

  • 插件安装 (updator.py, zip_updator.py):在解压安装阶段增加了路径兼容性处理,确保安装和更新解压过程能处理并修复已存在的路径冲突。

  • 核心 IO 工具 (io.py):重构了 remove_dir 函数,使其支持同时安全处理文件、软链接和目录的删除,提升了底层工具的健壮性。

  • This is NOT a breaking change. / 这不是一个破坏性变更。

Screenshots or Test Results / 运行截图或测试结果

data/plugin_data/ 下创建了一个与插件同名的空文件,正常运行
image
image
执行了 uv run pytest tests/test_backup.py tests/test_plugin_manager.py,共 91 个测试用例全部通过,确保没有引入任何回归问题。


Checklist / 检查清单

  • 😊 如果 PR 中有新加入的功能,已经通过 Issue / 邮件等方式和作者讨论过。
  • 👀 我的更改经过了良好的测试,并已在上方提供了“验证步骤”和“运行截图”
  • 🤓 我确保没有引入新依赖库。
  • 😮 我的更改没有引入恶意代码。

Summary by Sourcery

Resolve path conflicts and improve self-healing during backup restore and plugin installation to ensure plugin data directories are created correctly.

Bug Fixes:

  • Fix backup importer mis-handling ZIP directory entries that could create conflicting files instead of directories during restore.
  • Fix plugin data directory initialization failing when an existing file blocks the expected directory path.
  • Fix plugin update/install unzip routines failing when the target path is occupied by a file instead of a directory.

Enhancements:

  • Enhance core IO utilities so directory removal safely handles files, symlinks, and directories in a unified way.

1. 修复备份导入时目录条目被误识别为 0 字节文件的问题。
2. 增加插件加载和数据目录创建时的路径冲突自动清理逻辑。
3. 增强插件解压安装过程对现有冲突文件的兼容性。
4. 优化 remove_dir 工具类使其支持同时处理文件和目录的删除。
@dosubot dosubot Bot added size:S This PR changes 10-29 lines, ignoring generated files. area:core The bug / feature is about astrbot's core, backend feature:plugin The bug / feature is about AstrBot plugin system. labels Apr 23, 2026
Copy link
Copy Markdown
Contributor

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've left some high level feedback:

  • Several places now unconditionally delete a conflicting path when it exists as a file (e.g. in _import_directories, get_data_dir, and both unzip_file implementations); consider centralizing this into a shared helper that performs more cautious checks and logs the original file type/size to reduce the risk of accidental data loss and keep behavior consistent.
  • When calling unlink/os.remove on conflicting paths, you currently assume the removal always succeeds; you may want to handle potential exceptions (permissions, races, read-only FS) and either fall back or surface clearer error messages so that failures to self-heal don’t manifest as less obvious downstream errors.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Several places now unconditionally delete a conflicting path when it exists as a file (e.g. in `_import_directories`, `get_data_dir`, and both `unzip_file` implementations); consider centralizing this into a shared helper that performs more cautious checks and logs the original file type/size to reduce the risk of accidental data loss and keep behavior consistent.
- When calling `unlink`/`os.remove` on conflicting paths, you currently assume the removal always succeeds; you may want to handle potential exceptions (permissions, races, read-only FS) and either fall back or surface clearer error messages so that failures to self-heal don’t manifest as less obvious downstream errors.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request implements safer directory creation and cleanup across multiple modules, ensuring that existing non-directory files at target paths are removed before directory creation. The review feedback correctly identifies that the current use of exists() does not account for broken symbolic links, which would cause subsequent directory creation to fail. It is recommended to use lexists() (or is_symlink()) to handle these cases and to refactor the duplicated logic into a central utility function in astrbot/core/utils/io.py.

Comment thread astrbot/core/backup/importer.py Outdated
Comment thread astrbot/core/star/star_tools.py Outdated
Comment thread astrbot/core/star/updator.py Outdated
Comment thread astrbot/core/utils/io.py Outdated
Comment thread astrbot/core/zip_updator.py Outdated
@dosubot dosubot Bot added size:M This PR changes 30-99 lines, ignoring generated files. and removed size:S This PR changes 10-29 lines, ignoring generated files. labels Apr 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:core The bug / feature is about astrbot's core, backend feature:plugin The bug / feature is about AstrBot plugin system. size:M This PR changes 30-99 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant