如何把 Gridea 完整迁移到 Hugo(含踩坑与修复)
最近把博客从 Gridea 迁移到了 Hugo,看起来是“静态博客换框架”这么简单,实际上坑不少。
这篇把整个过程完整记录下来,尤其是那些“迁完才发现”的问题。
一、迁移目标
从这套源目录迁移:
~/GRIDEA_SITES/margrop.gridea
迁移到 Hugo 项目:
~/margrop-blog
要达到的标准:
- 文章、图片、标签、slug 全部保留。
- 历史 URL 尽量不变(或能兼容跳转)。
- 评论(Gitalk)和统计(GA / 51.la)不断档。
- 发布流程可自动化,且适配代理网络。
二、内容迁移(文章与 Front Matter)
最早是用一个脚本批量把 Gridea 的 posts/*.md 转成 Hugo 文章(思路正确,后续要补字段):
python3 /root/convert_posts.py
核心动作:
- 读取 Gridea front matter:
title/date/tags/published/feature。 - 跳过未发布文章(
published: false)。 - 写入 Hugo front matter。
- 把
<!-- more -->统一为 Hugo 常见写法<!--more-->。
建议补充点(实战结论):
- 一定显式写
slug,避免 URL 漂移。 date要注意时区与未来时间,否则文章不会被 Hugo 正常发布。- 标签建议保留原始值,但要建立 slug 映射,兼容历史链接。
三、Hugo 路由与基础配置
迁移时先把路由规则定死,不然后续修复成本很高。
hugo.toml 关键项:
[permalinks]
post = '/post/:slug/'
tags = '/tag/:slug/'
[taxonomies]
tag = "tags"
category = "categories"
这一步决定了文章和标签最终 URL。
四、静态资源与历史链接兼容
迁移后最容易丢的是这两类资源:
post-images(正文图片)- 历史标签短 ID 链接
处理方式:
- 把图片静态目录完整迁入
static/。 - 保留标签映射文件(例如
static/tag-slug-map.js、static/slug-to-name-map.js)。 - 为旧短链生成重定向页面:
/tag/<旧ID>/ -> /tag/<新slug>/。
这样历史外链、搜索引擎收录、微信旧链接都能继续访问。
五、评论系统迁移(Gitalk)
这是最容易“看起来有评论框,实际上全挂”的部分。
1) 配置迁移
把 Gridea 里的 Gitalk 参数迁到 Hugo 数据文件,例如:
{
"enabled": true,
"clientID": "...",
"clientSecret": "...",
"repo": "margropcomment.margrop.io",
"owner": "margrop",
"admin": ["margrop"]
}
2) 两个关键坑
坑一:admin 被渲染成字符串而不是数组。
坑二:id 生成规则改了,导致历史 issue 对不上。
旧站规则是:
id: (location.pathname).substring(0, 49)
新站如果改成 hash(如 sha1(RelPermalink)),历史评论会“消失”。
最终修复策略:
admin必须输出为真实 JS 数组。id与旧站保持一致(路径截断 49)。- 国内网络环境下,Gitalk 资源可优先用
jsdelivr。
六、统计脚本迁移(GA + 51.la)
文章迁完、页面正常后,最容易漏的是统计脚本的“初始化段”。
这次实际补齐了三段:
js.users.51.la/...LA_COLLECT + LA.init(...)LingQue.Monitor().init(...)
仅迁移第一段 script 标签不够,初始化缺失会导致后台没数据。
七、统一页面头部(导航一致性)
迁移后首页和文章页头部不一致,通常是因为多个模板各自写了一套导航。
修复方案:
- 提取公共 partial,例如
partials/header-nav.html。 - 首页、列表页、文章页、标签页全部引用同一份 partial。
- 统一样式,避免后续再分叉。
八、发布与代理网络
发布脚本采用:
bash scripts/deploy_github_pages.sh
配置来自:
config/github-pages.json
并从 gridea/setting.json 读取 token / email(兼容旧流程)。
实战坑点:如果网络访问 GitHub 必须走代理,发布脚本需要自动读取代理配置并导出 http_proxy/https_proxy,否则 git clone / git push 会卡死。
九、迁移后的核验清单(强烈建议逐条执行)
# 1) 本地构建
hugo --cleanDestinationDir
# 2) 线上可用性
curl -I https://blog.margrop.net
curl -I https://blog.margrop.net/post/hello-world/
curl -I https://blog.margrop.net/tag/github/
# 3) 页面源码核对(Gitalk/统计)
curl -sL https://blog.margrop.net/post/hello-world/ | rg "gitalk|LA_COLLECT|LingQue|gtag"
补充说明:
- GitHub Pages / CDN 有缓存,刚发布时看到旧页面很常见。
?v=timestamp可辅助验证是否已命中新版本。- 如果文章 URL 返回 404,先查
date是否写成未来时间。
十、结论
Gridea -> Hugo 的“内容迁移”并不难,真正难的是“行为迁移”:
- URL 行为(slug、tag、旧短链)
- 评论行为(Gitalk 的 id 规则)
- 统计行为(脚本 + 初始化)
- 发布行为(代理、自动化、缓存验证)
把这四块做完整,才算真正的“完整迁移”。