Skip to content

feat(linux): 新增可选构建 feature 数据目录遵循 XDG 规范 - #408

Open
mx6436 wants to merge 3 commits into
MaaXYZ:mainfrom
mx6436:feat/linux-xdg-data-dir
Open

mx6436 wants to merge 3 commits into
MaaXYZ:mainfrom
mx6436:feat/linux-xdg-data-dir

Conversation

@mx6436

@mx6436 mx6436 commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

默认关,需要包管理器的自行开启构建即可

Sourcery 摘要

新增可选的 Linux XDG 数据目录模式,同时保留默认的便携式存储方式。

新功能:

  • 新增可选的 Linux xdg-data-dir Cargo 功能,使用 interface.json 中的项目名称,将应用程序数据存储在 XDG 数据目录下。
  • 禁用此功能时,继续使用现有的可执行文件所在目录存储数据。

增强:

  • 验证 Linux 项目名称和环境路径,并在适当情况下回退到 MXU 和 ~/.local/share。
  • 更新前端路径处理方式和文档,说明 Linux 数据目录行为会因功能启用状态而异。

构建:

  • 新增 xdg-data-dir Cargo 功能,并提供启用该功能时的构建和测试指南。

文档:

  • 说明 Rust 功能、Linux 数据目录规则、兼容性影响和构建命令。

测试:

  • 新增 Linux 专属测试,覆盖 XDG 路径、默认值、JSONC 元数据、回退名称、无效名称和无效环境变量值。

杂项:

  • 更新内部路径注释和开发者指南,说明新的可选构建行为。
Original summary in English

Sourcery 摘要

为 Linux 引入可选的 XDG 数据目录支持,同时保持默认构建使用便携式数据存储方式。

新功能:

  • 为 Linux 新增默认关闭的 xdg-data-dir Cargo 功能,使应用数据可以按项目名称存储在 XDG 数据目录中,同时保留现有的便携式存储方式。

错误修复:

  • 增强 JSONC 解析功能以支持尾随逗号,确保能够正确读取项目元数据。

改进:

  • 增加对 Linux 项目名称、XDG_DATA_HOME 和 HOME 的校验及安全回退逻辑,并同步更新前端的数据路径说明与处理方式。

构建:

  • 新增 xdg-data-dir Cargo 功能及相应的构建和测试文档。

文档:

  • 补充 Rust 功能、Linux 数据目录规则、兼容性影响及构建使用说明。

测试:

  • 新增针对 JSONC 解析和 Linux XDG 数据目录行为的覆盖测试。

杂项:

  • 更新开发者指南及相关路径注释,说明可选构建行为。
Original summary in English

Sourcery 摘要

新增可选的 Linux XDG 数据目录支持,同时保留默认的便携式存储行为。

新功能:

  • 新增可选的 Linux xdg-data-dir Cargo 功能,可将应用数据存储在根据 interface.json 命名的 XDG 数据目录下,同时默认仍使用便携式的可执行文件目录存储方式。

错误修复:

  • 当应用数据目录与安装目录不同时,确保更新包归档和配置备份仍能正常工作。

改进:

  • 为 Linux 项目名称和 XDG 环境路径添加验证及安全回退机制。
  • 更新前端路径处理逻辑,使配置备份、下载内容和更新产物都使用解析后的应用数据目录。

构建:

  • 新增 xdg-data-dir Cargo 功能,并记录特定功能的构建和测试命令。

文档:

  • 记录 Linux 数据目录行为、兼容性注意事项、功能用法及回退规则。

测试:

  • 添加针对 XDG 路径、JSONC 元数据、默认值、无效项目名称及无效环境变量值的 Linux 专项测试覆盖。

杂项:

  • 更新开发者指南和路径注释,说明可选数据目录行为。
Original summary in English

Sourcery 摘要

在 Linux 上启用可选的、符合 XDG 规范的应用程序数据存储,同时仍默认使用便携式数据存储。

新功能:

  • 添加可选择启用的 Linux xdg-data-dir Cargo 功能,将应用程序数据存储在符合 XDG 规范且按项目区分的目录中,同时默认保留便携式存储方式。

错误修复:

  • 即使应用程序数据目录与安装目录不同,也能确保更新包、配置备份及相关更新文件正常工作。
  • 读取 JSONC 格式的项目元数据时,支持尾随逗号和注释。

改进:

  • 验证项目名称和环境路径,并在适当情况下安全回退到 MXU 和 ~/.local/share。
  • 更新前端路径处理逻辑,确保配置、下载、备份和更新文件统一使用解析后的应用程序数据目录。

构建:

  • 添加 xdg-data-dir Cargo 功能,并记录特定功能对应的构建和测试命令。

文档:

  • 说明 Linux 数据目录行为、兼容性注意事项、回退规则以及可选功能的用法。

测试:

  • 添加针对 Linux 的测试,覆盖 XDG 路径、默认值、JSONC 元数据、回退名称、无效项目名称和无效环境变量值。

杂项:

  • 更新开发者指南和路径注释,说明可选的数据目录行为。
Original summary in English

Summary by Sourcery

Enable optional XDG-compliant application data storage on Linux while retaining portable data storage as the default.

New Features:

  • Add an opt-in Linux xdg-data-dir Cargo feature that stores application data under an XDG-compliant, project-specific directory while preserving portable storage by default.

Bug Fixes:

  • Keep update packages, configuration backups, and related update artifacts working correctly when the application data directory differs from the installation directory.
  • Support trailing commas and comments when reading project metadata from JSONC.

Enhancements:

  • Validate project names and environment paths, with safe fallbacks to MXU and ~/.local/share where appropriate.
  • Update frontend path handling so configuration, downloads, backups, and update artifacts consistently use the resolved application data directory.

Build:

  • Add the xdg-data-dir Cargo feature and document feature-specific build and test commands.

Documentation:

  • Document Linux data-directory behavior, compatibility considerations, fallback rules, and optional feature usage.

Tests:

  • Add Linux-specific coverage for XDG paths, defaults, JSONC metadata, fallback names, invalid project names, and invalid environment values.

Chores:

  • Update developer guidance and path comments to describe the optional data-directory behavior.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 29, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-29T13:45:51.163623Z 0ab4ed3 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

嗨——我发现了 6 个问题

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="src-tauri/src/commands/utils.rs" line_range="246" />
<code_context>
+            .join(".local")
+            .join("share"),
+    };
+    Ok(base.join(project_name))
+}
+
</code_context>
<issue_to_address>
**Config saves are denied**

When linux is built with `xdg-data-dir` and the frontend attempts to save configuration, `get_app_data_dir` returns an XDG path, but the Tauri frontend’s `saveConfig` writes through the filesystem plugin and the configured recursive write permission is for app data. The plugin rejects writes to this path, so users cannot save configuration when this feature is enabled.

Add filesystem read/write scope for the resolved XDG directory, or perform configuration reads and writes through Rust commands that are not subject to the frontend filesystem scope.
</issue_to_address>

### Comment 2
<location path="src-tauri/src/commands/utils.rs" line_range="246" />
<code_context>
+            .join(".local")
+            .join("share"),
+    };
+    Ok(base.join(project_name))
+}
+
</code_context>
<issue_to_address>
**Existing settings are left behind**

When an existing Linux installation with configuration under the executable directory is launched with `xdg-data-dir` enabled, `AppConfigState::load_config` looks only under the newly selected XDG directory, so it ignores an existing installation’s `config/` under the executable directory and loads defaults. Saving then creates a separate XDG copy; users see their settings disappear, and switching back to a non-feature build exposes the stale copy.

On first feature-enabled launch, migrate the existing configuration and other persistent state into the XDG directory, or load the old configuration as a fallback and document the resulting migration behavior.
</issue_to_address>

### Comment 3
<location path="src-tauri/src/commands/utils.rs" line_range="247" />
<code_context>
         Ok(path)
     }

-    #[cfg(not(target_os = "macos"))]
</code_context>
<issue_to_address>
**Same-name projects share data**

When two distinct installations have the same `interface.json` name, `base.join(project_name)` resolves both projects to the same XDG directory, so their configs, logs, and caches overlap; saving settings in one project changes the data used by the other.

Use a stable unique project identifier for the XDG directory, or enforce and document uniqueness of `name` across projects.
</issue_to_address>

### Comment 4
<location path="src-tauri/src/commands/utils.rs" line_range="247" />
<code_context>
         Ok(path)
     }

-    #[cfg(not(target_os = "macos"))]
</code_context>
<issue_to_address>
**Concurrent saves overwrite shared config**

When two instances of the same project, or concurrent save requests, write configuration at the same time, separate instances of the same project now resolve to the same XDG directory, and concurrent config saves use the same `.tmp` path without cross-process coordination. One save can overwrite or remove the other’s temporary file, or a stale full-config save can replace newer changes; callers then see a failed save or lost configuration updates.

Serialize writes across processes (for example with a per-config lock) and use unique temporary filenames; merge or reject stale config versions rather than silently replacing newer state.
</issue_to_address>

### Comment 5
<location path="src-tauri/src/commands/utils.rs" line_range="247" />
<code_context>
         Ok(path)
     }

-    #[cfg(not(target_os = "macos"))]
</code_context>
<issue_to_address>
**Update backups survive cleanup**

When linux is built with `xdg-data-dir` and an update leaves backup files in `exe_dir/cache/old`, `move_to_old_folder` writes backups under `exe_dir/cache/old`, but startup cleanup checks the XDG cache directory, so the backups remain. When those directories are on different filesystems, the move also fails and leaves the package in the XDG cache.

Make `move_to_old_folder` and startup cleanup use the same backup directory.
</issue_to_address>

### Comment 6
<location path="src-tauri/src/commands/utils.rs" line_range="193" />
<code_context>
+            Err(e) if e.kind() == std::io::ErrorKind::NotFound => None,
+            Err(e) => return Err(format!("读取 interface.json 失败: {}", e)),
+        };
+        let xdg_data_home = std::env::var_os("XDG_DATA_HOME").map(PathBuf::from);
+        let home = std::env::var_os("HOME").map(PathBuf::from);
+        linux_data_dir(
</code_context>
<issue_to_address>
**Non-UTF-8 paths target wrong directory**

When `XDG_DATA_HOME` or the fallback `HOME` contains non-UTF-8 bytes, `get_data_dir` and `AppConfigState::load_config` convert the path to a lossy string, so later frontend and backend operations use a different path and configuration or other data operations fail or target the wrong directory.

Preserve the original path without lossy string conversion for filesystem operations.
</issue_to_address>

Sourcery 对开源项目免费——如果你喜欢我们的评审,请考虑分享 ✨
Original comment in English

Hey - I've found 6 issues

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="src-tauri/src/commands/utils.rs" line_range="246" />
<code_context>
+            .join(".local")
+            .join("share"),
+    };
+    Ok(base.join(project_name))
+}
+
</code_context>
<issue_to_address>
**Config saves are denied**

When linux is built with `xdg-data-dir` and the frontend attempts to save configuration, `get_app_data_dir` returns an XDG path, but the Tauri frontend’s `saveConfig` writes through the filesystem plugin and the configured recursive write permission is for app data. The plugin rejects writes to this path, so users cannot save configuration when this feature is enabled.

Add filesystem read/write scope for the resolved XDG directory, or perform configuration reads and writes through Rust commands that are not subject to the frontend filesystem scope.
</issue_to_address>

### Comment 2
<location path="src-tauri/src/commands/utils.rs" line_range="246" />
<code_context>
+            .join(".local")
+            .join("share"),
+    };
+    Ok(base.join(project_name))
+}
+
</code_context>
<issue_to_address>
**Existing settings are left behind**

When an existing Linux installation with configuration under the executable directory is launched with `xdg-data-dir` enabled, `AppConfigState::load_config` looks only under the newly selected XDG directory, so it ignores an existing installation’s `config/` under the executable directory and loads defaults. Saving then creates a separate XDG copy; users see their settings disappear, and switching back to a non-feature build exposes the stale copy.

On first feature-enabled launch, migrate the existing configuration and other persistent state into the XDG directory, or load the old configuration as a fallback and document the resulting migration behavior.
</issue_to_address>

### Comment 3
<location path="src-tauri/src/commands/utils.rs" line_range="247" />
<code_context>
         Ok(path)
     }

-    #[cfg(not(target_os = "macos"))]
</code_context>
<issue_to_address>
**Same-name projects share data**

When two distinct installations have the same `interface.json` name, `base.join(project_name)` resolves both projects to the same XDG directory, so their configs, logs, and caches overlap; saving settings in one project changes the data used by the other.

Use a stable unique project identifier for the XDG directory, or enforce and document uniqueness of `name` across projects.
</issue_to_address>

### Comment 4
<location path="src-tauri/src/commands/utils.rs" line_range="247" />
<code_context>
         Ok(path)
     }

-    #[cfg(not(target_os = "macos"))]
</code_context>
<issue_to_address>
**Concurrent saves overwrite shared config**

When two instances of the same project, or concurrent save requests, write configuration at the same time, separate instances of the same project now resolve to the same XDG directory, and concurrent config saves use the same `.tmp` path without cross-process coordination. One save can overwrite or remove the other’s temporary file, or a stale full-config save can replace newer changes; callers then see a failed save or lost configuration updates.

Serialize writes across processes (for example with a per-config lock) and use unique temporary filenames; merge or reject stale config versions rather than silently replacing newer state.
</issue_to_address>

### Comment 5
<location path="src-tauri/src/commands/utils.rs" line_range="247" />
<code_context>
         Ok(path)
     }

-    #[cfg(not(target_os = "macos"))]
</code_context>
<issue_to_address>
**Update backups survive cleanup**

When linux is built with `xdg-data-dir` and an update leaves backup files in `exe_dir/cache/old`, `move_to_old_folder` writes backups under `exe_dir/cache/old`, but startup cleanup checks the XDG cache directory, so the backups remain. When those directories are on different filesystems, the move also fails and leaves the package in the XDG cache.

Make `move_to_old_folder` and startup cleanup use the same backup directory.
</issue_to_address>

### Comment 6
<location path="src-tauri/src/commands/utils.rs" line_range="193" />
<code_context>
+            Err(e) if e.kind() == std::io::ErrorKind::NotFound => None,
+            Err(e) => return Err(format!("读取 interface.json 失败: {}", e)),
+        };
+        let xdg_data_home = std::env::var_os("XDG_DATA_HOME").map(PathBuf::from);
+        let home = std::env::var_os("HOME").map(PathBuf::from);
+        linux_data_dir(
</code_context>
<issue_to_address>
**Non-UTF-8 paths target wrong directory**

When `XDG_DATA_HOME` or the fallback `HOME` contains non-UTF-8 bytes, `get_data_dir` and `AppConfigState::load_config` convert the path to a lossy string, so later frontend and backend operations use a different path and configuration or other data operations fail or target the wrong directory.

Preserve the original path without lossy string conversion for filesystem operations.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨

Comment thread src-tauri/src/commands/utils.rs Resolved
Comment thread src-tauri/src/commands/utils.rs Resolved
Comment thread src-tauri/src/commands/utils.rs Resolved
Comment thread src-tauri/src/commands/utils.rs Resolved
Comment thread src-tauri/src/commands/utils.rs Resolved
Comment thread src-tauri/src/commands/utils.rs Resolved

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0ab4ed363a

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src-tauri/src/commands/utils.rs Resolved
Comment thread src-tauri/src/commands/utils.rs Resolved
* @param dataPath 数据目录(macOS: ~/Library/Application
* Support/MXU/,其他平台: exe 目录)
* 使用应用数据目录(macOS: ~/Library/Application Support/MXU/,
* Linux 启用 xdg-data-dir feature: $XDG_DATA_HOME/{项目名},否则为 exe 目录)。

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Keep update archive cleanup on the XDG filesystem

When this feature is enabled and the XDG data home and executable reside on different mounts, update archives live in the XDG cache, but completion calls move_to_old_folder, which hard-codes <exe>/cache/old and uses std::fs::rename (src-tauri/src/commands/update.rs:156-199). The resulting EXDEV is swallowed by moveToOldFolder, and cleanup only removes .downloading files, so archives remain and versioned ones can accumulate; even on one filesystem, startup now cleans XDG cache/old rather than the executable's cache/old. Keep archival and cleanup on the data filesystem or handle copy-and-remove.

AGENTS.md reference: AGENTS.md:L45-L45

Useful? React with 👍 / 👎.

@MistEO MistEO left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

基于最新提交 ff866b8,复核后确认两项问题,均涉及 Linux 启用 xdg-data-dir 后的更新流程:

  1. [P2] 更新前配置备份仍读取 exe 目录 — updateService.ts:995

    InstallConfirmModal 将 basePath 作为安装目标 targetDir 传入,而 installUpdate 又将它传给 backupConfigBeforeUpdate。启用 feature 后,实际配置位于 XDG 数据目录,但备份仍检查 <exe>/config/mxu-{项目名}.json:全新 XDG 部署会静默跳过更新前备份;如果 exe 目录留有旧配置,还会备份错误版本。已用现有备份函数复现,并确认改传 dataPath 后能够读取当前配置并写入备份。建议为安装流程单独传入 dataPath 用于配置备份,安装目标仍使用 basePath。

  2. [P2] 更新归档与启动清理目录不一致,跨分区归档会失败 — update.rs:156–199、lib.rs:169–174

    下载包现在位于 XDG 数据目录的 cache 下,但 move_to_old_folder 仍把文件归档到 <exe>/cache/old,启动时却只清理 <dataPath>/cache/old。同一文件系统上,归档文件不会在下次启动时清理;当 XDG 数据目录与 exe 位于不同文件系统时,std::fs::rename 返回 EXDEV,该错误被上层忽略,下载包留在缓存中,带版本号的包可能逐次积累。已用现有移动函数复现这两种情况。建议分别协调下载包归档、安装目录旧文件备份与启动清理的路径,并为跨文件系统移动提供回退。

验证:启用 xdg-data-dir 的 Rust 42 项单元测试、前端 12 项测试及 TypeScript 检查全部通过;上述两项另做了针对性复现。

@mx6436
mx6436 force-pushed the feat/linux-xdg-data-dir branch from ff866b8 to eb5bc45 Compare November 5, 2026 11:29
@mx6436

mx6436 commented Nov 5, 2026

Copy link
Copy Markdown
Contributor Author

配置备份与更新归档的目录归属问题是原本就存在的,这里选择全部改用数据目录

@MistEO

MistEO commented Nov 6, 2026

Copy link
Copy Markdown
Member

冲突了看看

mx6436 and others added 3 commits November 6, 2026 19:08
Co-Authored-By: GPT-6.1 Sol <noreply@pi.dev>
Generated-By: pi 0.87.1 (https://pi.dev)
Linux 数据目录改动以 `xdg-data-dir` feature 提供,默认构建仍保持
exe 目录的便携式布局,避免升级后配置/日志/缓存路径变更。

- Cargo.toml 声明 xdg-data-dir(非默认)
- get_app_data_dir 按 all(target_os = "linux", feature = "xdg-data-dir") 分派
- linux_data_dir 及其 6 个单测随 feature 条件编译
- 文档见 docs/rust-features.md
更新前备份从 exe 目录读取配置,开启 xdg-data-dir 后会跳过备份或备份
错误版本;改为使用 dataPath。更新包归档也移到 dataPath/cache/old,
与下载目录同分区,避免跨文件系统 rename 失败后残留在缓存中。

启动清理同时处理 exe 与 data 的 cache/old;程序旧文件与回滚备份仍
保留在 exe 目录。

验证:cargo test --features xdg-data-dir(79 项)、cargo check、
tsc、Prettier 与前端 92 项测试通过。
@mx6436
mx6436 force-pushed the feat/linux-xdg-data-dir branch from eb5bc45 to 9e06ae6 Compare November 6, 2026 11:13
@mx6436

mx6436 commented Nov 6, 2026

Copy link
Copy Markdown
Contributor Author

已 rebase

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants