Hexo 二次建设(一)
Hexo 维护记录
前言
自前年兴冲冲地折腾好了 Hexo + Buftterfly 后便陷入长达一年半的虚无期。无他,一者发现搭博客「也就那么回事」,二者,此类框架要写好文章才能渲染,且没有第三方博客平台一键发布的便利。[1] 近来被推荐在简历附上个人博客,于是借此机会重新修整,作文以记之。
框架 | 主题
略有过时的 Hexo + Bufferfly,但初见一图流的惊艳让它始终是我的白月光,框架选型时没犹豫地闭眼冲了。插件系统提供了高度自由的定制空间,只是时间过去已久,没能记下魔改博客的每一步。时至今日,倒也没那么重要了。
Twikoo 评论
当初部署时的一个小坑。Vereel 部署时,如果使用 Gitlab 为仓库,则 Twikoo 必须扔在 Personal Projects 下,否则跟你要会员。
之前发现 Twikoo 挂了,检查后发现 i) 当时绑定的域名没在续费;ii) MongoDB 因为太久不活跃,把数据库关了。
按照站长的教程[2]重新盘了一遍。定期发一些评论吧。
SSL
见 OHTTPS 相关文章。
图床
对象存储使用七牛云。上传接口使用 PicUploader,功能齐全。另部署 ImgUrl 作为备用的服务器本地图床。
CDN
Gitlab Pages 墙内加载不稳定,速度时好时坏。终于决定做 CDN 加速。[3] [4] [5]
七牛云 CDN
这里要说明,为什么博客用了两个域名。
主机记录 | 说明 | 备注 |
---|---|---|
blog | Gitlab Pages 绑定的域名 | 因为不是在用户同名仓库[6]弄的,必须绑定一个纯净域名方可 CDN,同时一定程度上避免被墙。 |
www | 加速域名 | 注意从绑定域名回源。 |
Hexo 资源路径不兼容子路径部署。即,_config.yml 中填入的 url 若带有子路径,则用绑定域名访问是无法正常加载各种 assets 的,反之亦然。所以在没上 CDN 的时候要填 blog.miyamura.top,绑了之后填这个其实也行,但是 RSS 和 sitemap 等就是按源站来,最好改成加速域名。
1 | url: https://www.miyamura.top |
缓存期限暂时设置与源站同步。这样配置之后得到 www 加速域名,实测速度尚可。
DNS の坑
我们腾讯云太伟大辣!起因是想把根域名重定向到 www,这有很多好处,如避免 SEO 分流等。正常情况下是 Nginx 来做。但由于是托管博客,自然是无法这样操作。几种重定向方式如下。
DNS 重定向 即直接 CNAME 到 www,尝试后 403 Forbidden,是七牛云给拒了。作罢。
HTTP 重定向 这也是最正统的方式,走 Nginx 即为这种方式。对于托管的服务,如果 DNS 托管在 CloudFlare 就好办了,通过配置转发规则可以进行 301 / 302 等重定向。
那么腾讯云是如何支持的呢?答案是不支持,其提供了一个叫显 / 隐性 url[7] 的记录类型,这个东西是类似 HTTP 重定向的,但它只允许你从 HTTP → HTTPS。最后还是用了这种方式,当弄着玩了。下次买新域名及时托管到 CloufFlare,谨记。
前端重定向 字面意思,过于丑陋,不弄。
监控
可用性监控。
首先从 UptimeRobot 添加站点监控,获取 API-KEY,后按照 imsyy/site-status 部署监控页。
自动化
懒是生产力进步的第一要素。
题外话 | StackEdit
前文提及,博客拖更一年的原因之一便是更新麻烦,但实际上这一年一直在写,用的是 Markdown 编辑器 + Github 的方式。编辑器的选择上,因为对 Web 的青眼和跨设备工作的便利性考量,一直用的 StackEdit。
这个开源平台我是十分推荐的,轻量级 / 一键同步 / 现代化的渲染器和语法支持性,附上 Stylus 自己注入一些样式进行美化,真是好用。但是也有令人恼火的地方,即定期乱改部分文章内容,看起来像 merge 错误一样。一直没有定位到成因,所以最后的方案就是凭直觉,预感网络要出问题的时候就直接清空工作区重新拉取。
文章 - 博客解耦
以往的文件组织方式都是把文章和博客框架放在一起,写起来麻烦,有类似 Hexon 之类的配套写作工具,但实际体验也不尽如人意。
那么,把文章和博客分成两个仓库 P 和 B,然后
- 每天定时触发 P 的流水线[8],检查 24h 内是否有更新;若执行流水线成功,则通过 Trigger 触发 B 的流水线。
- B 的流水线触发后,从 P 获取最新一次的构建产物,然后进行 pages 部署。
下文介绍实操过程。
Overview
仓库 | 平台 | CI |
---|---|---|
文章 | Github (Private) | Github Actions |
博客框架 | Gitlab (Public) | Gitlab CI |
所有 CI 脚本均使用 ChatGPT (GPT-4o) + CoPilot (Claude 3.7) 编写。
Github Actions | 任务
解耦的好处体现出来了,因为
- 可见性。不希望所有文章都展示。
- 文章元数据 (front matter) 组织灵活。
Hexo 的文章元数据通过以下方式携带
1 | --- |
这种方式在一般的 Markdown 渲染器里都会把元数据作为文本渲染出来,看起来较为丑陋。因此,通过 HTML 注释的方式嵌入
1 | <!-- |
这样,脚本的任务就很清晰了,首先遍历文章,将嵌入的 HTML 注释替换为 Hexo front-matter,同时,我们自定一个 display 属性,当且仅当为 true
时才允许该文章进入 artifacts/,以上。
Github Actions | 脚本
需要从 Gitlab 仓库配置一个 Pipeline Trigger,加入 Github 仓库的 Secrets。[9] Gitlab 会在 Trigger 的下方给你一个使用样例,内含当前仓库的 Project ID,这个 ID 是可以直接在 API 里使用的。设计上,还应允许手动触发。
关于脚本。定时任务的写法如下
1 | on: |
API 调用形如
1 | curl -X POST \ |
一些小坑。算时间的时候,尽量不要用 schedule - 24
这种方式,因为定时任务触发有延迟,从触发时间开始统计容易漏掉少部分时段。正确的方式是
1 | START=$(date -u -d "yesterday 21:00" --iso-8601=seconds) |
为什么是 yesterday 和 today 自己想一下。另注意 git log 输出格式带上 %n
,否则 wc -l
检测不到行数,无法进行下一步条件判断。
需要注意的是定时任务只能在默认分支触发,比如将 CI 脚本提交到 master。这样之后仍然需要手动触发一次流水线,然后方可进入 On Schedule 状态。
Gitlab CI | 任务
获取 artifacts → 解压到 source/_posts/ → 执行 pages 的任务,触发部署
Gitlab CI | 脚本
这一侧的流程复杂一些。
- 需要有三种触发方式
- Trigger 触发
- Commit 触发
- 手动触发
- Trigger 触发时可以直接接收对方携带的 RUN_ID、ARTIFACT_ID 等参数。其他触发方式则需要自行从对方的最新一次成功构建中获取。
由于文章仓库是 Private,需要在 Github 一侧配置 Personal Access Token (PAT),然后作为 Gitlab CI 的流水线变量。
在 Github Actions API 这里耽误了一点时间。参考 [10] 编写
1 | curl -L \ |
主要工作是获取上次成功构建的 RUN_ID 和 ARTIFACT_ID,获取完就万事大吉。(是吗?)
一些小坑。由于 Gitlab 的默认分支是 main,我将其改为 master,但没有将其设为受保护分支。因此,流水线变量默认注入为空,且不报错,看输出 Github API 一直返回 401,真是十分恼火。解决方案是在变量设置种取消勾选「仅在受保护分支生效」。
Trigger 触发被视为一种特殊的分支或 Tag。因此,不要手欠加上 only: master
条件。
联合调试了数次之后终于是解决了所有毛病。至此,一套完整自动化流程实现。
Summary
不要尝试一次性解决所有的问题,因为问题后面还是问题。问题就是矛盾。矛盾是普遍存在的。——《毛泽东选集》
一些遗留问题。
- 部分文章在博客发布后内容为空。
- 添加 mermaid 支持
- 个人主页模板 | imsyy/home
此外,这两个 CI 将近 400 行,即便有 Claude 3.7 加持,仍然调试了十来个小时才彻底贯通。对运维产生了一点阴影。