上一篇文章介绍了 Emacs 的理念以及其强大的扩展功能,基本上能在 Emacs 里面做到事,我都在 Emacs 里面做。之前的博客一直都是用的 markdown 来书写,虽然 Emacs 也有 markdown 插件,但是总感觉体验不如 org-mode。这周末就趁着手热,把博客系统进行了升级,完美支持 org-mode,这篇文章就是用 org-mode 完成的,下面就来讲一下迁移过程。
Hexo vs Hugo
最近两年一直在用 hexo 来写博客,记得静态托管类网站刚兴起时,hexo 是比较流行的,整个生态也比较完整。这次迁移博客系统时,决定不再用 hexo,原因主要有下面几个:
- 安装烦。作为一个 Node.js 写的系统,安装时需要下载大量的
node_modules
。这一点不是很核心,毕竟安装只是一次性工作 - 升级难。记得当初在 hexo 2 升级到 3 时,遇到过不少坑,对 Node 的版本也有要求,如果部分依赖用了 C 代码(比如 node-sass),安装起来也比较费劲;现在 hexo 大版本已经到了 5.2.0,不想再去折腾
- 原生不支持 org-mode,使用了下 hexo-renderer-org 插件,也没成功,而且其作者现在也没精力来维护了
基于以上几点,hexo 于我而言,不再是最佳的选择,当然并不是说它不优秀,Hugo 也很有可能有类似问题。我之所以选择 Hugo 主要有两个原因:
- 基于 Golang 的 Hugo 实现,安装方便,只需一个二进制文件;而且最近两年我个人也一直在用 Golang 开发,比较熟悉
- 原生支持 org-mode
Hugo 使用方式比较简单,这里不再赘述,新手可参考官方 Quick Start。
迁移过程
旧博客备份
文章链接
对于个人博客的迁移而言,保证文章链接不变是最重要的。在 hexo 中,文件名与链接的对应关系如下:
|
|
对应的 markdown 文件是
|
|
这样的好处是文件按照时间排列,管理方便。
Hugo 则不同,它默认会用文件中 frontmatter 的 date,而非文件名中的。我已经习惯了 hexo 的组织方式,不想去改变,经过一番探索,最终在 hugo 添加下面的配置完美解决:
|
|
这样 Hugo 就会首先从文件名中解析日期,同时也会解析 slug;失败时再从 frontmatter 中解析,更多用法可参考官方文档 Configure Dates。
同时,为了避免 hugo new post/year-month-day-slug.org
时标题中带日期,修改默认模板如下:
|
|
RSS
Tags 链接
Hugo 中默认会把链接中的字母变成小写,比如标签 Go
,在之前对应地址 /tags/Go
,换成 Hugo 后则是 /tags/go
,可以通过下面的配置关闭这个转化。
|
|
如果想保留这个功能,可以进行下面的操作:
|
|
这时 git status
会显示
|
|
然后提交就可以了。
Frontmatter
Frontmatter 定义了每篇文章的属性,比如标题、分类等。这也是在 hexo 迁移到 hugo 时问题最多的地方,根本原因在于 hexo 对 frontmatter 格式较宽松,而 hugo 则比较严格。
下面一个 hugo 中标准的 frontmatter(除 yaml 外,还可以是 toml/json):
|
|
主要有两点需要注意:
- categories/tags 这两个属性必须是数组
- frontmatter 前后需要用
---
包起来,与正文区分
而在 hexo 中,
- categories/tags 可以是数组,也可以是字符串,表示一个元素的数组
- 只需要 frontmatter 末尾强制用
---
与正文区分,前面的不做要求
由于我文章较多(72篇需要迁移),且格式也都不一样(可能是 hexo 2/3 的区别),因此写了两个脚本来辅助,最终生成符合 hugo 要求的 frontmatter。如果 frontmatter 格式不对,可能会遇到下面的错误:
|
|
说明 categories 或 tags 有不是数组的,需要改成数组
|
|
说明缺少了 frontmatter 结尾的分隔符,如果缺少开头的分隔符,编译文章没有错误,但是最终生成的文章页面会没有标题。
Categories
在 hexo 中分类(category) 和标签(tags) 用法是不一样的,分类可以有层次,比如:
|
|
标签则没有;在 hugo 中分类与标签用法一样,都只有一层。由于我之前博客就没有用到多层分类的情况,所以也就不需要额外处理了。
其次,在 hexo 可以通过 category_map/tag_map 来定义 category/tags 的固定链接地址(即 slug),虽然我之前也了这个特性,但是这次并没有去适配,采用 hugo 默认的即可。有修改需求的读者可参考:
Render hook
markdown 中引用图片的标准做法是
|
|
但是我一般只写 alt,title 基本没写过,之前使用的主题 maupassant 默认会把图片的 alt 显式在图片下面,而 hugo 只认 title,搜索发现可以通过 hugo 提供的 markdown render hook 来实现。方式如下:
- 创建
layouts/_default/_markup/render-image.html
文件 添加内容
1 2 3 4 5 6 7 8
{{ if .Text }} <figure> <img src="{{ .Destination | safeURL }}" alt="{{ .Text }}"> <figcaption>{{ .Text }}</figcaption> </figure> {{ else }} <img src="{{ .Destination | safeURL }}" alt="{{ .Text }}"> {{ end }}
对于 org-mode 而言,直接采用下面的方式即可:
|
|
修改记录
本次迁移的所有修改可以在 Github 中查看,供有相同迁移需求的读者参考。
Emacs 集成
经过上面的步骤,已经可以很好的把 hexo 迁移到 hugo,这一小节主要是介绍 Hugo 与编辑器的集成,方便写文章。
Easy-hugo
2022-09-03 更新:这个插件功能我用的比较少,已经不再使用,主要是使用我自己写的 my/hugo-newpost 来创建文章。
Hugo 官网上列举了一些与常用编辑整合的插件,这里介绍 easy hugo 的使用方式。由于目前我又两个博客(中文和英文),因此需要做些配置让 easy hugo 识别这两个。
|
|
由于目前我全局开启了 evil mode,需要把 easy-hugo-mode 添加到 evil-emacs-state-modes 里面去才能使用 easy-hugo 的快捷键,顺道解决了 easy hugo 的一个 bug。
easy-hugo 还提供了预览、发布(默认调用 deploy.sh)等命令,比较简单,这里不再赘述。
创建新文章
虽然可以用 hugo new post/xxx.org
的方式来创建新文件,但是由于文件名中需要有固定格式的日期,每次手动输入很繁琐,因此自己实现了一个辅助函数,实现如下:
|
|
这样就可以通过调用 my/hugo-newpost
来自动生成带日期的文件名。
自动部署
GitHub Pages
可以利用 actions-hugo 这个 action 实现网站自动的部署 。
早期的 Pages 需要把网站源码发布到一个 gh-pages
的分支上,对于纯手写 HTML 的网站来说还算可以,但是对于使用工具生成的网站来说,就有些浪费,因为这个分支根本不需要版本管理,不仅会污染仓库,还会导致仓库体积变大。
自从 2022-08-11 后,GitHub 解决了这个问题,允许使用 Actions 中构建出来的产物(Artifact)直接部署网站,无需新建分支,毫无疑问这种方式更优雅。使用方式两步:
- 配置 Pages 生成方式为基于 Action
- 根据 hugo.yml 编写自己项目的 action,本博客的配置文件可供读者参考:gh-pages.yml
Codeberg Pages
Codeberg 提供与 GitHub Pages 类似的服务,只不过必须用一个分支来保存 HTML 源文件,可以利用 Codeberg CI 自动化构建过程,可以参考:.woodpecker.yml
总结
屠龙刀已经磨好了,下面就需要多去动“刀”写出更多文章了。