新的博客配置(Obsidian + Hugo + 阿里云 OSS + CDN 一键发布)
自从 2017 年底搭了我的 WordPress 博客以来,虽几经辗转,但这套软件已经运行了很多年都没出过问题。从上个月开始,我突然发现我的博客后台所有文章都被锁定,网页也是一片混乱。不太确定是遇到了 WordPress 的恶性 bug,还是被网站被攻击了,总而言之,我得换了。
为了避免以后再发生这样的问题,同时也为了削减一下自建博客的开销,最终我决定切换到静态页面生成器了。正巧我也迁移到我的笔记到 Obsidian 有一段时间了,折腾一下自动备份 + 发布的问题。本文介绍了我目前基于 Obsidian 的笔记工作流,它支持在 Obsidian 内部编写博客,并在 Obsidian 菜单页面一键发布。本文只讲大体思路,具体代码见 LinHeLurking/obsidian-deploy。
Obsidian Markdown
开个新的 Obsidian Vault,用来存博客下的所有内容。旧博客的文章我抢救了一部分我觉得有一定价值的东西出来(剩下的垃圾文章就让它淹没在互联网的长河中吧),改好格式存成 MD 文件,放进了 Obsidian。
由于之后是计划用 Hugo 来做博客的网页,所以还要给准备一下每个页面的元数据。我利用 Obsidian Templates 来帮我生成:
---
title: "{{title}}"
date: {{date:YYYY-MM-DD}}T{{time:HH:mm:ss}}+08:00
draft: true
---
再利用 Obsidian 插入模板到页面,这个 ---
之间的内容(包括 ---
本身)会被自动替换成相应的事件和文档标题,放在页面文件开头的部分。旧的博客页面可能还需要手动修改一下日期,不过问题不大,Obsidian 本身是可以识别这种语法的,会弹出相应的面板来设置日期时间。这里的日期和时间,格式与 Hugo 的配置是对应的。
Obsidian 里面看起来应该像这样子。可以通过点击红框内的按钮,来调整该字段的数据类型。
备份到 GitHub 私有仓库
挑来挑去,最终还是用了这个插件:denolehov/obsidian-git。可以直接在 Obsidian 的社区插件页面搜索到它,安装即可。先在当前的 Obsidian Vault 目录配置好一个 git 仓库及其远端地址,之后就可以在 Obsidian 内部执行 push/pull 操作。这里我为了方便,把远端地址设置为了我的一个私有仓库,这样 push 之后在不做额外操作时,是不会有人看到备份内容的。推荐使用 SSH 私钥推送内容到 GitHub 私有仓库。
Hugo 集成
我用 devidw/obsidian-to-hugo 这个小工具来做 Obsidian Markdown 到 Hugo Markdown 的转换,其实主要就是把 Obsidian 内部的一些 Wiki link 转换成 Hugo 接受的格式,然后复制全部文件到指定的 Hugo 目录下。转换完成之后就可以丢给 Hugo 来做页面生成了。这个小工具支持文件过滤,比如说我把 .git
、.github
文件夹全都过滤了,这样它们都不会被放进 Hugo);也支持文件预处理,我利用它来特殊处理一下文件。文件预处理取决于自己的需求,可能不具有通用性,因此这部分内容我放在了 附录/数学公式 中。
图片
Obsidian 里面插入图片很自由,随便指定一个 Vault 内部的路径即可。但是在 Hugo 中,只有两种办法:
- 每篇文章创建一个文件夹,文件夹名是文章名,文件夹内部是一个包含文章内容的
index.md
文件,和若干图片文件。文章内部引用这些图片。 - 在 Hugo 站点的
static/images
文件夹内创建图片,在文章内部用/images/xxx
的路径来引用图片。
第一种无需任何额外操作,直接让 devidw/obsidian-to-hugo 把 Obsidian Vault 内的文件都复制到 Hugo 站点的 content
目录即可。第二种需要手动把 Obsidian Vault 内创建一个顶级的 images
文件夹,再在生成页面时将它复制到Hugo 站点的 static/images
文件夹内。我个人偏好第二种,因为这样 Obsidian 内部看到的目录结构会更干净清爽一些。虽然需要额外的手动操作,但是我们完全可以自动化它。
阿里云 OSS + CDN
这套方案对于个人博客而言还是很廉价的,相比于自己租低配 VPS 还是便宜一些。具体操作这里不多介绍,参见阿里云的文档页面:静态网站托管_对象存储(OSS)-阿里云帮助中心 (aliyun.com)。本来,OSS 可以基于最后修改的时间来做增量上传,但是由于我们这套方案每次都会刷新 Hugo 文件夹内的文件,永远都是本地修改时间戳晚于远端修改时间戳。因此我自己手写了一个脚本,基于文件 hash 值,生成了一个 .meta.json
文件。根据远端的信息和本地的信息,决定是否要删除远端存储或上传本地存储。姑且也是能省点流量,也能减少 CDN 回源的更新。
自动化发布
按照前文所述,现在我们的 Obsidian Vault 已经配置好,并且可以同步到一个私有的 GitHub 仓库了。现在我们白嫖 GitHub 的算力,来代替我们做手动打包发布的流程。在这里,我把 Obsidian Markdown 文件的备份仓库和打包的代码仓库分开了,这样一方面还是为了 Obsidian 界面里的目录干净清爽一些,另一方面也是为了方便未来修改打包方案。(实际上把两个仓库合在一起是完全可行的,自行取舍吧~)
按照我的设计,博客页面备份在仓库 A
,而打包发布的代码存放在仓库 B
。Obsidian 菜单内执行备份,文章全部上传至 A
,此时触发 仓库 A
的 GitHub Action,利用 webhook 触发 B
的打包发布操作。B
仓库拉取 A
仓库的代码,把 Obsidian 的文章全部转换为 Hugo 的文章,并生成对应的 HTML 页面。最后把生成的页面增量更新至云端,发布结束。
如何定制
我就是不想花一分钱买云服务,我要白嫖 GitHub Pages 我该怎么办?我要白嫖 Cloudflare Pages 我该怎么办?
可以看出,上述流程中 Obsidian 到备份仓库 A
的一切配置都可以不更改。只需替换 B
仓库的代码为对应的发布代码,就能完全把后续的发布流程改掉。
附录
数学公式
数学公式如何被处理可能与当前使用的 Hugo 主题有关。 我用的主题支持用标识符花括号括起来的 < raw >
和 < /raw >
包裹数学公式,然后标识符内部的数学公式都不会被 Hugo 执行转义,而被 1:1 复制到对应 HTML 页面交由 KaTeX 渲染。那么就只需要写个正则替换即可。
主题修改
我这套流程有点小问题:当标题包含 +
号时,生成的页面可以在本地正常预览,上了 CDN 之后 URL 转义会出错。原因是 +
号被转义成了 +
,但实际上在 URL 中他应该是 %2B
。于是我就改了改原本主题的几个布局文件,把这个字符串给替换掉,一切就正常了。该主题仓库是用 git submodule 形式引入的,但由于我懒得单独 fork 该主题仓库,所以我决定导出一个 patch 文件,在初始化 submodule 后,生成 HTML 文件之前执行 git apply
。此外,我对主题的一些小修小补也都是用的这个办法。