为什么不继续用Simple

  Simple是一个非常简单又纯粹的博客托管方案,然而它严重依赖 github 的API登录方案–而这个方案已经在去年关闭掉了,现在没有自己的服务器,应该是无法完成github的授权的.因此我必须要换掉Simple了.

为什么是Hugo

  其实我也想过类似hexo之类的方案.但是它们有个很麻烦的问题:编译过程需要nodejs的支持.nodejs当然不是不好,但是相关的安装文件体积会很大,依赖关系也会比较复杂.而我希望我的博客编写可以差不多随时随地–比起Simple的一个网页走天下,现在的方案可能会复杂的多.还好我有VS code的server版,随时随地写markdown并不是问题;但是想随地编译网站,这个成本就有点高了.经过一番调查,hugo的依赖相对简单,采用docker的方式来使用,成本也不高,相对来说还是可以接受的.

导出Simple

  不论如何, 导出原有的文章是必须的.Simple作为基于GitHub API的静态博客,它自己是没有数据库来存储文章的,而是将markdown作为注释放在正文里的。我们恰好可以借助这一特性,将相应的md文件提取出来:

1
cat fileName.html | awk '/<!-- markdown -->/,/<!-- markdown end -->/' > exports/fileName.md

  怎么遍历文件名就不赘述了,bash脚本跑循环就好。

  不过这样导出的文件里还有markdown的注解,可以使用如下命令来清除:

1
2
sed -i "s/<!-- markdown -->//g" *.md
sed -i "s/<!-- markdown end -->//g" *.md

  那么当我之前的博客都备份好了,怎么导入到Hugo里呢?

导入Hugo

  先不说Hugo的站点结构如何,这个问题各位观众那都可以慢慢调整;最重要的是Hugo的md文件是有格式的,你得遵照一定的格式导入,也就是每篇文章开头的元数据,至少包含title,date。此外我希望导入以后,网页的URL还能按照原先的URL存在,避免死链.那怎么办呢?

  原来Simple有一个main.json是专门存储元数据的,我们可以从它解析到相应的参数,填入目标文档。经过一番探究,我写了如下的脚本生成相应的目标文档:

1
2
3
4
5
6
7
echo "---" >hugo/content/imported/$filename.md && \
echo "title: \"$(jq -r '.posts|map(select(.path == "'$filename'.html").title)[0]' main.json)\"" >> hugo/content/imported/$filename.md && \
echo "date: \"$(jq -r '.posts|map(select(.path == "'$filename'.html").date)[0]' main.json)T00:00:00Z\"" >> hugo/content/imported/$filename.md && \
echo "draft: false" >> hugo/content/imported/$filename.md && \
echo "tags: [\"$(jq -r '.posts|map(select(.path == "'$filename'.html").tags)[0]|split(" ")|join("\",\"")' main.json)\"]" >> hugo/content/imported/$filename.md && \
echo "url: $filename.html" >> hugo/content/imported/$filename.md && \
echo "---" >> hugo/content/imported/$filename.md && cat exports/$filename.md >> hugo/content/imported/$filename.md

  通过上述脚本指定的url, 我们导入的文章在imported目录下,而url仍然在根目录下,不会受到影响,这样的url就跟原来的simple的url一致,保证迁移后搜索引擎的链接仍然有效。

使用Even

  据我观察, Hugo 使用 Even 主题的博客很多,简单了解了一下,这个主题已经比较完善了.但是我刚刚开始使用它,咦,目录空了,囧. 谷歌一下,发现它必须使用post目录,而不是默认的posts目录,更不支持imported目录了.最简单的办法就是把imported目录放到post目录下,以后新建的文章就放到post目录下就好啦.

  但是随后就发现生成的rss文章包括了关于我这样的页面,这个问题还是比较严重的.不过默认hugo生成的rss是包含区域rss的,因此直接使用post/rss.xml就可以只生成post目录下的rss文件了.还可以在编译后将rss.xml拷贝到根目录下,这样原有url的rss也是正确的了.

修复Tag的URL

默认的tag是baseurl/tags/MyTag.html,但是tag页面的url是baseurl/tags/MyTag/.怎么修复呢?原来Hugo本身有一个Tag的uglyUrl的bug,在生成tag的时候读不到config里的uglyUrl的变量.这个能力在Even里已经解决了,但是不是非常的优雅:就是在config里的params再增加一个uglyURLs的变量,供even使用.记得它跟config的uglyURLs变量保持一致即可.

RSS全文化

  RSS默认只输出部分,这种体验对于我们这种不求访问量的网站的订阅者来说,是非常不友好的.因此需要调整为全文输出.按照网友的经验,在layouts目录下新建index.rss.xml就可以解决这个问题:

 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
{{- $pctx := . -}}
{{- if .IsHome -}}{{ $pctx = .Site }}{{- end -}}
{{- $pages := slice -}}
{{- if or $.IsHome $.IsSection -}}
{{- $pages = $pctx.RegularPages -}}
{{- else -}}
{{- $pages = $pctx.Pages -}}
{{- end -}}
{{- $limit := .Site.Config.Services.RSS.Limit -}}
{{- if ge $limit 1 -}}
{{- $pages = $pages | first $limit -}}
{{- end -}}
{{- printf "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>" | safeHTML }}
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>{{ if eq  .Title  .Site.Title }}{{ .Site.Title }}{{ else }}{{ with .Title }}{{.}} on {{ end }}{{ .Site.Title }}{{ end }}</title>
    <link>{{ .Permalink }}</link>
    <description>Recent content {{ if ne  .Title  .Site.Title }}{{ with .Title }}in {{.}} {{ end }}{{ end }}on {{ .Site.Title }}</description>
    <generator>Hugo -- gohugo.io</generator>{{ with .Site.LanguageCode }}
    <language>{{.}}</language>{{end}}{{ with .Site.Author.email }}
    <managingEditor>{{.}}{{ with $.Site.Author.name }} ({{.}}){{end}}</managingEditor>{{end}}{{ with .Site.Author.email }}
    <webMaster>{{.}}{{ with $.Site.Author.name }} ({{.}}){{end}}</webMaster>{{end}}{{ with .Site.Copyright }}
    <copyright>{{.}}</copyright>{{end}}{{ if not .Date.IsZero }}
    <lastBuildDate>{{ .Date.Format "Mon, 02 Jan 2006 15:04:05 -0700" | safeHTML }}</lastBuildDate>{{ end }}
    {{- with .OutputFormats.Get "RSS" -}}
    {{ printf "<atom:link href=%q rel=\"self\" type=%q />" .Permalink .MediaType | safeHTML }}
    {{- end -}}
    {{ range $pages }}
    <item>
      <title>{{ .Title }}</title>
      <link>{{ .Permalink }}</link>
      <pubDate>{{ .Date.Format "Mon, 02 Jan 2006 15:04:05 -0700" | safeHTML }}</pubDate>
      {{ with .Site.Author.email }}<author>{{.}}{{ with $.Site.Author.name }} ({{.}}){{end}}</author>{{end}}
      <guid>{{ .Permalink }}</guid>
      <description>{{ .Content | html }}</description>
    </item>
    {{ end }}
  </channel>
</rss>

但是我们发现有个问题,about之类的静态页面也被当成博文输出到RSS里了,这个体验可有点奇怪.正常的解决方案是如何将这些非正常的页面不被识别为RegularPages,不过我找了半天,并没有找到怎么处理.好在这个问题已经有人解决过了,将28行的代码改成

1
{{ range (where .Site.Pages ".Section" "post") }}

但是这个方案并不完美,因为开头的代码刚刚处理了limit问题,这里一改,一夜回到解放前lol.

那我们把这个条件换到开头的if里即可.而且使用的section就是post,因为我们说过了,even这个主题只支持post目录下的文章,自然我们只要输出post目录就行了.

但是这还有个问题,因为hugo会生成每个section的landing page,名为posts.html,title是Posts,输出的RSS里总有这个文件. 这个可能跟我们刚刚改的方案有关系,少了regularpages的判断,那么条件可以改成

1
(where $pctx.RegularPages ".Section" "post")

那自然是大功告成了,最终的模板如下:

 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
{{- $pctx := . -}}
{{- if .IsHome -}}{{ $pctx = .Site }}{{- end -}}
{{- $pages := slice -}}
{{- if $.IsHome -}}
{{- $pages = (where $pctx.RegularPages ".Section" "post") -}}
{{- else if $.IsSection -}}
{{- $pages = $pctx.RegularPages -}}
{{- else -}}
{{- $pages = $pctx.Pages -}}
{{- end -}}
{{- $limit := .Site.Config.Services.RSS.Limit -}}
{{- if ge $limit 1 -}}
{{- $pages = $pages | first $limit -}}
{{- end -}}
{{- printf "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>" | safeHTML }}
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>{{ if eq  .Title  .Site.Title }}{{ .Site.Title }}{{ else }}{{ with .Title }}{{.}} on {{ end }}{{ .Site.Title }}{{ end }}</title>
    <link>{{ .Permalink }}</link>
    <description>Recent content {{ if ne  .Title  .Site.Title }}{{ with .Title }}in {{.}} {{ end }}{{ end }}on {{ .Site.Title }}</description>
    <generator>Hugo -- gohugo.io</generator>{{ with .Site.LanguageCode }}
    <language>{{.}}</language>{{end}}{{ with .Site.Author.email }}
    <managingEditor>{{.}}{{ with $.Site.Author.name }} ({{.}}){{end}}</managingEditor>{{end}}{{ with .Site.Author.email }}
    <webMaster>{{.}}{{ with $.Site.Author.name }} ({{.}}){{end}}</webMaster>{{end}}{{ with .Site.Copyright }}
    <copyright>{{.}}</copyright>{{end}}{{ if not .Date.IsZero }}
    <lastBuildDate>{{ .Date.Format "Mon, 02 Jan 2006 15:04:05 -0700" | safeHTML }}</lastBuildDate>{{ end }}
    {{- with .OutputFormats.Get "RSS" -}}
    {{ printf "<atom:link href=%q rel=\"self\" type=%q />" .Permalink .MediaType | safeHTML }}
    {{- end -}}
    {{ range $pages }}
    <item>
      <title>{{ .Title }}</title>
      <link>{{ .Permalink }}</link>
      <pubDate>{{ .Date.Format "Mon, 02 Jan 2006 15:04:05 -0700" | safeHTML }}</pubDate>
      {{ with .Site.Author.email }}<author>{{.}}{{ with $.Site.Author.name }} ({{.}}){{end}}</author>{{end}}
      <guid>{{ .Permalink }}</guid>
      <description>{{ .Content | html }}</description>
    </item>
    {{ end }}
  </channel>
</rss>

部署

我的博客放在github上,部署的话非常简单,只要把生成的public目录推送到github即可. 可以借助刘家财的部署脚本:

 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
#!/usr/bin/env bash

# If a command fails then the deploy stops
set -e

printf "\033[0;32mDeploying updates to GitHub...\033[0m\n"

rm -rf public/*

# Build the project.
hugo # if using a theme, replace with `hugo -t <YOURTHEME>`

# Go To Public folder
cd public

# Add changes to git.
git add -A .

# Commit changes.
msg="rebuilding site $(date)"
if [ -n "$*" ]; then
	msg="$*"
fi
git commit -m "$msg"

# Push source and build repos.
git push origin master