16 · Quattro 一次性升级器¶
omarchy-upgrade-to-quattro 有约 2,370 行,是仓库中最集中的迁移程序。它要把旧版 git-checkout 布局转成 package-backed 布局,同时让当前图形会话活到 reboot cutover。
为什么它必须自包含¶
旧机器可能运行 3.8.x,也可能通过 curl | bash 直接取得升级器,不能假设新包中的 helper 已存在。因此脚本在 package install 之前自带:
- 参数/渠道解析;
- root、user 与 user-session 执行 wrapper;
- pacman/keyring/boot 配置逻辑;
- 旧配置 hash 表与迁移策略;
- cleanup trap 与失败提示。
安装新包以后才逐渐切换到新 omarchy 命令。自包含牺牲了文件短小,换来可从多个旧版本启动的 bootstrap 能力。
总状态机¶
stateDiagram-v2
[*] --> Preflight
Preflight --> Snapshot
Snapshot --> PackageTransition
PackageTransition --> LiveOverlay
LiveOverlay --> SystemTransition
SystemTransition --> UserTransition
UserTransition --> Migrations
Migrations --> Verification
Verification --> AwaitReboot
AwaitReboot --> Quattro: reboot cutover
PackageTransition --> Incomplete: 任意硬失败
SystemTransition --> Incomplete
UserTransition --> Incomplete
Incomplete --> Resume: 修复后重跑
脚本设置 upgrade_started / upgrade_completed,从第一项真实 mutation 开始,任何非零退出都告知机器升级不完整并且不要 reboot。目标是让失败停在可诊断的 live session,而不是自动跨入可能无法启动的新布局。
1. Preflight:先确认身份与渠道¶
脚本正规化 stable/main/master、rc、edge/dev,默认保持机器现有渠道语义。Quattro 尚未正式发布时,rc/edge 使用 dev packages;--dev 要求可提供开发包的 edge repo。
它还确认:
- 系统有 pacman;
- 非 root 调用时有 sudo;
- target user 存在且不是 root;
- home、UID 与 runtime dir 可解析;
- 参数组合没有把 dev package 指到 stable repo。
root 与用户上下文由 as_root、run_as_user、run_as_user_session 区分;需要 Hyprland/DBus 的动作会从目标进程//run/user/<uid> 恢复 Wayland session 环境。
2. package 与 boot 转换¶
mutation 开始前暂停 live Hyprland reload 并创建 pre-upgrade snapshot。随后:
- 配置新 pacman channel;
- 安装 keyrings;
- 移除 legacy installer/冲突包;
- 安装
omarchy/omarchy-settings(或 dev 版本); - 安装硬件 transition packages;
- 规范化 Limine;
- 保存 kernel command line 的 root 参数;
- 配置 Snapper 与 lock authentication。
boot cmdline 的 root= 无法确认时设置 boot_cmdline_unsafe。升级可以继续做可修复转换,但最后明确跳过 reboot,防止把机器送进 emergency shell。
3. live overlay:给旧会话一座桥¶
package 已切到新布局时,当前 Hyprland 会话仍读取旧路径和旧配置。升级器创建 live overlay/shim,让旧 UI 在 reboot 前继续找到必要文件;同时暂停 config reload,避免目录交换窗口中出现 source= glob 错误。
flowchart LR
Old[旧会话/旧路径] --> Shim[legacy shims + live overlay]
New[/usr/share/omarchy + 新配置] --> Shim
Shim --> Running[当前 Hyprland 继续运行]
Running --> Reboot[reboot]
Reboot --> Native[原生 Quattro Lua/session]
脚本结尾也不强制 reload Hyprland;reboot 才是真正切换点。这是一次典型的“兼容桥先行、下一生命周期切换”。
4. system transition¶
系统侧转换应用新的 service、firewall、登录、环境和已退休服务清理。旧 systemd-networkd、过时 user units 或 package 不能只留着不管,否则新旧 manager 会同时生效。
升级器还确保 sleep-lock service 已存在,使当前会话到 reboot 之间仍有安全锁定路径。
5. user transition:hash-aware 配置迁移¶
最棘手的问题是:一个旧配置文件究竟是未修改默认值,还是用户定制?升级器内置已知版本默认文件的 SHA-256 集合,并分类处理:
| 状态 | 动作 |
|---|---|
| 文件缺失 | 复制新 default |
| hash 是已知 shipped default | 刷新为新 default |
| hash 未知 | 视为用户定制,先备份/保留 |
| 已退休且仍是默认 | 移除 |
| 已退休但被定制 | 不静默丢弃,保留备份/提示 |
| 必须每次重建的少数配置 | 走 explicit always-copy/repair |
flowchart TD
File[旧配置] --> Exists{存在?}
Exists -->|否| New[复制新默认]
Exists -->|是| Hash{SHA 属于已知默认?}
Hash -->|是| Retired{已退休?}
Retired -->|是| Remove[安全移除]
Retired -->|否| Refresh[刷新默认]
Hash -->|否| Preserve[备份并保留定制]
这比“旧内容等于当前仓库默认吗”更可靠,因为机器可能从多个历史版本升级。hash 表就是已发布默认值的兼容数据库。
6. 状态与主题转换¶
升级器迁移 legacy current path、user units、Hyprland 配置、应用 launcher 与 bar defaults;运行 post-upgrade migrations 和最终 system update。
主题刷新使用 OMARCHY_THEME_HEADLESS=1 生成 Quattro 模板,避免 live post hook 执行 hyprctl reload。它仍尝试重启当前终端以吸收新配色;失败仅警告,因为不影响 boot correctness。
7. reboot 是显式 cutover¶
全部步骤完成后,脚本恢复 reload guard,打印必须处理错误的警告,再根据:
- boot cmdline 是否安全;
--reboot是否传入;- 用户是否确认;
决定是否 reboot。只有 reboot 后,新 UWSM/Hyprland Lua/package 布局成为完整运行环境。
为什么支持重跑¶
大部分动作先探测或按目标状态收敛,config migration 有 hash/backup,package install 可幂等,迁移有 marker。升级器无法把数千个副作用变成单一事务,因此把“失败后安全重跑”作为核心恢复策略。
但重跑不代表盲目执行:先阅读第一个硬错误,确认 pacman/boot/磁盘状态,再运行同一入口。脚本的 incomplete trap 正是为了保留这种人工诊断窗口。
关键源码索引¶
bin/omarchy-upgrade-to-quattro参数与渠道- root/user/session wrapper
- pacman/keyring/boot 迁移
- 安装 Quattro 与硬件包
- live overlay 与 retired services
- Hyprland reload suppression 与 migrations
- system/user transition
- 已知默认 hash 与配置迁移
- 最终调用顺序与 reboot gate
test/shell.d/upgrade-to-quattro-test.sh— 结构与关键安全契约
下一章解释这些复杂边界如何被不同层次的测试约束:17 · 测试与开发闭环。