- 日期:2026-09-24
- 站点:https://high33light.cn(控制台 /console)
- 仓库:github.com/gaogaohang/halo-gao(fork 自 halo-dev/halo)
- 涉及版本:Halo 2.23.0 → 2.26.1;应用市场插件 1.12.0 → 1.19.0
本文按时间顺序记录当天处理的四个问题:CI 构建失败、应用市场插件不可用、Halo 内核升级、Markdown 编辑/渲染体验修复。每个问题给出证据、根因和处置,最后附可复用的操作要点。
一、CI 构建失败(fork 合并上游后)
现象
将 halo-dev/halo 最新 main 合并推送到 fork 后,GitHub Actions 的 "Halo Workflow → test → Check Halo" 步骤失败,执行命令为 ./gradlew clean check。
排查过程
- 通过 GitHub API 拉取 run/job 信息,确认失败步骤是
Check Halo,且只运行了 111 秒(完整 check 正常需要 15~30 分钟),判断是早期失败而非测试用例失败。 - 对比上游:halo-dev/halo 同期全部构建通过;fork 的代码树与上游
a88cf4194的差异仅两个文件(README.md 和 ROLLBACK_NOTICE.md),排除业务代码问题。 - 拿到 CI 日志末尾后定位到具体报错:
Execution failed for task ':spotlessFlexmarkCheck'
> The following files had format violations:
ROLLBACK_NOTICE.md
...
+
Run './gradlew spotlessApply' to fix all violations.
根因
仓库根 build.gradle 中的 Spotless(flexmark)规则会检查所有 Markdown 文件,要求文件结尾保留一个空行。ROLLBACK_NOTICE.md 是 2026-03-25 手动添加的文件,提交时没有结尾空行。此前该 fork 的推送只触发过 Stale Issues 定时任务,从未跑过完整 check,所以问题一直潜伏,直到这次合并触发。
处置
在 ROLLBACK_NOTICE.md 末尾补一个空行,提交推送(commit e6e50edd0)。修复后需要等 Actions 触发(本次推送 webhook 丢失未触发,需手动关注或补推空提交)。
二、应用市场插件不可用("没法在线装插件/主题")
现象
控制台里插件/主题的"远程下载"只能手动粘贴 URL,没有在线浏览应用市场的界面。此前该功能是正常可用的。
关键认知
控制台内的"应用市场"界面本身是一个插件(app-store-integration,官方闭源,仅通过 Halo 商店分发,商店编号 app-VYJbF)。官方发行版将其作为预置插件打进发行包、启动时自动安装启用(见上游 application/build.gradle 的 downloadPluginPresets 任务与 BuiltInPluginsInitializer)。插件缺失或被删除时,控制台就只剩下核心的"粘贴 URL"安装方式。
排查过程
- 通过控制台 API(Bearer 个人令牌)列出已装插件,发现
app-store-integration已安装(v1.12.0)但状态为FAILED。 - 读取插件 status 中的 Ready condition,报错核心为:
java.lang.NoSuchMethodError:
'org.springframework.web.util.UriComponentsBuilder
org.springframework.web.util.UriComponentsBuilder.fromHttpUrl(java.lang.String)'
at run.halo.appstore.license.LicenseServiceImpl.<init>
即插件编译时依赖的 Spring 方法 UriComponentsBuilder.fromHttpUrl(String) 在当前内核的 Spring 版本中已不存在(该方法在 Spring 6.1 弃用、后续版本移除),插件启动即崩溃。
- 尝试升级官方最新预设版本 1.18.1 时,API 返回:
插件要求一个最小的系统版本为 >=2.25.0, 但当前版本为 2.23.0。
由此确认线上内核实际为 2.23.0(部署于 2026-03-25,此后未再更新),与预期"已是新版"不符。
- 通过商店 API(
https://www.halo.run/apis/api.store.halo.run/v1alpha1/applications/app-VYJbF/releases)拉取全部 release 列表,下载 1.13.1~1.17.0 五个候选 jar,逐一检查plugin.yaml的requires字段和 class 字节码中是否仍引用fromHttpUrl:
| 版本 | 要求 Halo 版本 | 是否已移除 fromHttpUrl 引用 |
|---|---|---|
| 1.13.1 / 1.14.0 | >=2.22.0 | 否(仍会崩) |
| 1.15.0 / 1.16.0 | >=2.22.0 | 是 |
| 1.17.0 | >=2.24.0 | 是 |
| 1.18.x 及以上 | >=2.25.0 | 是 |
- 选择 1.16.0(满足 2.23.0 且已修复 Spring 兼容),通过
upgrade-from-uri接口在线升级,插件恢复STARTED。
根因
内核在 3 月升级到 2.23.0 时 Spring 大版本升级,旧版应用市场插件(1.12.0)不兼容而崩溃;官方修复版又要求 ≥2.25.0。属于典型的"插件版本落后于内核"问题。临时用 1.16.0 兼容过渡,根治依赖内核升级(见第三节)。
三、Halo 内核升级:2.23.0 → 2.26.1
部署形态(升级前摸底)
通过 ps 与 systemd 确认(服务器内部路径已脱敏为 <数据目录>):
- 非 Docker,裸 jar 部署:
<数据目录>/halo-latest.jar - systemd 服务
halo.service,JVM 参数-Xms256m -Xmx512m -XX:+UseG1GC -XX:MaxGCPauseMillis=200 - 额外配置
--spring.config.additional-location=<数据目录>/config/application.yaml,端口 8090 - 数据目录
<数据目录>/work(含 H2 数据库、插件、主题、上传文件)
是否无损
无损。数据全部在数据目录中,与程序 jar 分离;数据库结构由内置 r2dbc-migrate 在启动时自动迁移。风险点仅在于:旧插件可能因内核升级不兼容(与第二节同类问题),表现为插件 FAILED,不影响主站,可单独处理。
操作步骤
# 1. 停服务并备份数据
systemctl stop halo
tar czf <备份文件> -C <数据目录> work config
# 2. 下载 2.26.1 发行 jar(国内服务器经 ghfast.top 镜像拉 GitHub Release)
curl -L -o /tmp/halo-2.26.1.jar \
"https://ghfast.top/https://github.com/halo-dev/halo/releases/download/v2.26.1/halo-2.26.1.jar"
# 3. 校验后替换(保留旧 jar 以便回滚)
file /tmp/halo-2.26.1.jar # 应为 Zip archive,约 125MB
cp <数据目录>/halo-latest.jar <数据目录>/halo-old.jar.bak
\cp /tmp/halo-2.26.1.jar <数据目录>/halo-latest.jar
md5sum /tmp/halo-2.26.1.jar <数据目录>/halo-latest.jar # 两个值必须一致
# 4. 启动并跟踪日志,确认 Version: 2.26.1
systemctl start halo
journalctl -u halo -f
过程中的两个小坑
cp被 alias 成cp -i:覆盖时提示overwrite?,直接回车等于回答"否",必须输y或用\cp绕过 alias。曾因此出现"以为换好了、实际还是旧包"(启动日志仍显示 Version: 2.23.0),靠启动日志版本号 + md5 双验证发现。- 替换后启动仍为 2.23.0:即上一条的后果,重新覆盖并以 md5 校验后恢复正常。
升级后验证
- 启动日志
Starting Application v2.26.1,数据库迁移约 1~3 分钟。 - 通过 API 轮询插件状态:升级后所有插件被 reconciler 逐个重启,最终 10 个插件全部
STARTED,无一个因内核升级挂掉。 - 将应用市场升级到 1.19.0(要求 >=2.26.0,此时满足)。
- 补装此前缺失的
plugin-ai-foundationv1.1.0(要求 >=2.26.0)。注意:让服务器从 GitHub 直链下载会 504(国内服务器到 GitHub 链路不稳定,openresty 网关先超时),改为在本机下载 jar 后通过POST /plugins/install(multipart)上传安装,成功。
回滚方案(未使用,留档)
systemctl stop halo
cp <数据目录>/halo-old.jar.bak <数据目录>/halo-latest.jar
systemctl start halo
数据未变更时无需恢复数据备份;如数据库迁移后需回滚,则用数据备份整体还原数据目录(H2 结构不向下兼容)。
四、Markdown 写作与渲染修复
背景
写作习惯:Typora(纯 Markdown)→ 复制 → 粘贴进 Halo 控制台。控制台内置的是 ProseMirror 块状富文本编辑器,不具备 Markdown 源码编辑能力,粘贴 Typora 富文本后格式退化,体验差。
处置一:安装 Vditor Markdown 编辑器
- 插件:
vditor-mdev1.10.3(justice2001/halo-plugin-vditor,要求 >=2.14.0)。 - 安装方式同上(本机下载 + multipart 上传),启用后
STARTED。 - 效果:文章编辑页右上角可切换编辑器为 Vditor,纯 Markdown 编辑 + 实时预览,Typora 内容可直接粘贴;选择会被记住。
处置二:修复已被写坏的一篇文章
文章:《LangChain 官方开源了个"AI 软件工厂",我把源码扒了个底朝天》。
问题定位:通过 API 读取该文章正文(GET /posts/{name}/head-content),发现整篇文章的 raw 是被一个巨型代码块包裹的 HTML:
<pre><code class="language-markdown"># LangChain 官方开源了个…
…(全文 Markdown 原文,引号被转义为 ")…
</code></pre>
rawType 为 HTML。这解释了为什么编辑器里满屏裸露的 **、|---|、``` 符号:内容整体退化成了一个"代码块"。
修复步骤:
- 用正则拆掉
<pre><code class="language-markdown">…</code></pre>包裹层,还原 HTML 实体("'<>&)得到纯净 Markdown(3674 字符)。 PUT /posts/{name}/content,body{raw, rawType, snapshotName}。- 注意:第一次误用
rawType: "text/markdown",导致前台正文渲染为空。对照站内其他正常文章,其 rawType 均为markdown,改回markdown后正常。Halo 对两种值的行为不一致,markdown是主题实际识别的值。
- 注意:第一次误用
- 内容更新后
releaseSnapshot不会自动同步,必须再调用一次PUT /posts/{name}/publish(注意是 PUT 不是 POST)重新发布,前台才切换到新内容。 - 验证时的一个易误判点:修复后前台页面由 Vditor 插件改为浏览器端渲染(服务端 HTML 里正文为空,取而代之的是 vditor-render 脚本和
data-type占位容器)。用 curl/脚本抓 HTML 会误以为"正文没了",需以浏览器实际显示为准,或检查release-content的 rawType 与内容。 - 用户最后重新发布一次,前台显示正常(标题、目录、代码块、表格、ASCII 架构图均正确)。
结论
- 内核/插件层面已具备完整的 Markdown 工作流:Vditor 编辑(后台)+ 主题 Markdown 渲染(前台)。
- 建议此后新文章统一使用 Vditor 编辑器录入,避免块状编辑器对 Markdown 粘贴的退化问题。
附:可复用的操作要点
商店 API(公开,无需认证)
- 应用信息:
GET https://www.halo.run/apis/api.store.halo.run/v1alpha1/applications?names=app-xxxxx - 应用全部 release:
GET https://www.halo.run/apis/api.store.halo.run/v1alpha1/applications/app-xxxxx/releases(含 asset 名、版本、spec.artifact.requires最低内核版本) - 下载直链格式:
https://www.halo.run/store/apps/{appId}/releases/download/{releaseName}/assets/{assetName}
经验
- 插件兼容性排查:插件 FAILED 先看
status.conditions[type=Ready].message;跨版本选择时用 jar 内plugin.yaml的requires+ 字节码方法引用做双验证,比翻 release note 可靠。 - 国内服务器拉 GitHub 不稳定:服务器侧
install-from-uri指向 GitHub 直链容易 504;改为"本机下载(可走 ghfast.top 等镜像)+ multipart 上传"最稳。 - 换包操作必须双重验证:启动日志版本号 + 文件 md5,防止
cp -ialias、下载不完整等静默失败。 - Halo 正文更新 ≠ 前台更新:
PUT content只改 head 快照,需再publish同步 release 快照;且前台可能有 page-cache 静态缓存,验证时用浏览器强刷。 - rawType 值敏感:实测主题识别
markdown,text/markdown会导致正文渲染为空。