作为一个已经入行了一年多的(老)技术人,维护一个看得过去的个人博客是很有必要的。
刚学编程的时候,还开源过一个基于 Flask 和 MongoDB 的博客项目,但后面就没怎么使用和维护了,除开主观上的懒,还因为:
- 自建博客绕不开主机和域名,这是一笔持续的经济成本。国内的主机往往是第一年割肉第二年宰猪,国外的主机访问延迟很高。
- 国内主机更麻烦的是还需要定期备案,第一个博客就是因为备案到期中断了。
- 博客功能的实现技术难度不大,开始还有一些新鲜感,有了工作经验后就很难有兴趣继续维护了。
刚好最近有写一些文章的打算,决定找个简单、省事(最后发现并没有)且不花钱的路子把博客再搞起来,一番研究后,选择了生成静态站点发布到 GitHub Pages 的方案。
工作流
整个方案的流程大致如下:
- 用 Markdown 格式写作文章。
- 使用生成器将 markdown 文件转换成静态站点。
- 将生成的站点内容推送到 GitHub 并发布。
写 markdown 没啥好说的,什么编辑器都可以,我一直用的是 Typora。
静态站点生成器我选择了 Hugo,原因是最近刚好在学 Go,此外还有 Gatsby、Jekyll、Hexo等很多选项。
接下来要做的工作是生成静态站点并通过 GitHub Pages 发布。
生成静态站点
使用 Hugo 生成静态博客站点非常简单,具体的步骤和用法可以参考官方文档的 Quick Start。下面简单介绍下整个过程:
-
安装 Hugo。
macOS
下可以直接使用homebrew
安装:brew install hugo
。 -
创建一个新的站点。这会生成一个特定目录结构的项目文件夹,用来维护所有的站点内容。假设我们想把它命名为
hugo-blog
,则使用以下命令创建并切换到该目录,后续的操作和命令都会在这个根目录下执行:hugo new site hugo-blog cd hugo-blog
-
安装一个主题。这一步是必需的,否则会因为缺少基础模板无法生成站点。安装主题有 3 种方式,以
eureka
主题为例:-
直接下载主题的压缩文件,将解压得到的文件夹重命名为主题名称
eureka
放到themes/
目录下。 -
通过
git submodule
安装:git init git submodule add https://github.com/wangchucheng/hugo-eureka.git themes/eureka
-
通过
Hugo Modules
安装(这种方式要求本机安装有 Go 1.12 及以上版本,且只有部分主题支持):hugo mod init <module_name>
<module_name>
并不重要,随便起个名字就行。
安装后需要启用主题,方法是将主题名称写入到根目录下的默认配置文件
config.yml
中:echo 'theme = "eureka"' >> config.toml
如果是通过
Hugo Modules
安装,需要把主题名称替换成模块名称:echo 'theme = "github.com/wangchucheng/hugo-eureka"' >> config.toml
-
-
添加一篇文章。可以直接在
content/posts
目录下创建 markdown 文件,但需要手动写入一些元信息,因此推荐使用 Hugo 自带的命令:hugo new posts/my-first-post.md
,添加的文件会以如下元信息开头:--- title: "My First Post" date: 2019-03-26T08:47:11+01:00 draft: true ---
在下方接着写入文章内容即可。注意此时该文件为草稿状态,写作完成后需要改成
draft: false
才能部署。 -
启动 Hugo 预览服务器。Hugo 可以启动一个 Web 服务器,同时构建站点内容到内存中并在检测到文件更改后重新渲染,方便我们在开发环境实时预览对站点所做的更改。
hugo server -D
添加
-D
选项以输出草稿状态的文章,执行成功后可以通过http://localhost:1313/
访问站点。 -
自定义主题配置。站点的配置项默认保存在根目录的
config.toml
文件中,配置项较多时通常会用主题提供的预设配置文件来替换该文件,还可以通过config
目录加多个文件的方式来组织配置。默认配置文件如下:baseURL = "http://example.org/" # 发布地址,由主机名以及路径组成 languageCode = "en-us" # 语言代码,中文可以设置为"zh" title = "My New Hugo Site" # 站点标题
这一步应该是整个过程中最麻烦也是最容易出问题的一步,视乎你选择的主题与想要的功能不同,需要自定义的配置项也不同,数量从几个到上百个不等。有些主题会有详细的文档解释配置过程,有些则一笔带过只能自己去摸索,配置较多时相互间可能还有依赖关系,最好更改一个配置就刷新一次页面确认下结果。
建议起步时一切从简,花大把时间搞各种花里胡哨的样式和功能,还不如多写几篇文章。
-
构建静态页面。站点配置成我们理想的效果之后就可以构建静态页面了:
hugo -D
添加
-D
选项可以在结果中包括草稿内容,默认情况下静态页面会输出到根目录下的public
文件夹中。
通过 GitHub Pages 发布
这一步 Hugo 的官方文档同样在 Host on GitHub 中进行了详细的介绍,并且还很贴心的提供了自动化操作的 Shell 脚本。
有两种方式:
-
通过个人主页发布:必须创建一个
<USERNAME>.github.io
仓库来托管生成的静态内容,发布后的域名为https://<USERNAME>.github.io
。 -
通过项目主页发布:可以随意创建
<PROJECT_NAME>
仓库,发布后的域名为https://<USERNAME>.github.io/<PROJECT_NAME>
。
视选择的发布方式不同,我们需要将 config.yml
中的 baseUrl
设置为不同的值。
通过个人主页发布
建议非特殊情况下使用第 1 种方式,原因是许多主题都不能很好的支持第 2 种,具体来说是将 config.toml
的 baseURL
设置为含子路径的地址时,不能正确的处理所有资源的构建位置。我尝试了 3 个主题,均遭遇了不同的问题:
-
eureka
: 构建失败,提示 :Error: Error building site: POSTCSS: failed to transform "css/eureka.css" (text/css): resource "css/waynerv.github.io/css/eureka.css_fc3f76d7bee2760c3a903059afc3d9b2" not found in file cache
-
LoveIt
: 构建成功,但除主页以外的文章、分类和标签的页面均提示 404。 -
MemE
: 构建成功,但文章中插入的图片加载 404(放在同一文件夹的favicon
却能正常展示)。
这个问题我在 eureka
项目提交了 issue,开发者回复可能是 Hugo 本身的机制所导致,并已经在 Hugo 论坛中提出了此问题,有兴趣的可以关注后续进展。
发布步骤
-
在 GitHub 创建个人主页仓库,仓库名称必须设置为
<USERNAME>.github.io
,这个仓库仅存放生成的静态内容。 -
在 GitHub 创建一个项目仓库
hugo-blog
并添加为我们本地项目文件夹的远程仓库。这个仓库用来维护站点配置和原始的文章内容。 -
假设我们在已经通过上文的步骤在
public
文件夹中生成了想发布的静态内容,运行:git submodule add -b main https://github.com/<USERNAME>/<USERNAME>.github.io.git public
在
public
目录中创建一个 git 子模块,之后这个目录将以https://github.com/<USERNAME>/<USERNAME>.github.io
作为远程仓库。 -
确保配置文件中的
baseUrl
已经设置为了<USERNAME>.github.io
。 -
Hugo 为我们接下来的部署操作提供了一个自动化的 Shell 脚本:
#!/bin/sh # 任一步骤执行失败都会终止整个部署过程 set -e printf "\033[0;32mDeploying updates to GitHub...\033[0m\n" # 构建静态内容 hugo # if using a theme, replace with `hugo -t <YOURTHEME>` # 切换到 Public 文件夹 cd public # 添加更改到 git git add . # 提交更改 msg="rebuilding site $(date)" if [ -n "$*" ]; then msg="$*" fi git commit -m "$msg" # 推送到远程仓库 git push origin main
将如上内容保存到 deploy.sh
文件中,并执行 chmod +x deploy.sh
为其添加可执行权限。接着执行部署脚本:
./deploy.sh
大功告成!稍等几分钟就可以在 https://<USERNAME>.github.io
看到我们的个人博客了。
通过 GitHub Actions 自动部署
目前我们的「创作-发布」流程如下:
-
在项目仓库编辑原始内容并进行版本管理。
-
执行自动脚本生成静态站点并推送到个人主页仓库完成发布。
这套流程已经很流畅,但还有一些改进空间:我们可以使用 GitHub Actions,在每次向远程的项目仓库推送原始内容更改时自动执行第 2 步进行发布。
GitHub 上有许多这类自动化部署任务的开源 Actions 项目,我们选择了其中一个简单易用的 GitHub Actions for Hugo。具体的操作步骤截图和详细配置项可以查看该项目的 README。下面简单介绍下配置过程:
-
在项目文件夹中添加目录和文件:
.github/workflows/gh-pages.yml
,gh-pages.yml
文件内容如下:name: github pages on: push: branches: - main # 每次推送到 main 分支都会触发部署任务 jobs: deploy: runs-on: ubuntu-18.04 steps: - uses: actions/checkout@v2 with: submodules: true # Fetch Hugo themes (true OR recursive) fetch-depth: 0 # Fetch all history for .GitInfo and .Lastmod - name: Setup Hugo uses: peaceiris/actions-hugo@v2 with: hugo-version: '0.79.1' extended: true - name: Build run: hugo --minify - name: Deploy uses: peaceiris/actions-gh-pages@v3 with: deploy_key: ${{ secrets.ACTIONS_DEPLOY_KEY }} external_repository: <USERNAME>/<USERNAME>.github.io # 替换成上文所创建的个人主页仓库 publish_branch: main publish_dir: ./public
这个文件所定义的
workflow
基于项目仓库运行,但我们需要将运行过程生成的静态文件推送到个人主页仓库<USERNAME>.github.io
完成发布,因此在Deploy
任务中按照文档的 Deploy to external repositoryexternal_repository
一节做了专门的配置。 -
在本地生成 SSH 部署密钥:
ssh-keygen -t rsa -b 4096 -C "$(git config user.email)" -f gh-pages -N "" # 将在当前目录生成如下密钥文件: # gh-pages.pub (公钥) # gh-pages (私钥)
-
在 GitHub 分别进入项目仓库和个人主页仓库的
Settings
页面:-
将公钥
gh-pages.pub
作为Secret
添加到项目仓库,并设置Name
为ACTIONS_DEPLOY_KEY
。 -
将私钥
gh-pages
作为Deploy Key
添加到个人主页仓库,并设置为Allow write access
。
-
接下来我们测试一下效果。
在本地做一些更改,预览效果后提交并推送,然后在项目仓库的 GitHub Actions 页面检查相应的 workflow
是否运行成功。不出意外的话,很快个人主页仓库将新增一个由该 workflow
创建的提交,访问个人博客页面也会发现页面已经更新。
个人体验
由于先后选择的 3 个主题均遭遇了上述发布地址不能包含子路径的问题,我在基本按照官方文档操作的前提下,依然花了超过 10 个小时才把博客上线,浪费了很多时间在配置主题以及寻找问题的解决方案上。本以为选择了一个简单快捷的省心方案,结果还是免不了过程的一顿踩坑和折腾。
虽然搭建博客的流程不算省心,但我所遇到的这些问题也算是个例。一切准备就绪后,我们可以像写代码一样写博客,对文章修改提交即自动发布,也不需要考虑博客的样式、后台功能及主机维护等问题,对提升写作效率会有所帮助,可以省下来很多的时间和精力,综合来看体验还是不错的。