博客迁移至 Hugo 并实现自动化

好久不见!

自 6 月以来,本人虽然没什么产出,但怎奈没动力写博客,至今只写了两篇不疼不痒的综述文章。但今天(2019/10/20)偶然在 v2ex 上看到了 hexo 释出 4.0 版本 的消息,而后想到自己曾数次想要脱离贵前端生态圈,摆脱令人脑壳痛的依赖问题,实现博客自动化等等等等事情。

于是,我趁着今天是闲暇的周日,把一切事情办好了。

下面是过程复现与总结。

Hugo

其实最早我并不是太想换到 Hugo 的,但奈何自己想要写一个静态博客生成器的计划一直提不上日程…… 便选择了这个解决方案。

之后是主题的选择。由于 Hugo 的主题生态圈较之 Hexo 仍较为匮乏,因此选择并不多。经过我在各种网站上的搜罗与比较,下面列出我较为偏向的选择:

  • Maupassant,这个是我在 Hexo 上一直使用的主题,但 Hugo 上的移植版我不太满意,pass。
  • https://linw1995.com/,这个主题我十分喜欢,但由于没有提供主题的 repo,只能作罢。
  • LeaveIt,这个主题看着还行,但太久没维护,都没办法用……
  • KeepIt,这个是上面 LeaveIt 的 fork,也是我最终的选择。

但由于 KeepIt 的功能还是不太能满足我的需求,因此我又 fork 了一份,作为自用。地址 batkiz/left

开始迁移

整个迁移过程中,我最在意的是该死的 兼容性 ,即,我新生成的博客要与之前的博客在整体行为上没有太大变化,下面就是在保证兼容性前提下的迁移过程。

链接

之前的链接不能直接 404,让读者找不到文章(虽然原来就没啥读者),不能造成割裂行为。

Hexo 默认的链接生成方法为 /:year/:month/:day/:filename/,而 Hugo 的则为 /:year/:filename/

解决很简单,修改为相同的即可。

RSS

之前在 hexo 之上构建时,我采用的 hexo 插件是生成出 atom.xml 文件的,而在新的主题下生成的则是 index.xml

解决方法:在 config.toml 中加入下列内容

1
2
3
4
5
6
[outputs]
  home = ["Atom", "HTML"]

[outputFormats.Atom]
  mediatype = "application/rss"
  baseName = "atom"

即可生成 atom.xml 文件作为 RSS feed。

在成功解决上述兼容性问题之后,新问题随之而来:为什么生成的 xml 里 tmd 没有内容?

为解决此问题,我试着看了看源码,但看不懂(

接着我试了试 Google,感谢 kaushalmodi 的代码,我用它覆盖了 index.atom.xml 之后便成功部署了正确的 RSS,简单修改(将默认的只生成最近 6 条增加到 20)之后便符合了需求。

mathjax

虽然我平时很少输入数学公式,但这个功能也不能少!

首先我尝试了将 mathjax 的支持直接写入 head/footer/js 等几个模板,但,还是没用(即使我已经在源码中看到了 mathjax 的支持代码)。

接下来仍然是 Google。

感谢 匿蟒,我在 在 Hugo 中使用 MathJax 一文中找到了解决方案,即下述方法:

在添加 MathJax 时,把所有修改写成了一个 layouts/partials/mathjax.html 文件:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<script
  type="text/javascript"
  async
  src="https://cdn.bootcss.com/mathjax/2.7.6/MathJax.js?config=TeX-AMS-MML_HTMLorMML"
>
  MathJax.Hub.Config({
      "HTML-CSS": {preferredFont: "TeX", availableFonts: ["STIX", "TeX"], linebreaks: { automatic: true }, EqnChunk: (MathJax.Hub.Browser.isMobile ? 10 : 50) },
      tex2jax: {inlineMath: [["$", "$"], ["\\(", "\\)"]], processEscapes: true, ignoreClass: "tex2jax_ignore|dno", skipTags: ['script', 'noscript', 'style', 'textarea', 'pre', 'code'] },
      TeX: {noUndefined: { attributes: { mathcolor: "red", mathbackground: "#FFEEEE", mathsize: "90%"} }, Macros: { href: "{}" } },
      messageStyle: "none"
  });

  MathJax.Hub.Queue(function () {
      var all = MathJax.Hub.getAllJax(), i;
      for (i = 0; i < all.length; i += 1) {
          all[i].SourceElement().parentNode.className += 'has-jax';
      }
  });
</script>

<style>
  code.has-jax {
    font: inherit;
    font-size: 100%;
    background: inherit;
    border: inherit;
    color: #515151;
  }
</style>

这里,把官网的三处修改合并成一个 partial。 此外,还把 MathJax 的 CDN 从 cdnjs.cloudflare.com 替换成了 cdn.bootcss.com,更好地支持国内。

把这个 partial 模板添加到 <head> 中,即可正常工作。

1
{{partial "mathjax.html" .}}

frontmatter

Hugo 支持 toml, yml 与 json 格式的 frontmatter,但与 Hexo 的 yml frontmatter 又有所不同。

Hugo 中的 tags 的形式应为

1
2
3
4
tags:
  - hello
  - world
  - etc

即使只有一个 tag ,也要按此格式来写,否则会编译错误。

此外还有 title 的值应被包裹在括号之中等。

另外,hexo 中的 time 项可不用修改,Hugo 似乎已支持了此种格式。

版本控制与自动化

很久之前也曾想过要实现博客的云同步与自动化部署,但当时没有余力去做,今天趁着迁移博客,顺带利用 GitHub actions 实现自动化。

版本控制

首先是对内容(markdown 文件)的版本控制。

hugo new site . 生成出来的文件夹,使用 git init ,并添加以下的 .gitignore 信息:

/archetypes/
/data/
/layouts/
/public/
/resources/
/static/
/themes/

这样的话,只有下面两项被加入了版本控制之中:

  • content/,这个是文章所在的文件夹
  • config.toml,这个是 hugo 的配置文件

之后可以将此目录推向 GitHub,进行文章的版本控制与部署的自动化。

自动化

Hugo 的无依赖特性、配置都写在根目录下的 config.toml 文件中的功能与 GitHub Actions 的出现,使得文章自动部署变得极为简单。毫不夸张的说,在最初的配置之后,我们可以专注于文章的内容,本地甚至不需要有 Hugo 的存在。

下面是 GitHub Actions 的 workflow,详情请看注释。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
name: blog

on: [push]

jobs:
  build:
    name: Build
    runs-on: ubuntu-latest
    steps:
      # 使用预编译的 Hugo 二进制文件
      - name: Setup Hugo
        uses: peaceiris/actions-hugo@v2.2.2
        with:
          hugo-version: "latest"
          # 使用 Hugo-extended 版本
          extended: true

      # 新建站点
      - name: new site
        run: |
          hugo new site $HOME/blog          

      # 获取主题
      - name: get theme
        run: |
          git clone --depth=1 https://github.com/batkiz/left.git $HOME/blog/themes/left          

      # 获取 username.github.io 以保持 commit 记录
      - name: get username.github.io
        run: |
          git clone --no-checkout https://github.com/batkiz/batkiz.github.io.git $HOME/blog/public          

      # 获取内容文件
      - uses: actions/checkout@v1
        with:
          fetch-depth: 1

      # 将内容文件复制进入站点内
      - name: copy in
        run: |
          rm -rf $HOME/blog/config.toml $HOME/blog/content/
          /bin/cp -rf config.toml $HOME/blog/config.toml
          /bin/cp -rf content/ $HOME/blog/content/          

      # build
      - name: build
        run: |
          cd $HOME/blog
          HUGO_ENV=production hugo --gc --minify          

      # 将生成出的站点推向 username.github.io
      - name: publish
        env:
          USER: batkiz
          EMAIL: batkiz@outlook.com
          GH_REF: github.com/batkiz/batkiz.github.io.git
          # 这里的 token 需要自己在 GitHub settings 中部署,并利用 actions 的 secrets 功能
          GH_TOKEN: ${{ secrets.GH_TOKEN }}
        run: |
          cd $HOME/blog/public
          git config --global user.name $USER
          git config --global user.email $EMAIL
          git add .
          git commit -m "Auto Update: `date +'%Y-%m-%d %H:%M:%S'`"
          git push "https://$USER:$GH_TOKEN@$GH_REF" master:master          

感谢 https://github.com/peaceiris/actions-hugo 提供的可在 GitHub actions 使用的 Hugo 二进制文件,避免了每次都手动编译 Hugo。

另外希望大家别不看内容就随便写,会出 typo 的,然后 failed(泪

结语

经过一个下午 + 一个晚上的努力,终于成功地把博客迁移到了 Hugo,并实现了内容的版本管理与自动化部署。可喜可贺,可喜可贺。

接下来大概会抽空将以前的文章格式调整一下(Hugo 的 markdown 不知道是用啥解析的,写的随意的话不会按你想的那样渲染),以及自己写一个 Hugo 的主题。

see you later.