跳转至

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。随后:

  1. 配置新 pacman channel;
  2. 安装 keyrings;
  3. 移除 legacy installer/冲突包;
  4. 安装 omarchy / omarchy-settings(或 dev 版本);
  5. 安装硬件 transition packages;
  6. 规范化 Limine;
  7. 保存 kernel command line 的 root 参数;
  8. 配置 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 正是为了保留这种人工诊断窗口。

关键源码索引

下一章解释这些复杂边界如何被不同层次的测试约束:17 · 测试与开发闭环。