diff --git a/AGENTS.md b/AGENTS.md index c81473e..c5e2590 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -294,11 +294,11 @@ Semantics( 测试文件位于 `test/unit/core/accessibility/a11y_semantics_test.dart`。 ## 变更记录 -- 2026-06-20:OSTree 修复必须区分 linyaps 正常 partial pull 与 fsck 检测损坏后的 partial。依据 linyaps 1.13.0 源码,`fetchRefMetaData()` 会使用 commit-only 或 `/info.json` subdir pull,因此单独出现 `partial commits not verified` 不能判定为仓库损坏;只有 `partial commits from fsck-detected corruption`、`Marking commit as partial` 后接 `Repository corruption encountered`,或 `.commitpartial` 内容为 `f` 才表示 fsck 截断后的损坏状态。环境管理执行 `pkexec ostree fsck --repo=/var/lib/linglong/repo --all --delete` 后若遇到 fsck partial,必须追加受控 `pkexec bash` 脚本扫描受影响 refs、尽量以 `deepin-linglong` 用户执行 `ostree pull --disable-static-deltas ` 重新拉取,并再次 `ostree fsck --quiet` 复验;复验仍出现 `Corrupted file object` 或 checksum mismatch 时,结果必须是失败并提示可能是上游仓库数据或 OSTree/玲珑 `bare-user-only` 模式兼容问题,禁止再显示“已修复成功”。 -- 2026-06-19:玲珑环境管理必须单独识别“玲珑数据目录权限异常”:`ll-package-manager` 服务用户为 `deepin-linglong:deepin-linglong`,如果 `/var/lib/linglong/.version`、`config.yaml`、`states.json`、`repo`、`layers`、`entries`、`merged` 被 root 或其他用户接管,会导致 `.version` 打不开、OSTree pull `mkdirat` 权限不足、layer 目录创建失败。该问题不得再笼统展示为 OSTree 仓库完整性异常;分析统一由 `LinglongEnvironmentManagementService` 执行 `stat -c %U:%G:%a:%n`,修复统一由环境管理对话框二次确认后调用 `repairLinglongDataPermissions()`,通过 `pkexec bash` 恢复属主、重启 `org.deepin.linglong.PackageManager.service` 并执行 `ll-cli --json repo show` 验证。UI 和 Provider 禁止直接拼接权限修复脚本或散写 `chown/systemctl` 命令。 -- 2026-06-19:玲珑环境管理的 OSTree 状态必须区分“运行可用性”和“深度对象完整性审计”。依据 linyaps 1.13.0 源码,运行路径主要依赖 `OSTreeRepo::init/loadFromPath/create` 打开仓库、读取 refs/cache/states 以及 `/var/lib/linglong/layers/` checkout 目录,`ostree fsck` 不是启动或运行前置条件。环境分析必须先执行 `ostree refs --repo=/var/lib/linglong/repo` 判断仓库是否可读;仅在 refs 可读后执行 `ostree fsck --repo=/var/lib/linglong/repo --quiet` 做深度审计。`fsck` 非零但 refs 可读时只能展示“OSTree 对象完整性风险”警告和修复建议,不得再展示笼统“OSTree 仓库完整性异常”或把顶层状态标为不可用;只有 refs 不可读才可展示“OSTree 仓库不可用”错误。 -- 2026-06-18:OSTree 修复必须兼容不同版本 `ostree fsck` 行为:优先执行 `pkexec ostree fsck --repo=/var/lib/linglong/repo --all --delete`;仅当输出明确表示不支持 `--all` 时,降级重试不带 `--all` 的 `--delete` 命令,并追加写入同一日志。若 `--delete` 不支持,禁止退化成只检查并伪装修复成功,必须提示当前版本无法自动删除损坏对象。新版 OSTree 输出 `partial commits from fsck-detected corruption` 或先输出 `Marking commit as partial` 后以 `Repository corruption encountered` 返回时,应视为自动删除步骤已执行但仍需重新拉取受影响应用/基础环境;单独出现 `partial commits not verified` 不能判定为仓库损坏。UI 要展示具体 partial commit 数量和后续处理建议,不能再显示笼统“修复失败”。 -- 2026-06-14:玲珑环境管理统一入口为设置页「玲珑环境管理」对话框;仓库增删改、默认仓库、优先级和镜像开关必须走 `LinglongRepositoryManagementRepository`(当前由 `LinglongCliRepositoryImpl` 实现),页面和 Provider 禁止直接拼 `ll-cli repo` 命令。环境分析、OSTree 完整性检查、`pkexec ostree fsck --all --delete` 修复、保存位置迁移脚本统一收敛到 `LinglongEnvironmentManagementService`;UI 只能通过 `linglongEnvironmentManagementProvider` 触发。保存位置迁移必须遵循 OpenAtom-Linyaps/linyaps#1411 的 systemd bind mount 方案,不得创造“自定义安装目录”业务语义;迁移前必须阻断运行中玲珑应用和安装/更新队列活跃任务,拒绝危险目标路径和目标空间不足场景;迁移脚本必须写日志、保留旧目录备份并在挂载后做 OSTree fsck 校验。OSTree 修复和保存位置迁移都必须显式二次确认,UI 只展示截断输出,完整输出以日志文件为准。 +- 2026-06-20:玲珑环境管理默认健康检查必须以 linyaps 运行路径为准:`ll-cli --json repo show`、`ll-cli --json list`、`ll-cli --json ps`、数据目录权限和空间状态。默认分析禁止再执行底层完整性审计,也禁止把底层 checksum/fsck 差异展示成用户侧“环境异常”;顶部指标统一展示“本地数据”,问题标题统一使用“玲珑本地数据不可用/检测失败”。底层修复命令只属于用户显式确认后的“玲珑本地数据修复”和日志细节。 +- 2026-06-20:玲珑本地数据修复必须区分 linyaps 正常 partial pull 与完整性审计检测损坏后的 partial。依据 linyaps 1.13.0 源码,`fetchRefMetaData()` 会使用 commit-only 或 `/info.json` subdir pull,因此单独出现 `partial commits not verified` 不能判定为仓库损坏;只有 `partial commits from fsck-detected corruption`、`Marking commit as partial` 后接 `Repository corruption encountered`,或 `.commitpartial` 内容为 `f` 才表示审计截断后的损坏状态。显式修复后若遇到 fsck partial,必须追加受控 `pkexec bash` 脚本扫描受影响 refs、尽量以 `deepin-linglong` 用户重新拉取,并再次复验;复验仍出现 `Corrupted file object` 或 checksum mismatch 时,结果必须是失败并提示可能是上游仓库数据或 linyaps 本地存储模式兼容问题,禁止再显示“已修复成功”。 +- 2026-06-19:玲珑环境管理必须单独识别“玲珑数据目录权限异常”:`ll-package-manager` 服务用户为 `deepin-linglong:deepin-linglong`,如果 `/var/lib/linglong/.version`、`config.yaml`、`states.json`、`repo`、`layers`、`entries`、`merged` 被 root 或其他用户接管,会导致 `.version` 打不开、拉取对象 `mkdirat` 权限不足、layer 目录创建失败。该问题不得再笼统展示为本地数据仓库完整性异常;分析统一由 `LinglongEnvironmentManagementService` 执行 `stat -c %U:%G:%a:%n`,修复统一由环境管理对话框二次确认后调用 `repairLinglongDataPermissions()`,通过 `pkexec bash` 恢复属主、重启 `org.deepin.linglong.PackageManager.service` 并执行 `ll-cli --json repo show` 验证。UI 和 Provider 禁止直接拼接权限修复脚本或散写 `chown/systemctl` 命令。 +- 2026-06-18:玲珑本地数据修复必须兼容不同版本底层工具行为:优先执行带全量对象和自动清理的受控修复命令;仅当输出明确表示不支持全量参数时,降级重试不带全量参数的自动清理命令,并追加写入同一日志。若自动清理参数不支持,禁止退化成只检查并伪装修复成功,必须提示当前系统组件无法自动清理问题对象。新版工具输出 `partial commits from fsck-detected corruption` 或先输出 `Marking commit as partial` 后以 `Repository corruption encountered` 返回时,应视为自动清理步骤已执行但仍需重新拉取受影响应用/基础环境;单独出现 `partial commits not verified` 不能判定为仓库损坏。UI 要展示具体 partial commit 数量和后续处理建议,不能再显示笼统“修复失败”。 +- 2026-06-14:玲珑环境管理统一入口为设置页「玲珑环境管理」对话框;仓库增删改、默认仓库、优先级和镜像开关必须走 `LinglongRepositoryManagementRepository`(当前由 `LinglongCliRepositoryImpl` 实现),页面和 Provider 禁止直接拼 `ll-cli repo` 命令。环境分析、本地数据修复、数据目录权限修复、保存位置迁移脚本统一收敛到 `LinglongEnvironmentManagementService`;UI 只能通过 `linglongEnvironmentManagementProvider` 触发。保存位置迁移必须遵循 OpenAtom-Linyaps/linyaps#1411 的 systemd bind mount 方案,不得创造“自定义安装目录”业务语义;迁移前必须阻断运行中玲珑应用和安装/更新队列活跃任务,拒绝危险目标路径和目标空间不足场景;迁移脚本必须写日志、保留旧目录备份并在挂载后做只读校验。本地数据修复和保存位置迁移都必须显式二次确认,UI 只展示截断输出,完整输出以日志文件为准。 - 2026-06-10:下载管理当前任务主卡片的阶段文案只允许在进度条上方展示;应用名下方标题区只展示应用身份补充信息(如版本),不得重复渲染 `InstallTask.displayMessage`。慢安装提示行需保持黄色提示图标与中文 caption 首行视觉对齐,后续调整下载中心提示文案时必须同步检查图标/文字对齐。 - 2026-06-08:GitHub Release / Nightly release notes 的总结范围必须由 `generate-changelog.sh ` 或仓库变量 `LINGLONG_RELEASE_NOTES_START_REF` 显式指定,禁止把“从某个版本开始总结”写进 AI prompt 的临时自然语言要求;Claude Code 只允许输出 `{"items":["用户可读描述"]}` 结构化文案数组,最终 `## Release Notes` 和 `1、2、3` 编号描述列表必须由脚本渲染,禁止 AI 输出编号、分类前缀或 Markdown,避免再次出现 `0、` 起始编号或 commit 标题复述。prompt 中禁止保留让 AI 删除文件、git 提交或修改仓库的指令。 - 2026-06-08:下载中心 UI 统一采用轻工作面板结构,保持 `showDownloadManagerDialog(context)` 单一入口;面板包含固定顶栏、概览条、当前任务主卡、等待/历史紧凑行和底部状态栏。后续优化只允许调整展示层,不得新增 `ll-cli` 调用、绕过 `installQueueProvider`,也不得改变 `InstallTask.commandOutput` 作为“复制日志”唯一来源的约定。 diff --git a/build/scripts/generate-changelog.sh b/build/scripts/generate-changelog.sh index 33953ce..58a8e30 100755 --- a/build/scripts/generate-changelog.sh +++ b/build/scripts/generate-changelog.sh @@ -69,7 +69,9 @@ format_release_notes_markdown() { /^- / { text = substr($0, 3) if (item_count < 5 && is_visible_change(section, text)) { - items[++item_count] = item_count "、" text + # awk 对赋值左右两侧求值顺序没有统一保证,拆开递增可避免 mawk 输出 0 起始编号。 + item_count++ + items[item_count] = item_count "、" text } next } diff --git a/docs/21-linglong-environment-management.md b/docs/21-linglong-environment-management.md index 79fcf17..1fcfff6 100644 --- a/docs/21-linglong-environment-management.md +++ b/docs/21-linglong-environment-management.md @@ -1,16 +1,16 @@ # 玲珑环境管理、修复与保存位置迁移 -> 文档版本:1.4 +> 文档版本:1.5 > 更新日期:2026-06-20 > 适用范围:设置页「玲珑环境管理」入口、仓库管理、环境分析与修复、保存位置迁移 ## 背景 -项目已有启动期玲珑环境检测、自动安装、安装队列和废弃 base 清理能力,但运行期缺少集中诊断入口。用户遇到仓库配置错误、OSTree 本地仓库损坏、`/var/lib/linglong` 空间不足时,过去只能从安装失败日志里看到零散错误。 +项目已有启动期玲珑环境检测、自动安装、安装队列和废弃 base 清理能力,但运行期缺少集中诊断入口。用户遇到仓库配置错误、玲珑本地数据不可读、`/var/lib/linglong` 空间不足时,过去只能从安装失败日志里看到零散错误。 本功能在设置页新增「玲珑环境管理」入口,打开统一对话框,包含: -- 「环境分析」:展示 ll-cli、仓库、数据目录权限、磁盘、OSTree、运行中应用等诊断结果。 +- 「环境分析」:展示 ll-cli、仓库、数据目录权限、磁盘、本地数据、运行中应用等诊断结果。 - 「仓库管理」:查看、添加、修改、删除仓库,设置默认仓库、优先级和镜像开关。 - 「保存位置」:按上游建议通过 systemd bind mount 迁移 `/var/lib/linglong`。 @@ -18,21 +18,21 @@ 1. 远程 UOS 25 / Loong64 环境实测 `ll-cli 1.12.2` 支持 `repo add/remove/update/set-default/show/set-priority/enable-mirror/disable-mirror`。 2. `ll-cli --json repo show` 返回 `defaultRepo`、`repos`、`version`,可作为仓库列表首选解析来源;文本表格输出作为兜底。 -3. 玲珑本地根目录为 `/var/lib/linglong`,OSTree 仓库位于 `/var/lib/linglong/repo`。 +3. 玲珑本地根目录为 `/var/lib/linglong`,本地数据仓库位于 `/var/lib/linglong/repo`。 4. OpenAtom-Linyaps/linyaps#1411 中上游维护者说明当前不支持直接自定义安装位置,推荐通过 systemd `.mount` 将目标目录 bind 到 `/var/lib/linglong`。 -5. linyaps 1.13.0 源码中仓库运行路径主要通过 `OSTreeRepo::init/loadFromPath/create` 打开仓库、读取 refs/cache/states,并通过 `/var/lib/linglong/layers/` checkout 目录支撑运行;源码未把 `ostree fsck` 作为启动或运行前置条件。 -6. 远程环境实测 `ostree fsck --repo=/var/lib/linglong/repo --quiet` 可发现 corrupted file object,但 `ll-cli --json repo show`、`ll-cli --json list`、`ll-cli --json ps` 和 `ostree refs --repo=/var/lib/linglong/repo` 仍可正常执行。UI 必须区分“仓库不可读”和“深度对象完整性风险”,不能仅凭 `fsck` 非零码展示笼统的运行异常。 -7. 远程 Loong64 环境实测 `ll-package-manager` 以 `deepin-linglong:deepin-linglong` 运行;当 `/var/lib/linglong/.version`、`states.json`、`repo`、`layers`、`entries`、`merged` 被 root 接管时,会出现 `couldn't open "/var/lib/linglong/.version"`、`ostree_repo_pull_with_options [code 14]: mkdirat: 权限不够`、`failed to create layer dir ... 权限不够` 等错误。环境管理必须把这类问题识别为“玲珑数据目录权限异常”,不能混同为 OSTree 仓库完整性异常。 +5. linyaps 1.13.0 源码中运行路径主要通过仓库初始化、仓库配置、`states.json`、refs/cache 以及 `/var/lib/linglong/layers/` checkout 目录支撑安装、列表和运行;源码未把底层完整性审计作为启动或运行前置条件。 +6. 远程环境实测底层完整性审计可发现对象 checksum mismatch,但 `ll-cli --json repo show`、`ll-cli --json list` 和 `ll-cli --json ps` 仍可正常执行。UI 默认健康结论必须以 linyaps 自身运行路径为准,不能仅凭底层审计非零码展示笼统运行异常。 +7. 远程 Loong64 环境实测 `ll-package-manager` 以 `deepin-linglong:deepin-linglong` 运行;当 `/var/lib/linglong/.version`、`states.json`、`repo`、`layers`、`entries`、`merged` 被 root 接管时,会出现 `couldn't open "/var/lib/linglong/.version"`、`ostree_repo_pull_with_options [code 14]: mkdirat: 权限不够`、`failed to create layer dir ... 权限不够` 等错误。环境管理必须把这类问题识别为“玲珑数据目录权限异常”,不能混同为本地数据仓库完整性异常。 8. linyaps 1.13.0 的 `OSTreeRepo::fetchRefMetaData()` 会使用 `OSTREE_REPO_PULL_FLAGS_COMMIT_ONLY` 或 `/info.json` subdir pull,因此普通 `.commitpartial` 或 `partial commits not verified` 不能直接等同于仓库损坏。 -9. OSTree 源码中 `.commitpartial` 内容为 `f` 时表示 `OSTREE_REPO_COMMIT_STATE_FSCK_PARTIAL`,后续 `ostree fsck` 会以 `partial commits from fsck-detected corruption` 返回错误。远程 Loong64 环境和本机干净 `bare-user-only` 仓库对照验证显示,部分 `stable` loong64 ref 重新拉取后仍会出现 `Corrupted file object; checksum expected=... actual=...`,这类结果应提示上游仓库数据或 OSTree/玲珑仓库模式兼容风险,不能继续伪装成已修复。 +9. 底层仓库 `.commitpartial` 内容为 `f` 时表示完整性审计截断后的 partial,后续修复命令会以 `partial commits from fsck-detected corruption` 返回错误。远程 Loong64 环境和本机干净 `bare-user-only` 仓库对照验证显示,部分 `stable` loong64 ref 重新拉取后仍会出现 `Corrupted file object; checksum expected=... actual=...`,这类结果应提示上游仓库数据或 linyaps 本地存储模式兼容风险,不能继续伪装成已修复。 ## 实现边界 - 不新增“任意配置玲珑安装目录”的业务语义。 - 不把仓库源重新变成商店业务配置;后端接口仍使用 `AppConfig.defaultStoreRepoName`。 - 不直接修改玲珑内部数据库。 -- 不在页面或 Provider 中散写 `ll-cli repo`、`ostree fsck`、`pkexec`、`rsync`、`systemctl` 命令。 -- 不静默执行数据目录权限修复、`ostree fsck --delete` 或保存位置迁移,所有副作用操作必须由用户确认。 +- 不在页面或 Provider 中散写 `ll-cli repo`、本地数据修复、`pkexec`、`rsync`、`systemctl` 命令。 +- 不静默执行数据目录权限修复、本地数据修复或保存位置迁移,所有副作用操作必须由用户确认。 - 不在安装/更新队列仍有活跃任务或玲珑应用仍在运行时迁移保存位置。 ## 分层契约 @@ -41,7 +41,7 @@ - `LinglongRepositoryConfig`:仓库配置版本、默认仓库、仓库列表。 - `LinglongRepoInfo`:单个仓库的 `name/url/alias/priority/isDefault/isMirrorEnabled`。 -- `LinglongEnvironmentAnalysis`:环境检测结果、数据目录权限、存储信息、OSTree 检查、问题列表、运行中应用数量。 +- `LinglongEnvironmentAnalysis`:环境检测结果、数据目录权限、存储信息、本地数据检查、问题列表、运行中应用数量。 - `LinglongDataPermissionCheckResult`:`/var/lib/linglong` 关键目录和状态文件是否由玲珑服务用户持有,并具备 owner 写权限。 - `LinglongEnvironmentIssue`:问题 code、严重级别、标题、说明、诊断详情和可执行修复动作。 - `LinglongStorageInfo`:根目录、文件系统、挂载源、容量、已用、可用、使用率、bind mount 状态。 @@ -72,9 +72,9 @@ - 执行 `stat -c %U:%G:%a:%n` 检查 `/var/lib/linglong`、`.version`、`config.yaml`、`states.json`、`repo`、`layers`、`entries`、`merged` 的属主和 owner 写权限。 - 执行 `df -PB1 /var/lib/linglong` 读取空间。 - 执行 `findmnt --json /var/lib/linglong` 读取挂载状态。 -- 先执行 `ostree refs --repo=/var/lib/linglong/repo` 做轻量只读可用性检查。 -- 仅在 refs 可读时执行 `ostree fsck --repo=/var/lib/linglong/repo --quiet` 做深度对象完整性审计。 -- 通过 `pkexec ostree fsck --repo=/var/lib/linglong/repo --all --delete` 执行 OSTree 修复。 +- 执行 `ll-cli --json list` 做 linyaps 本地数据读取能力检查。 +- 默认环境分析不执行底层完整性审计,避免把不影响 linyaps 运行路径的审计差异误报为基础环境异常。 +- 通过受控特权命令执行玲珑本地数据修复,底层命令细节只进入日志和高级诊断。 - 通过 `pkexec bash ` 执行数据目录权限修复脚本。 - 通过 `pkexec bash ` 执行保存位置迁移脚本。 - 修复与迁移日志写入 XDG logs 目录;UI 只展示截断摘要。 @@ -86,7 +86,7 @@ - `load()` 同时加载环境分析和仓库配置。 - 仓库写操作成功后刷新仓库配置。 - 数据目录权限修复后重新执行完整分析。 -- OSTree 修复后重新执行完整分析。 +- 本地数据修复后重新执行完整分析。 - 保存位置迁移前读取 `installQueueProvider`,只要当前任务或等待队列仍存在,就直接返回失败结果,不进入特权脚本。 ### Presentation @@ -112,10 +112,8 @@ 3. `/var/lib/linglong` 所在文件系统容量、已用、可用和使用率。 4. `/var/lib/linglong` 是否处于 bind mount。 5. `/var/lib/linglong` 关键目录和状态文件是否由 `deepin-linglong:deepin-linglong` 持有,并具备 owner 写权限。 -6. `ostree` 命令是否可用。 -7. `/var/lib/linglong/repo` refs 是否可读取。 -8. `/var/lib/linglong/repo` 深度对象完整性审计是否存在风险。 -9. `ll-cli --json ps` 是否存在运行中应用。 +6. `ll-cli --json list` 是否能按 linyaps 运行路径读取本地应用数据。 +7. `ll-cli --json ps` 是否存在运行中应用。 数据目录权限状态模型: @@ -123,12 +121,12 @@ - `isOk=false`:关键路径不是 `deepin-linglong:deepin-linglong`,或 owner 不具备写权限,展示“玲珑数据目录权限异常”错误,并列出异常路径。 - `isOk=true`:关键路径属主和 owner 写权限符合玲珑服务运行要求。 -OSTree 状态模型: +本地数据状态模型: -- `isAvailable=false`:`ostree` 命令不可用或无法执行基础检查,展示“OSTree 工具不可用”警告。 -- `isOk=false`:`ostree refs --repo=/var/lib/linglong/repo` 无法读取本地仓库,展示“OSTree 仓库不可用”错误,可引导尝试修复。 -- `isOk=true && hasIntegrityWarning=true`:refs 可读但 `ostree fsck --quiet` 发现对象损坏,展示“OSTree 对象完整性风险”警告,并保留修复入口。 -- `isOk=true && hasIntegrityWarning=false`:仓库可读且深度审计未发现风险,展示“正常”。 +- `isAvailable=false`:无法执行 `ll-cli --json list`,展示“玲珑本地数据检测失败”。 +- `isOk=false`:`ll-cli --json list` 返回失败,展示“玲珑本地数据不可用”,并保留原始输出。 +- `isOk=true`:linyaps 能按运行路径读取本地应用数据,展示“正常”。 +- `hasIntegrityWarning` 是历史字段,默认环境分析不再设置;底层审计详情只写入修复日志或高级诊断,不作为用户侧主健康状态。 问题 code: @@ -142,8 +140,8 @@ OSTree 状态模型: 严重级别: -- `error`:缺少可用 ll-cli、仓库未配置、玲珑数据目录权限异常、OSTree 仓库 refs 不可读、空间严重不足。 -- `warning`:ostree 工具不可用、OSTree 深度对象完整性风险、空间偏高、有运行中应用阻断迁移。 +- `error`:缺少可用 ll-cli、仓库未配置、玲珑数据目录权限异常、本地应用数据不可读、空间严重不足。 +- `warning`:空间偏高、有运行中应用阻断迁移。 - `info`:保留给后续状态说明。 ## 修复动作 @@ -181,11 +179,11 @@ pkexec bash - 脚本必须写完整日志;UI 只展示截断输出。 - 修复后自动重新执行环境分析。 -该动作不能替代 OSTree 对象修复,也不能删除损坏对象;它只处理服务用户无法读写本地数据树导致的权限类故障。 +该动作不能替代本地数据对象修复,也不能删除问题对象;它只处理服务用户无法读写本地数据树导致的权限类故障。 -### 修复 OSTree 仓库 +### 修复玲珑本地数据 -默认只分析,不自动修复。用户点击修复并确认后执行: +默认分析不主动执行该动作。用户点击修复并确认后,服务层执行受控特权命令,当前底层实现为: ```bash pkexec ostree fsck --repo=/var/lib/linglong/repo --all --delete @@ -198,18 +196,18 @@ pkexec ostree fsck --repo=/var/lib/linglong/repo --all --delete - UI 只展示截断输出。 - 修复后自动重新执行环境分析。 -`--delete` 会删除损坏对象,后续安装或更新可重新拉取缺失内容。这个动作不能静默执行。 +该命令会清理明确存在问题的对象,后续安装或更新可重新拉取缺失内容。这个动作不能静默执行,也不能在用户侧描述为默认环境健康检查。 -OSTree 版本兼容规则: +底层修复工具兼容规则: - 优先执行 `pkexec ostree fsck --repo=/var/lib/linglong/repo --all --delete`。 -- 如果输出明确表示当前 OSTree 不支持 `--all`,降级重试 `pkexec ostree fsck --repo=/var/lib/linglong/repo --delete`,并继续写入同一个日志文件。 -- 如果输出明确表示当前 OSTree 不支持 `--delete`,不能退化成只检查命令,也不能提示修复成功;必须告知用户该版本无法自动删除损坏对象,需要升级 ostree 或使用发行版工具手动修复。 +- 如果输出明确表示当前系统组件不支持 `--all`,降级重试 `pkexec ostree fsck --repo=/var/lib/linglong/repo --delete`,并继续写入同一个日志文件。 +- 如果输出明确表示当前系统组件不支持 `--delete`,不能退化成只检查命令,也不能提示修复成功;必须告知用户当前组件无法自动清理问题对象,需要升级系统相关组件或使用发行版工具处理。 - 普通 `partial commits not verified` 可能来自 linyaps 元数据/子路径拉取;只有同时出现 `fsck-detected corruption`,或 `.commitpartial` marker 内容为 `f`,才进入损坏修复后续流程。 -- 新版 OSTree 可能在删除损坏对象后输出 `partial commits from fsck-detected corruption` 并以非零码退出;部分版本会先输出 `Marking commit as partial` 和 `Repository corruption encountered`,不会继续走到 `fsck-detected corruption` 汇总错误。两类结果都表示需要进入 fsck partial 后续流程。服务层必须追加执行受控 `pkexec bash` 脚本:扫描 `/var/lib/linglong/repo/state/*.commitpartial`,只选择 marker 内容为 `f` 且能映射到 `ostree refs` 的 ref,使用仓库 ref 中的 remote 重新执行 `ostree pull --disable-static-deltas `,然后再次执行 `ostree fsck --repo=/var/lib/linglong/repo --quiet` 复验。 +- 新版底层工具可能在删除问题对象后输出 `partial commits from fsck-detected corruption` 并以非零码退出;部分版本会先输出 `Marking commit as partial` 和 `Repository corruption encountered`,不会继续走到 `fsck-detected corruption` 汇总错误。两类结果都表示需要进入 partial 后续流程。服务层必须追加执行受控 `pkexec bash` 脚本:扫描 `/var/lib/linglong/repo/state/*.commitpartial`,只选择 marker 内容为 `f` 且能映射到本地 ref 的记录,使用仓库 ref 中的 remote 重新拉取受影响 ref,然后再次复验。 - 重新拉取必须尽量以 `deepin-linglong` 用户执行,避免 root 拉取后再次制造数据目录权限问题;如果系统缺少 `runuser` 或服务用户不存在,脚本可回退为当前特权上下文执行,但日志必须保留完整命令输出。 -- 复验通过时才能提示修复成功;复验仍出现 `Corrupted file object`、`checksum expected=... actual=...` 或其他 corruption 时,必须提示“重新拉取后复验仍未通过”,并说明可能需要上游仓库数据或 OSTree/玲珑兼容性修复。 -- partial commit 数量优先从 OSTree 输出中提取,例如 `32 partial commits not verified` 展示为“32 个 partial commits”。完整输出仍以日志文件为准。 +- 复验通过时才能提示修复成功;复验仍出现 `Corrupted file object`、`checksum expected=... actual=...` 或其他 corruption 时,必须提示“重新拉取后复验仍未通过”,并说明可能需要上游仓库数据或 linyaps 本地存储兼容性修复。 +- partial commit 数量优先从底层输出中提取,例如 `32 partial commits not verified` 展示为“32 个 partial commits”。完整输出仍以日志文件为准。 ## 保存位置迁移 @@ -261,10 +259,10 @@ WantedBy=multi-user.target 10. `systemctl daemon-reload`。 11. `systemctl enable --now var-lib-linglong.mount`。 12. `findmnt /var/lib/linglong` 验证挂载。 -13. 若存在 `ostree`,执行 `ostree fsck --repo=/var/lib/linglong/repo --quiet` 做迁移后校验。 +13. 若存在底层仓库工具,执行只读校验做迁移后校验。 14. 输出旧目录备份路径。 -脚本带 `ERR` trap:如果挂载前失败,会尝试把备份目录恢复回 `/var/lib/linglong`。挂载成功后的 fsck 失败会保留挂载状态和备份路径,便于用户查看日志后人工处理。 +脚本带 `ERR` trap:如果挂载前失败,会尝试把备份目录恢复回 `/var/lib/linglong`。挂载成功后的只读校验失败会保留挂载状态和备份路径,便于用户查看日志后人工处理。 ### 备份清理 @@ -274,8 +272,8 @@ WantedBy=multi-user.target ### 环境分析 -- 顶部展示 ll-cli 版本、运行中应用数量、空间使用率、OSTree 状态;OSTree 必须区分“正常”“可用,有风险”“不可用”“工具不可用”。 -- 数据目录权限异常必须在环境分析问题列表中展示,修复入口和 OSTree 修复入口同级,不进入保存位置 Tab。 +- 顶部展示 ll-cli 版本、运行中应用数量、空间使用率、本地数据状态;本地数据只区分“正常”“不可用”“检测失败”等 linyaps 运行路径状态。 +- 数据目录权限异常必须在环境分析问题列表中展示,修复入口和本地数据修复入口同级,不进入保存位置 Tab。 - 问题按严重程度排序展示。 - 每个问题展示标题、描述、原始诊断详情和可执行动作。 - 修复按钮必须先弹出确认对话框。 @@ -297,7 +295,7 @@ WantedBy=multi-user.target ## 性能与响应 -- 权限修复、`ostree fsck` 和保存位置迁移都可能耗时较长,必须异步执行。 +- 权限修复、本地数据修复和保存位置迁移都可能耗时较长,必须异步执行。 - UI 操作期间只显示轻量进度状态,不阻塞主线程。 - 命令输出截断到 4000 字符,避免大量 corrupted object 输出导致 UI 卡顿。 - 仓库列表只在打开对话框、刷新或写操作成功后重新加载。 @@ -307,9 +305,9 @@ WantedBy=multi-user.target 已覆盖: - 仓库 JSON 解析、ANSI 文本兜底解析、仓库命令参数。 -- 环境分析中的数据目录权限异常、OSTree refs 不可读、OSTree 深度对象完整性风险、空间不足和运行中应用阻断。 +- 环境分析中的数据目录权限异常、本地应用数据不可读、空间不足和运行中应用阻断。 - 数据目录权限修复脚本、修复命令和日志参数。 -- OSTree 修复命令和日志参数。 +- 本地数据修复命令和日志参数。 - 保存位置迁移脚本内容、危险目标路径拒绝、目标空间不足拒绝。 - Provider 状态加载、修复后刷新、仓库写操作刷新、安装队列活跃时阻断迁移。 - 设置页入口和三 Tab 对话框基础展示。 diff --git a/lib/application/services/linglong_environment_management_service.dart b/lib/application/services/linglong_environment_management_service.dart index 94b26e1..064d89f 100644 --- a/lib/application/services/linglong_environment_management_service.dart +++ b/lib/application/services/linglong_environment_management_service.dart @@ -1,6 +1,6 @@ /// 玲珑运行期环境管理应用服务。 /// -/// 该文件集中承载设置页「玲珑环境管理」所需的环境分析、OSTree 修复和保存位置迁移编排。 +/// 该文件集中承载设置页「玲珑环境管理」所需的环境分析、本地数据修复和保存位置迁移编排。 /// 这些能力都涉及系统命令、管理员权限或磁盘状态,必须收敛在服务层,避免页面层直接拼接命令导致行为漂移。 library; @@ -88,16 +88,16 @@ class LinglongEnvironmentManagementService { ); } - /// 执行 OSTree 仓库修复。 + /// 执行玲珑本地数据修复。 /// - /// 新版 OSTree 在 `--delete` 删除损坏对象后,可能因为 affected commit 被标记为 - /// fsck partial 而返回非零码;这类结果不能当成修复成功,需要重新拉取并复验。 - /// 旧版 OSTree 可能不支持 `--all`,此处会降级到仅带 `--delete` 的修复命令。 + /// 该动作面向用户时只表达为“玲珑本地数据修复”;底层仍需要调用仓库存储工具清理 + /// 已明确标记的问题对象,并在必要时重新拉取受影响内容后复验。 + /// 旧系统组件可能不支持完整参数,此处会降级到兼容命令。 Future repairOstreeRepository({ String? logFilePath, }) async { final resolvedLogFilePath = - logFilePath ?? await _createLogFilePath('linglong-ostree-repair'); + logFilePath ?? await _createLogFilePath('linglong-local-data-repair'); final primaryResult = await _runOstreeRepairCommand( includeAllObjects: true, @@ -157,7 +157,7 @@ class LinglongEnvironmentManagementService { /// 修复玲珑数据目录属主。 /// /// `ll-package-manager` 以 `deepin-linglong` 身份运行,数据目录如果被 root 接管, - /// 会导致 `.version` 打不开、OSTree pull 无法 mkdir、layer 目录无法创建。 + /// 会导致 `.version` 打不开、对象拉取无法 mkdir、layer 目录无法创建。 Future repairLinglongDataPermissions({ String? logFilePath, }) async { @@ -396,9 +396,9 @@ echo "玲珑数据目录权限已修复。" '''; } - /// 构建 OSTree partial commit 重新拉取脚本。 + /// 构建玲珑本地数据 partial commit 重新拉取脚本。 /// - /// OSTree 普通 partial commit 可能来自 linyaps 的元数据/子路径拉取;只有 marker + /// 普通 partial commit 可能来自 linyaps 的元数据/子路径拉取;只有 marker /// 内容为 `f` 的 commit 才是 fsck 检测损坏后的截断状态。脚本只重拉这类 ref, /// 并在最后重新执行 fsck,让 UI 依据复验结果展示是否真正恢复。 String buildOstreePartialRepullScript() { @@ -411,18 +411,18 @@ REPO="\$ROOT/repo" SERVICE_USER=${_shellSingleQuote(_linglongServiceUser)} if [ ! -d "\$REPO" ]; then - echo "OSTree 仓库目录不存在:\$REPO" >&2 + echo "玲珑本地数据仓库目录不存在:\$REPO" >&2 exit 2 fi if ! command -v ostree >/dev/null 2>&1; then - echo "ostree 命令不可用,无法重新拉取受影响 ref。" >&2 + echo "底层仓库工具不可用,无法重新拉取受影响 ref。" >&2 exit 3 fi repo_mode="\$(ostree config --repo="\$REPO" get core.mode 2>/dev/null || true)" if [ -n "\$repo_mode" ]; then - echo "OSTree repo mode: \$repo_mode" + echo "玲珑本地数据仓库模式:\$repo_mode" fi run_ostree_pull() { @@ -572,36 +572,17 @@ exit "\$verify_rc" ); } - if (!ostree.isAvailable) { - issues.add( - LinglongEnvironmentIssue( - code: LinglongEnvironmentIssueCode.ostreeToolUnavailable, - severity: LinglongEnvironmentIssueSeverity.warning, - title: 'OSTree 工具不可用', - description: '无法执行 OSTree 仓库完整性检查,请确认 ostree 命令已安装。', - rawDetail: ostree.detail, - ), - ); - } else if (!ostree.isOk) { + if (!ostree.isAvailable || !ostree.isOk) { + final isDetectionFailure = !ostree.isAvailable; issues.add( LinglongEnvironmentIssue( code: LinglongEnvironmentIssueCode.ostreeRepositoryCorrupted, severity: LinglongEnvironmentIssueSeverity.error, - title: 'OSTree 仓库不可用', - description: '无法读取玲珑本地 OSTree 仓库 refs,可尝试执行修复;若目录缺失或权限异常,请先恢复仓库路径。', - repairAction: LinglongEnvironmentRepairAction.ostreeFsckDelete, - rawDetail: ostree.detail, - ), - ); - } else if (ostree.hasIntegrityWarning) { - issues.add( - LinglongEnvironmentIssue( - code: LinglongEnvironmentIssueCode.ostreeRepositoryCorrupted, - severity: LinglongEnvironmentIssueSeverity.warning, - title: 'OSTree 对象完整性风险', - description: - '深度校验发现对象存储存在损坏记录,但当前玲珑仓库仍可读取。' - '建议在空闲时执行修复,系统会删除可清理的损坏对象,必要时重新拉取受影响应用或基础环境并复验。', + title: isDetectionFailure ? '玲珑本地数据检测失败' : '玲珑本地数据不可用', + description: isDetectionFailure + ? '无法执行 linyaps 本地数据读取检查,请确认 ll-cli 和 package-manager 服务状态。' + : '无法按 linyaps 运行路径读取已安装应用数据,可能影响应用列表、安装或运行。' + '请先确认玲珑数据目录权限和基础环境状态,再按需执行修复。', repairAction: LinglongEnvironmentRepairAction.ostreeFsckDelete, rawDetail: ostree.detail, ), @@ -758,71 +739,32 @@ exit "\$verify_rc" } Future _checkOstreeRepository() async { - // linyaps 启动和运行时主要依赖仓库能被打开并读取 refs/cache。 - // 因此先执行轻量只读检查,避免把深度 fsck 的对象风险误判为整体不可用。 - final refsResult = await _run([ - 'ostree', - 'refs', - '--repo=$_linglongRootPath/repo', + // linyaps 启动、列表、运行和安装路径都通过 ll-cli/package-manager 访问本地 + // config、states.json 与 layers 目录;这里按 linyaps 自身能力做健康判断, + // 不再把底层存储实现的深度 fsck 结果暴露为默认环境问题。 + final listResult = await _run([ + 'll-cli', + '--json', + 'list', ], timeout: const Duration(minutes: 2)); - if (refsResult == null) { + if (listResult == null) { return const LinglongOstreeCheckResult( isAvailable: false, isOk: false, - detail: 'ostree refs 命令执行失败', - ); - } - - if (refsResult.exitCode == 127) { - return LinglongOstreeCheckResult( - isAvailable: false, - isOk: false, - detail: _truncateOutput(_primaryOutput(refsResult)), + detail: 'll-cli list 命令执行失败', ); } - if (!refsResult.success) { + if (!listResult.success) { return LinglongOstreeCheckResult( isAvailable: true, isOk: false, - detail: _truncateOutput(_primaryOutput(refsResult)), + detail: _truncateOutput(_combinedCommandOutput(listResult)), ); } - // fsck 作为深度对象审计保留;它发现风险时只影响完整性提示, - // 不覆盖上面的 linyaps 可用性判断。 - final fsckResult = await _run([ - 'ostree', - 'fsck', - '--repo=$_linglongRootPath/repo', - '--quiet', - ], timeout: const Duration(minutes: 2)); - - if (fsckResult == null) { - return const LinglongOstreeCheckResult( - isAvailable: false, - isOk: false, - detail: 'ostree fsck 命令执行失败', - ); - } - - if (fsckResult.exitCode == 127) { - return LinglongOstreeCheckResult( - isAvailable: false, - isOk: false, - detail: _truncateOutput(_primaryOutput(fsckResult)), - ); - } - - return LinglongOstreeCheckResult( - isAvailable: true, - isOk: true, - hasIntegrityWarning: !fsckResult.success, - detail: fsckResult.success - ? null - : _truncateOutput(_combinedCommandOutput(fsckResult)), - ); + return const LinglongOstreeCheckResult(isAvailable: true, isOk: true); } _DfInfo _parseDfOutput(String? output) { @@ -983,10 +925,10 @@ exit "\$verify_rc" } } - /// 按当前 OSTree 版本能力执行一次修复命令。 + /// 按当前底层存储工具能力执行一次本地数据修复命令。 /// /// `includeAllObjects` 只控制是否携带 `--all`,`--delete` 是修复语义必须项; - /// 如果目标 OSTree 不支持 `--delete`,调用方会返回明确失败而不是退化成只检查。 + /// 如果目标工具不支持 `--delete`,调用方会返回明确失败而不是退化成只检查。 Future _runOstreeRepairCommand({ required bool includeAllObjects, required String logFilePath, @@ -1012,16 +954,16 @@ exit "\$verify_rc" ); } - /// 重新拉取被 fsck 标记为 partial 的受影响 ref,并把输出追加到同一份日志。 + /// 重新拉取被修复流程标记为 partial 的受影响 ref,并把输出追加到同一份日志。 /// - /// 重拉可能下载大量基础环境对象,因此只在 `fsck-detected corruption` 已明确出现后执行, - /// 且由最终 fsck 复验结果决定是否算修复成功。 + /// 重拉可能下载大量基础环境对象,因此只在底层工具已经明确发现需重拉状态后执行, + /// 且由最终复验结果决定是否算修复成功。 Future _runOstreePartialRepullCommand({ required String logFilePath, }) async { final scriptFile = await _writeTemporaryScript( buildOstreePartialRepullScript(), - prefix: 'linglong-ostree-repull', + prefix: 'linglong-local-data-repull', ); try { @@ -1039,9 +981,9 @@ exit "\$verify_rc" } } - /// 将 OSTree 修复命令结果归类为 UI 可理解的业务状态。 + /// 将本地数据修复命令结果归类为 UI 可理解的业务状态。 /// - /// 这里刻意不只看退出码:新版 OSTree 会在删除损坏对象后返回 partial commit 错误, + /// 这里刻意不只看退出码:底层工具可能在清理对象后返回 partial commit 错误, /// 这种状态已经由调用方进入重拉复验流程;旧版缺少参数则需要给出可操作的版本兼容信息。 LinglongEnvironmentRepairResult _buildOstreeRepairResult({ required ShellCommandResult result, @@ -1056,19 +998,19 @@ exit "\$verify_rc" return LinglongEnvironmentRepairResult( action: action, success: false, - message: '当前 OSTree 版本不支持 --delete,无法自动删除损坏对象,请升级 ostree 或使用发行版工具手动修复。', + message: '当前系统组件不支持自动清理问题对象,无法自动修复玲珑本地数据,请升级系统相关组件或使用发行版工具处理。', logFilePath: logFilePath, output: output, ); } final successMessage = usedLegacyFallback - ? 'OSTree 仓库完整性修复已执行(已兼容旧版 OSTree)' - : 'OSTree 仓库完整性修复已执行'; + ? '玲珑本地数据修复已执行(已兼容旧版系统参数)' + : '玲珑本地数据修复已执行'; final failureMessage = _hasChecksumCorruption(result) - ? 'OSTree 校验发现对象 checksum 不一致,自动删除后仍未完成修复;' - '若重新拉取后仍复现,通常需要上游仓库数据或 OSTree/玲珑兼容性修复。' - : 'OSTree 仓库完整性修复失败'; + ? '玲珑本地数据复验发现对象 checksum 不一致,自动清理后仍未完成修复;' + '若重新拉取后仍复现,通常需要上游仓库数据或 linyaps 本地存储兼容性修复。' + : '玲珑本地数据修复失败'; return LinglongEnvironmentRepairResult( action: action, success: result.success, @@ -1091,7 +1033,7 @@ exit "\$verify_rc" }) { final count = _extractPartialCommitCount(fsckResult); final countText = count == null ? '部分' : '$count 个'; - final legacySuffix = usedLegacyFallback ? '(已兼容旧版 OSTree 参数)' : ''; + final legacySuffix = usedLegacyFallback ? '(已兼容旧版系统参数)' : ''; final output = _truncateOutput(_combinedPrimaryOutput(outputResults)); const action = LinglongEnvironmentRepairAction.ostreeFsckDelete; @@ -1100,7 +1042,7 @@ exit "\$verify_rc" action: action, success: true, message: - 'OSTree 已删除损坏对象,并重新拉取 $countText fsck partial commits,' + '玲珑本地数据已清理问题对象,并重新拉取 $countText partial commits,' '复验通过$legacySuffix。', logFilePath: logFilePath, output: output, @@ -1108,20 +1050,20 @@ exit "\$verify_rc" } final compatibilityHint = _hasChecksumCorruption(repullResult) - ? '复验仍发现 checksum 不一致,可能是上游仓库数据在当前 bare-user-only 模式下与 OSTree 校验不兼容。' + ? '复验仍发现 checksum 不一致,可能是上游仓库数据与 linyaps 本地存储模式不兼容。' : '请查看日志确认具体 ref 的拉取或复验失败原因。'; return LinglongEnvironmentRepairResult( action: action, success: false, message: - 'OSTree 已删除可自动清理的损坏对象,并尝试重新拉取 $countText partial commits,' + '玲珑本地数据已清理可自动处理的问题对象,并尝试重新拉取 $countText partial commits,' '但重新拉取后复验仍未通过。$compatibilityHint$legacySuffix', logFilePath: logFilePath, output: output, ); } - /// 判断 OSTree 输出是否表示当前版本不支持指定参数。 + /// 判断底层工具输出是否表示当前版本不支持指定参数。 /// /// 不同发行版会使用 `unknown option`、`unrecognized option` 或 `invalid option` /// 等不同措辞,因此匹配时同时要求出现参数名,避免误判普通错误。 @@ -1138,7 +1080,7 @@ exit "\$verify_rc" output.contains('unsupported option'); } - /// 判断 `fsck --delete` 是否进入“损坏对象已处理但仍有 partial commits”的新版 OSTree 状态。 + /// 判断自动清理命令是否进入“问题对象已处理但仍有 partial commits”的新版工具状态。 /// /// 这类状态通常退出码非 0,但继续提示“修复失败”会误导用户; /// 正确处理是告知自动删除已执行,并引导重新拉取受影响对象。 @@ -1151,7 +1093,7 @@ exit "\$verify_rc" /// 判断 `fsck --delete` 后是否需要进入 fsck partial 重拉流程。 /// - /// OSTree 在部分版本中会先返回 `Repository corruption encountered`,不会继续走到 + /// 底层工具在部分版本中会先返回 `Repository corruption encountered`,不会继续走到 /// `partial commits from fsck-detected corruption` 分支;但日志中已经写出 /// `Marking commit as partial`,这同样表示后续应按 fsck partial 处理。 bool _needsOstreePartialRepull(ShellCommandResult result) { @@ -1168,14 +1110,14 @@ exit "\$verify_rc" /// 判断输出是否包含对象 checksum 损坏。 /// /// uuu/loong64 环境实测这类错误在全新 bare-user-only repo 中也能复现, - /// 因此文案必须提示上游仓库数据或 OSTree 模式兼容风险,而不是继续建议反复 fsck。 + /// 因此文案必须提示上游仓库数据或 linyaps 本地存储模式兼容风险,而不是继续建议反复修复。 bool _hasChecksumCorruption(ShellCommandResult result) { final output = _combinedCommandOutput(result).toLowerCase(); return output.contains('corrupted file object') || (output.contains('checksum expected') && output.contains('actual=')); } - /// 从 OSTree 输出中提取 partial commit 数量,用于给用户展示具体影响规模。 + /// 从底层工具输出中提取 partial commit 数量,用于给用户展示具体影响规模。 int? _extractPartialCommitCount(ShellCommandResult result) { final match = RegExp( r'(\d+)\s+partial\s+commits?', @@ -1215,7 +1157,7 @@ exit "\$verify_rc" /// 合并一次命令的 stdout/stderr,供兼容判断使用。 /// - /// `ShellCommandResult.primaryMessage` 会优先取 stderr,但 OSTree 的进度、partial 计数和错误原因 + /// `ShellCommandResult.primaryMessage` 会优先取 stderr,但底层工具的进度、partial 计数和错误原因 /// 可能分散在两个流里,因此兼容判断必须读取完整输出。 String _combinedCommandOutput(ShellCommandResult result) { return [ diff --git a/lib/domain/models/linglong_environment_management.dart b/lib/domain/models/linglong_environment_management.dart index 52228f9..b86829b 100644 --- a/lib/domain/models/linglong_environment_management.dart +++ b/lib/domain/models/linglong_environment_management.dart @@ -8,7 +8,7 @@ enum LinglongEnvironmentIssueCode { ostreeRepositoryCorrupted, ostreeToolUnavailable, - /// 玲珑服务用户无法读写本地数据树时使用,避免误归类为 OSTree 对象损坏。 + /// 玲珑服务用户无法读写本地数据树时使用,避免误归类为本地仓库对象损坏。 linglongDataPermissionAbnormal, storageNearlyFull, runningAppsBlockStorageMove, @@ -74,7 +74,7 @@ class LinglongStorageInfo { /// 玲珑数据目录权限检查结果。 /// /// `ll-package-manager` 以 `deepin-linglong` 用户运行,本地数据目录如果被 root 接管, -/// 会导致 `.version` 迁移、OSTree pull 或 layer 生成在运行期失败。 +/// 会导致 `.version` 迁移、对象拉取或 layer 生成在运行期失败。 class LinglongDataPermissionCheckResult { const LinglongDataPermissionCheckResult({ required this.isAvailable, @@ -92,10 +92,11 @@ class LinglongDataPermissionCheckResult { final String? detail; } -/// OSTree 仓库检查结果。 +/// 玲珑本地数据检查结果。 /// -/// `isOk` 表示玲珑运行路径能否读取本地仓库,不等同于深度 `fsck` 完全干净; -/// 深度校验发现对象风险时通过 `hasIntegrityWarning` 单独表达,避免把仍可运行的环境误判为不可用。 +/// 该类型沿用历史命名以减少 Provider 和 UI 改动面,但业务语义已经收敛到 +/// linyaps 运行路径:`isOk` 表示 `ll-cli`/package-manager 能否读取本地数据。 +/// 深度对象审计只属于手动修复日志和高级诊断,不参与默认环境健康结论。 class LinglongOstreeCheckResult { const LinglongOstreeCheckResult({ required this.isAvailable, @@ -104,16 +105,15 @@ class LinglongOstreeCheckResult { this.detail, }); - /// `ostree` 命令是否可用于本地仓库检查。 + /// 是否成功执行 linyaps 本地数据读取检查。 final bool isAvailable; - /// 本地仓库是否能完成玲珑运行所依赖的只读访问。 + /// 本地数据是否能完成玲珑运行所依赖的只读访问。 final bool isOk; - /// 深度对象完整性校验是否发现风险。 + /// 历史字段,保留给手动深度诊断结果表达。 /// - /// 该字段为 `true` 时代表需要提示用户择机修复或重新拉取受影响内容, - /// 但不能直接推导为玲珑基础环境不可用。 + /// 默认环境分析不再设置该字段,避免把底层存储审计结果误判为 linyaps 运行异常。 final bool hasIntegrityWarning; /// 面向诊断展示的命令输出摘要,完整输出仍应以日志文件为准。 diff --git a/lib/presentation/widgets/linglong_environment_management_dialog.dart b/lib/presentation/widgets/linglong_environment_management_dialog.dart index beb815d..4dd42f8 100644 --- a/lib/presentation/widgets/linglong_environment_management_dialog.dart +++ b/lib/presentation/widgets/linglong_environment_management_dialog.dart @@ -142,16 +142,16 @@ class _LinglongEnvironmentManagementDialogState ); } - /// 二次确认后执行 OSTree 修复与必要的受影响 ref 重拉。 + /// 二次确认后执行玲珑本地数据修复与必要的受影响 ref 重拉。 /// - /// `fsck-detected corruption` 不能仅靠删除坏对象判断成功;服务层会在需要时追加 - /// full pull 和复验,因此这里的确认文案必须提示可能产生下载与较长耗时。 + /// 该动作内部可能触发底层对象清理和重新拉取,但用户侧只表达 linyaps + /// 本地数据修复语义,避免把存储实现细节暴露成主健康模型。 Future _confirmAndRepairOstree() async { final confirmed = await _showConfirmDialog( - title: '修复 OSTree 仓库', + title: '修复玲珑本地数据', content: - '将以管理员权限执行 OSTree 完整性修复,删除可清理的损坏对象;' - '如果检测到 fsck 标记的 partial commits,还会重新拉取受影响应用或基础环境并再次校验。是否继续?', + '将以管理员权限尝试修复玲珑本地数据;' + '如果检测到需要重新拉取的应用或基础环境数据,可能产生下载并耗时较长。是否继续?', confirmText: '执行修复', ); if (!confirmed || !mounted) return; @@ -165,7 +165,7 @@ class _LinglongEnvironmentManagementDialogState /// 二次确认后修复玲珑数据目录权限。 /// - /// 权限修复会改变系统数据目录属主并重启 package-manager,所以必须与 OSTree 修复一样 + /// 权限修复会改变系统数据目录属主并重启 package-manager,所以必须与本地数据修复一样 /// 经由用户显式确认,且只通过环境管理 Provider 触发服务层脚本。 Future _confirmAndRepairDataPermissions() async { final confirmed = await _showConfirmDialog( @@ -486,7 +486,7 @@ class _EnvironmentAnalysisTab extends StatelessWidget { const _InlineInfoPanel( icon: Icons.check_circle_outline, title: '未发现需要处理的问题', - message: '玲珑基础环境、仓库与本地 OSTree 仓库当前状态正常。', + message: '玲珑基础环境、仓库与本地数据当前状态正常。', ) else ...issues.map( @@ -709,10 +709,10 @@ class _StatusSummary extends StatelessWidget { color: theme.colorScheme.secondary, ), _MetricChip( - icon: _ostreeMetricIcon(analysis.ostree), - label: 'OSTree', - value: _ostreeMetricValue(analysis.ostree), - color: _ostreeMetricColor(analysis.ostree), + icon: _localDataMetricIcon(analysis.ostree), + label: '本地数据', + value: _localDataMetricValue(analysis.ostree), + color: _localDataMetricColor(analysis.ostree), ), _MetricChip( icon: Icons.storage_outlined, @@ -765,37 +765,34 @@ class _StatusSummary extends StatelessWidget { }; } - /// 根据服务层给出的双通道状态展示 OSTree 指标文案。 + /// 根据服务层给出的 linyaps 本地数据状态展示指标文案。 /// - /// 深度 fsck 风险不等同于玲珑仓库不可用,因此这里要显式展示“可用,有风险”, - /// 让用户知道可以择机修复,而不是误解为基础环境已经整体损坏。 - static String _ostreeMetricValue(LinglongOstreeCheckResult ostree) { + /// 这里刻意不展示底层存储实现细节;默认健康模型只表达 linyaps + /// 能否读取本地数据,不把深度审计结果作为用户侧主状态。 + static String _localDataMetricValue(LinglongOstreeCheckResult ostree) { if (!ostree.isAvailable) { - return '工具不可用'; + return '检测失败'; } if (!ostree.isOk) { return '不可用'; } - if (ostree.hasIntegrityWarning) { - return '可用,有风险'; - } return '正常'; } - /// 为 OSTree 指标选择状态图标,保持与文案语义一致。 - static IconData _ostreeMetricIcon(LinglongOstreeCheckResult ostree) { - if (ostree.isOk && !ostree.hasIntegrityWarning) { + /// 为本地数据指标选择状态图标,保持与文案语义一致。 + static IconData _localDataMetricIcon(LinglongOstreeCheckResult ostree) { + if (ostree.isOk) { return Icons.verified_outlined; } - if (!ostree.isAvailable || ostree.hasIntegrityWarning) { + if (!ostree.isAvailable) { return Icons.report_problem_outlined; } return Icons.error_outline; } - /// 为 OSTree 指标选择状态颜色,区分不可用错误和可用但有风险的警告。 - static Color _ostreeMetricColor(LinglongOstreeCheckResult ostree) { - if (ostree.isOk && !ostree.hasIntegrityWarning) { + /// 为本地数据指标选择状态颜色,区分检测失败和运行路径不可用。 + static Color _localDataMetricColor(LinglongOstreeCheckResult ostree) { + if (ostree.isOk) { return AppColors.success; } if (!ostree.isOk && ostree.isAvailable) { @@ -1298,7 +1295,7 @@ class _BlockingProgressOverlay extends StatelessWidget { /// 玲珑环境管理弹窗顶部的警示横幅。 /// -/// 设计原因:该功能涉及 OSTree 修复、保存位置迁移等高危操作,当前尚不稳定, +/// 设计原因:该功能涉及玲珑本地数据修复、保存位置迁移等高危操作,当前尚不稳定, /// 需在弹窗显眼位置统一提示用户「无问题勿用、遇问题谨慎操作」, /// 避免用户在不知风险的情况下随意触发高危流程。 /// 横幅采用红色强烈警告([AppColors.error]),区别于普通提示,强调高风险性质; diff --git a/test/unit/application/services/linglong_environment_management_service_test.dart b/test/unit/application/services/linglong_environment_management_service_test.dart index f1e948f..0964977 100644 --- a/test/unit/application/services/linglong_environment_management_service_test.dart +++ b/test/unit/application/services/linglong_environment_management_service_test.dart @@ -9,7 +9,7 @@ import 'package:linglong_store/domain/models/linglong_environment_management.dar void main() { group('LinglongEnvironmentManagementService', () { test( - 'analyzeEnvironment reports ostree integrity warning while repository remains usable', + 'analyzeEnvironment follows linyaps health model without running fsck', () async { final runner = _FakeShellCommandRunner.fromCommands({ ..._healthyEnvironmentCommands(), @@ -44,34 +44,32 @@ void main() { expect(analysis.envResult.isOk, isTrue); expect(analysis.storage.usagePercent, 94); expect(analysis.ostree.isOk, isTrue); - expect(analysis.ostree.hasIntegrityWarning, isTrue); + expect(analysis.ostree.hasIntegrityWarning, isFalse); expect( analysis.issues.map((issue) => issue.code), - containsAll([ - LinglongEnvironmentIssueCode.ostreeRepositoryCorrupted, - LinglongEnvironmentIssueCode.storageNearlyFull, - ]), + contains(LinglongEnvironmentIssueCode.storageNearlyFull), ); - final ostreeIssue = analysis.issues.firstWhere( - (issue) => - issue.code == - LinglongEnvironmentIssueCode.ostreeRepositoryCorrupted, + expect( + analysis.issues.map((issue) => issue.title), + isNot(contains('OSTree 对象完整性风险')), ); - expect(ostreeIssue.severity, LinglongEnvironmentIssueSeverity.warning); - expect(ostreeIssue.title, 'OSTree 对象完整性风险'); expect( - ostreeIssue.repairAction, - LinglongEnvironmentRepairAction.ostreeFsckDelete, + runner.commands.map((command) => command.join(' ')), + isNot(contains('ostree fsck --repo=/var/lib/linglong/repo --quiet')), ); - expect(ostreeIssue.rawDetail, contains('Corrupted file object')); }, ); test( - 'analyzeEnvironment reports ostree repository unavailable when refs cannot be read', + 'analyzeEnvironment reports local data unavailable when ll-cli list fails', () async { final runner = _FakeShellCommandRunner.fromCommands({ ..._healthyEnvironmentCommands(), + 'll-cli --json list': const ShellCommandResult( + stdout: '', + stderr: 'error: failed to load repo cache', + exitCode: 1, + ), 'll-cli --json ps': const ShellCommandResult( stdout: '[]', stderr: '', @@ -89,11 +87,6 @@ void main() { stderr: '', exitCode: 0, ), - 'ostree refs --repo=/var/lib/linglong/repo': const ShellCommandResult( - stdout: '', - stderr: 'error: opening repo: No such file or directory', - exitCode: 1, - ), }); final service = _buildManagementService(runner); @@ -102,14 +95,14 @@ void main() { expect(analysis.ostree.isAvailable, isTrue); expect(analysis.ostree.isOk, isFalse); expect(analysis.ostree.hasIntegrityWarning, isFalse); - final ostreeIssue = analysis.issues.firstWhere( + final localDataIssue = analysis.issues.firstWhere( (issue) => issue.code == LinglongEnvironmentIssueCode.ostreeRepositoryCorrupted, ); - expect(ostreeIssue.severity, LinglongEnvironmentIssueSeverity.error); - expect(ostreeIssue.title, 'OSTree 仓库不可用'); - expect(ostreeIssue.rawDetail, contains('opening repo')); + expect(localDataIssue.severity, LinglongEnvironmentIssueSeverity.error); + expect(localDataIssue.title, '玲珑本地数据不可用'); + expect(localDataIssue.rawDetail, contains('failed to load repo cache')); expect( runner.commands.map((command) => command.join(' ')), isNot(contains('ostree fsck --repo=/var/lib/linglong/repo --quiet')), @@ -118,10 +111,13 @@ void main() { ); test( - 'analyzeEnvironment reports ostree tool unavailable when deep check cannot run', + 'analyzeEnvironment reports local data detection failure when ll-cli list cannot run', () async { + final commands = Map.of( + _healthyEnvironmentCommands(), + )..remove('ll-cli --json list'); final runner = _FakeShellCommandRunner.fromCommands({ - ..._healthyEnvironmentCommands(), + ...commands, 'll-cli --json ps': const ShellCommandResult( stdout: '[]', stderr: '', @@ -146,13 +142,54 @@ void main() { expect(analysis.ostree.isAvailable, isFalse); expect(analysis.ostree.isOk, isFalse); - expect(analysis.ostree.hasIntegrityWarning, isFalse); - final ostreeIssue = analysis.issues.firstWhere( + final localDataIssue = analysis.issues.firstWhere( (issue) => - issue.code == LinglongEnvironmentIssueCode.ostreeToolUnavailable, + issue.code == + LinglongEnvironmentIssueCode.ostreeRepositoryCorrupted, + ); + expect(localDataIssue.title, '玲珑本地数据检测失败'); + expect(localDataIssue.rawDetail, contains('ll-cli list 命令执行失败')); + }, + ); + + test( + 'analyzeEnvironment does not require ostree command when linyaps checks pass', + () async { + final runner = _FakeShellCommandRunner.fromCommands({ + ..._healthyEnvironmentCommands(), + 'll-cli --json ps': const ShellCommandResult( + stdout: '[]', + stderr: '', + exitCode: 0, + ), + 'df -PB1 /var/lib/linglong': const ShellCommandResult( + stdout: + 'Filesystem 1-blocks Used Available Capacity Mounted on\n/dev/nvme0n1p5 1000000000 400000000 600000000 40% /var\n', + stderr: '', + exitCode: 0, + ), + 'findmnt --json /var/lib/linglong': const ShellCommandResult( + stdout: + '{"filesystems":[{"target":"/var/lib/linglong","source":"/dev/nvme0n1p5","fstype":"ext4","options":"rw"}]}', + stderr: '', + exitCode: 0, + ), + }); + final service = _buildManagementService(runner); + + final analysis = await service.analyzeEnvironment(); + + expect(analysis.ostree.isAvailable, isTrue); + expect(analysis.ostree.isOk, isTrue); + expect(analysis.ostree.hasIntegrityWarning, isFalse); + expect( + analysis.issues.map((issue) => issue.code), + isNot(contains(LinglongEnvironmentIssueCode.ostreeToolUnavailable)), + ); + expect( + runner.commands.map((command) => command.join(' ')), + isNot(contains('ostree refs --repo=/var/lib/linglong/repo')), ); - expect(ostreeIssue.severity, LinglongEnvironmentIssueSeverity.warning); - expect(ostreeIssue.rawDetail, contains('ostree fsck 命令执行失败')); }, ); @@ -379,7 +416,7 @@ void main() { '--all', '--delete', ], - ['pkexec', 'bash', startsWith('/tmp/linglong-ostree-repull-')], + ['pkexec', 'bash', startsWith('/tmp/linglong-local-data-repull-')], ]); }, ); @@ -445,7 +482,7 @@ void main() { '--all', '--delete', ], - ['pkexec', 'bash', startsWith('/tmp/linglong-ostree-repull-')], + ['pkexec', 'bash', startsWith('/tmp/linglong-local-data-repull-')], ]); }, ); @@ -474,7 +511,7 @@ void main() { ); expect(result.success, isTrue); - expect(result.message, contains('已兼容旧版 OSTree')); + expect(result.message, contains('已兼容旧版系统参数')); expect(runner.commands, [ [ 'pkexec', @@ -515,8 +552,8 @@ void main() { ); expect(result.success, isFalse); - expect(result.message, contains('不支持 --delete')); - expect(result.message, contains('无法自动删除损坏对象')); + expect(result.message, contains('系统组件不支持自动清理')); + expect(result.message, contains('无法自动修复玲珑本地数据')); expect(runner.commands, [ [ 'pkexec', @@ -783,8 +820,8 @@ Map _healthyEnvironmentCommands() { stderr: '', exitCode: 0, ), - 'ostree refs --repo=/var/lib/linglong/repo': ShellCommandResult( - stdout: 'stable:main/org.deepin.base/25.2.2.5/x86_64/binary\n', + 'll-cli --json list': ShellCommandResult( + stdout: '[]', stderr: '', exitCode: 0, ), diff --git a/test/widget/presentation/widgets/linglong_environment_management_dialog_test.dart b/test/widget/presentation/widgets/linglong_environment_management_dialog_test.dart index 121c0cf..c89a34e 100644 --- a/test/widget/presentation/widgets/linglong_environment_management_dialog_test.dart +++ b/test/widget/presentation/widgets/linglong_environment_management_dialog_test.dart @@ -46,9 +46,10 @@ void main() { expect(find.text('玲珑环境管理'), findsOneWidget); expect(find.text('环境分析'), findsOneWidget); - expect(find.text('可用,有风险'), findsOneWidget); - expect(find.text('OSTree 对象完整性风险'), findsOneWidget); - expect(find.text('修复'), findsOneWidget); + expect(find.text('本地数据'), findsOneWidget); + expect(find.text('正常'), findsWidgets); + expect(find.text('OSTree'), findsNothing); + expect(find.text('OSTree 对象完整性风险'), findsNothing); // 警示横幅:提示功能尚不稳定,三个 Tab 共享,位于标题与 TabBar 之间。 // 采用红色强烈警告(AppColors.error),图标为 error_outline。 @@ -221,22 +222,8 @@ LinglongEnvironmentAnalysis _defaultAnalysis() { isAvailable: true, isOk: true, ), - ostree: const LinglongOstreeCheckResult( - isAvailable: true, - isOk: true, - hasIntegrityWarning: true, - detail: 'Corrupted file object found', - ), - issues: const [ - LinglongEnvironmentIssue( - code: LinglongEnvironmentIssueCode.ostreeRepositoryCorrupted, - severity: LinglongEnvironmentIssueSeverity.warning, - title: 'OSTree 对象完整性风险', - description: '深度校验发现对象损坏,但当前玲珑仓库仍可读取。', - repairAction: LinglongEnvironmentRepairAction.ostreeFsckDelete, - rawDetail: 'Corrupted file object found', - ), - ], + ostree: const LinglongOstreeCheckResult(isAvailable: true, isOk: true), + issues: const [], runningAppCount: 0, analyzedAt: DateTime.fromMillisecondsSinceEpoch(1), );