升级 Lumos 站点
升级完成时必须满足两个相互矛盾的目标:
- 用户所写的内容无一丢失。 包括他们的覆盖、页面、令牌和组件。
- 不留任何旧规范。 重命名的属性已重命名,移动的组件已移动,废弃的模式已被淘汰。
仅仅保留用户代码而不做更改会违背第二个目标。直接覆盖以现代化则会违背第一个目标。解决方法是通过三路合并——比较用户开始时的代码、当前代码以及最新发布的代码——并在三方不一致时选择询问而不是猜测。
站点初始状态
升级的效果取决于对起始点的了解。
node -p "JSON.parse(require('fs').readFileSync('package.json')).lumos ?? 'unstamped'"
- 已标记(Stamped) ——
{ version, commit, scaffolded }由create-lumos写入。version是站点来源的发行版本,用于阅读;commit是合并基准,真正起作用的是它。两者并非冗余:在两个发行版之间搭建的站点包含了该版本及其后的内容,只有 commit 能准确说明这一点。在版本信息出现之前搭建的站点仅有 commit,这已经足够。该 commit 即为合并基础。获取它,就能精确区分哪些行是框架的,哪些行是用户的。 - 未标记(Unstamped) —— 站点早于标记机制。将
v0.0.1作为基础版本。在报告中明确说明:基础是猜测的,因此更多合并将产生冲突,需要人工审核。
站点自身的 package.json 版本号没有意义——create-lumos 将所有新站点设置为 0.0.1。不要将其视为框架版本。
步骤
1. 提交当前状态
拒绝在脏工作区上开始升级——如果与昨天的工作混在一起,升级中的差异将难以阅读。
git status --porcelain # 必须为空
git commit --allow-empty -m "Before Lumos upgrade"
空提交是有意为之:它是整个升级的锚点,git diff <该提交> 将成为审查的依据。
2. 截图保存站点
在一切更改之前,启动开发服务器:
astro dev --background
node .agents/skills/lumos-upgrade-version/visual-check.mjs capture before
捕获每个静态路由的桌面和移动端视图。这是步骤 8 将检查的证据,并且无法事后补充。
3. 获取两个参考副本
npm create lumos@latest .lumos-upgrade/latest # 最新版本
以及合并基础——站点最初搭建时的 commit:
curl -sL https://codeload.github.com/lumosframework/lumos-for-astro/tar.gz/<commit> \
| tar -xz -C .lumos-upgrade --one-top-level=base --strip-components=1
现在存在三个目录:base(起始点)、站点目录(当前位置)、latest(目标位置)。
4. 将每个文件分为四类
对于框架覆盖范围内的每个文件(组件、布局、样式、工具):
| base vs site | base vs latest | 含义 | 操作方式 |
|---|---|---|---|
| 相同 | 相同 | 两边均未改动 | 保留 |
| 相同 | 已更改 | 框架已更新,用户从未修改 | 直接替换为最新版本 |
| 已更改 | 相同 | 用户覆盖了框架代码,框架未变 | 保留用户版本 |
| 已更改 | 已更改 | 两边都有改动 | 三路合并,冲突写入报告 |
src/pages、src/content 和 src/assets 属于用户内容——永远不要替换它们。它们仍需在步骤 6 中进行迁移。
.agents/skills 整体升级,但有一个例外。 Skill 的说明文本属于框架,应更新为最新版本,但 skill 可能包含项目写入的特殊部分——例如 /lumos-import-webflow 中的 This project 部分,记录了路径、提供者和 CMS 过滤器,这些信息在其他地方不存在。在获取新文件之前,先逐字保留这些部分,并在报告中说明。直接替换会销毁其他来源无法恢复的记录。
5. 合并 base.css 而非直接替换
全局 CSS 最可能属于第四类:框架添加令牌,用户更改品牌颜色和字体。按令牌逐个进行三路合并,并遵循两条规则:
- 用户更改过的令牌保留用户的值,即使框架更改了默认值。用户的品牌颜色并非过时的默认值。
- 框架新增的令牌使用框架的值,并放置在其自己的区域中——而不是追加到末尾。
双方都更改过的每个令牌,在报告中同时列出两个值。不要替用户做选择。
6. 迁移站点自己的代码
比较 base 和 latest 以了解哪些内容发生了变化,然后将其应用到 src/pages 和用户编写的所有代码:
- 已重命名或移动的组件 —— 更新所有导入和标签。一个组件如果移动了文件夹并失去了前缀(例如
FormInput→Form/Input.astro),则同时涉及两项更改;两项都要处理。 - 已重命名的属性 —— 在每个调用处重新命名。
- 已移除的属性 —— 停下来询问。 对每个属性,提供三种真实选项:将其保留为本地覆盖、删除它、或迁移到替代它的属性。永远不要悄悄做出决定;被移除的属性通常有特定含义。
- 已删除的组件 —— 保留用户的副本。 将
base中的组件复制到站点自己的组件目录下,确保站点仍能构建并保持原样,并标记它:该组件不再由框架维护,并提供替代方案(如有)。
7. 验证构建成功
npx astro check && npx astro build
两者都必须通过才能进行截图对比。此时发现的类型错误意味着迁移遗漏。
借此机会检查 Astro 版本兼容性。 Astro 是站点自身的依赖项,而非框架的——Lumos 只是被搭建进去,并非作为依赖安装——因此升级是检查的合理时机,但不是强制升级的理由。将站点使用的 astro 版本与正在合并的发行版中的版本进行比较;如果站点在主要版本上落后了,在报告中说明,让站点所有者决定。
使用 npx @astrojs/upgrade 而不是手动升级。它会同时更新 @astrojs/* 集成,而 @astrojs/sitemap 未声明对 astro 的对等依赖——因此版本不匹配时可能会静默安装,表现为构建失败或站点地图静默停止生成。
8. 验证设计未变化
node .agents/skills/lumos-upgrade-version/visual-check.mjs capture after
node .agents/skills/lumos-upgrade-version/visual-check.mjs compare
将每个路由与步骤 2 的图像进行像素对比(抗锯齿误差容限),并对超过 0.1% 像素差异的页面生成高亮差异图。当有页面发生变化时,退出码非零。
升级如果改变了设计,说明某个地方出了问题——可能是令牌合并错误、属性被丢弃、或组件被替换为不完全相同的版本。从差异图像逆向排查,直到每个差异都有合理解释。有些差异是合理的:上游的错误修复确实会移动像素。规则不是“无差异”,而是 “无不合理的差异”。
9. 提交结果
git add -A && git commit -m "Upgrade Lumos to <commit>"
更新 package.json 中的标记,将其设为刚刚合并的 commit,以便下次升级有准确的基础。然后删除 .lumos-upgrade/ 目录。
报告
- 从最新版替换 —— 因未改动而整体替换的文件。
- 保留为你的 —— 保留的用户覆盖,拒绝框架更改。
- 已合并 —— 双方都有改动的文件,说明来源。
- 已迁移 —— 对用户代码应用的重命名和移动操作,已计数。
- 保持存活 —— 上游已删除但被复制到站点中的组件,附上替代组件说明(如有)。
- 已解答 —— 移除的属性及用户如何处理的记录。
- 视觉差异 —— 每个发生变化的视图及其原因。
- 仍待解决 —— 任何未解决的问题。升级可以在列表非空的情况下完成,但必须明确说明。
版本
- Skill 版本 —— 1.0.0。
- 框架 —— 目前还没有正式的发行版:
create-lumos从main分支的最新提交搭建,因此“最新”意味着最新提交,合并基准是 commit 而非标签。如果将来有带标签的发行版,则优先使用标记中的标签,并在步骤 4 中读取变更日志,而不是盲目比对。
在没有 Claude Code 的情况下使用
步骤仅依赖 git 和 node。visual-check.mjs 需要 headless Chrome 和 sharp,这两者在 Mac 上的 Astro 项目中已经具备。只有自动触发和 /lumos-upgrade 功能是 Claude Code 特有的特性。