我已经记不得这是第几次重建博客了,虽然基于git仓库的备份,所有的md文件都还在,但是node_modules以及其他的环境配置会因为各种各样的原因损坏(换电脑,系统重装,迁移系统等),有必要重新梳理一下博客搭建的过程。

下面的内容主要是在windows上和linux(WSL2)上的本地部署,以及在云服务器和Github Page的远程部署,并不涉及具体的Hexo主题以及配置细节。

生成与部署逻辑

Hexo是一个基于NodeJS的静态博客框架。首先梳理一下主要的工作逻辑:

  • 在本地博客文件夹中,下载必要的Hexo组件并配置
  • source\_posts目录中添加博客文件(markdown格式)
  • 生成并推送
    • 执行hexo generate生成相应的静态网页,生成的静态网页及相关资源都会存放在public\目录下
    • 执行hexo deploy部署到远程仓库,实质上是将public\目录下的所有内容打包复制到一个git本地仓库,然后提交到指定的远程仓库
  • 本地预览:执行hexo server启动本地预览,Hexo会在本地部署一个网站,通过http://localhost:4000访问。在本地启动期间,Hexo会持续监视博客文件变动并自动更新,无须重启服务器。但是对于配置文件的修改必须重启服务器才能生效。
  • 清理本地缓存:执行hexo clean清理本地缓存(包括public\和db.json文件),这可以有效避免一些错误。

日常写博客的流程:

  1. 启动本地预览:hexo s
  2. 新建或修改博客文档
  3. 清理本地缓存:hexo clean
  4. 生成并推送,完成博客的更新:hexo g, hexo d

由于Hexo生成的是静态网页,实际上我们只需要把public\的所有内容复制到网站对应的目录下即可,然后配置Nginx确保文件夹可以被访问即可,通常这个复制过程使用git完成,但这并不是必须的。

本地搭建

准备工作

安装git,这没什么好说的。

安装nodejs(Latest LTS Version: 20.11.1),对于windows很简单,对于Linux则略有不同,因为Ubuntu的apt提供的Nodejs版本太低,这里采用nvm给普通用户安装Nodejs,在普通用户家目录执行下面的命令安装nvm

1
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash

这里给bash添加了一段配置,需要重新登录才能生效。

查看所有可供安装的node版本

1
nvm ls-remote

这里选择Latest LTS Version: 20.11.1安装

1
nvm install v20.11.1

查看本地已安装的node

1
nvm ls

使用指定版本的node

1
nvm use v20.11.1

安装pandoc(3.1.12.2),因为需要它来提供LaTeX渲染,在windows上直接安装即可,Linux通过apt安装还是一样的问题——版本太低,安装过程如下: 首先通过pandoc的GitHub仓库下载pandoc-3.1.12.2-1-amd64.deb文件并复制到家目录中,然后执行

1
sudo dpkg -i pandoc-3.1.12.2-1-amd64.deb

测试一下安装成功

1
pandoc -v

从零搭建

新建BlogBase/文件夹,在其中本地安装hexo-cli

1
npm i hexo-cli

新建BlogBase/Blog文件夹,在其中执行Hexo初始化(注意hexo必须在一个空文件夹里面进行初始化)

1
hexo init

由于hexo-cli不是全局安装的,存在找不到hexo命令的问题,对于windows可以通过临时添加路径到PATH解决,在BlogBase目录下执行

1
$env:Path += "$((Get-Item -Path .\node_modules\.bin -Force).FullName);"

对于Linux可以在Blog目录下添加hexo软链接

1
ln -s ~/blogbase/node_modules/.bin/hexo hexo

使用npm安装butterfly(butterfly主题不会放置在themes文件夹下)

1
npm install hexo-theme-butterfly

在_config.yml中切换主题,本地即可正常测试运行。

在基本功能测试完成之后,就可以安装插件并且进行相应配置,目前使用如下的插件:

  • 本地搜索需要额外下载插件:hexo-generator-search
  • 字数统计需要额外下载插件:hexo-wordcount
  • 为了支持latex,首先需要root安装pandoc,然后卸载自带的hexo-renderer-marked,安装hexo-renderer-pandoc
  • 部署的git插件默认也是没有的,需要安装hexo-deployer-git
  • 一个似乎是支持置顶和隐藏的插件,卸载自带的hexo-generator-index,安装hexo-generator-index-custom
  • 关于外链,安装hexo-filter-nofollow
  • 关于sitemap,安装hexo-generator-sitemap
  • 关于footer,安装hexo-butterfly-footer-beautify

如果npm下载的网络太慢,可以考虑换淘宝镜像源

1
npm config set registry https://registry.npmmirror.com

迁移搭建

如果已经有了Hexo博客的源代码仓库,那就更简单了:在BlogBase目录下安装hexo-cli之后,新建Blog文件夹并直接初始化Git仓库,然后拉取远程仓库的博客源代码仓库即可

1
2
3
git init
git remote add origin <remote-url>
git pull origin main

然后自动下载依赖的插件(基于packages.json和packages-lock.json)

1
npm install

依赖顺利下载完成之后,就可以执行本地测试和进一步的部署了

1
hexo s

package.json

这里记录一下目前的package.json(2024年5月10日)

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
{
"name": "hexo-site",
"version": "0.0.0",
"private": true,
"scripts": {
"build": "hexo generate",
"clean": "hexo clean",
"deploy": "hexo deploy",
"server": "hexo server"
},
"hexo": {
"version": "7.3.0"
},
"dependencies": {
"hexo": "^7.0.0",
"hexo-abbrlink": "^2.2.1",
"hexo-butterfly-footer-beautify": "^1.0.6",
"hexo-deployer-git": "^4.0.0",
"hexo-filter-nofollow": "^2.0.2",
"hexo-generator-archive": "^2.0.0",
"hexo-generator-category": "^2.0.0",
"hexo-generator-feed": "^3.0.0",
"hexo-generator-index-custom": "^1.0.1",
"hexo-generator-search": "^2.4.3",
"hexo-generator-sitemap": "^3.0.1",
"hexo-generator-tag": "^2.0.0",
"hexo-renderer-ejs": "^2.0.0",
"hexo-renderer-pandoc": "^0.4.0",
"hexo-renderer-stylus": "^3.0.0",
"hexo-server": "^3.0.0",
"hexo-theme-butterfly": "^4.13.0",
"hexo-theme-landscape": "^1.0.0",
"hexo-wordcount": "^6.0.1",
"with": "https://registry.npmmirror.com/with/-/with-7.0.2.tgz"
}
}

更新记录

  • 2024年5月10日更新:添加了hexo-abbrlink插件,为博客生成唯一的哈希值链接,不再依赖于路径。

  • 2024年9月2日更新:添加了hexo-generator-feed插件,为博客提供RSS支持。

远程部署

部署插件源码分析

远程部署如果基于Git实现,那么通常使用hexo-deployer-git插件,这个插件不是默认的插件,需要手动安装。(还有一些非Git的方式,Hexo也提供了名为hexo-deployer-xxx的插件) hexo-deployer-git插件需要我们在_config.yml中加入如下形式的配置,这里提供了两个Git仓库的例子

1
2
3
4
5
6
7
deploy:
- type: git
repo: git@tc:blog.git
branch: main
- type: git
repo: git@github.com:<user>/<user>.github.io.git
branch: main

安装并配置好部署插件之后,hexo deploy命令才会有具体的行为,实质等价于如下的命令:(分析depoly.js源码)

  1. 创建或清空.deploy_git\文件夹

  2. public\文件夹复制所有内容到.deploy_git\文件夹

  3. 初始化 Git 仓库

    1
    git init

  4. 添加配置信息

    1
    2
    git config user.name "Your Name"
    git config user.email "your.email@example.com"

  5. 将所有文件添加到暂存区:

    1
    git add -A

  6. 提交

    1
    git commit -m "Site updated: {{ now('YYYY-MM-DD HH:mm:ss') }}"

  7. 推送到远程仓库

    1
    git push -u <remote_repo_url> HEAD:<branch> --force

部署插件其实就做了这些事情,至于通过Git仓库具体部署到了什么地方,与本地的行为无关。 由于我们已经完全拆解了部署插件的行为,完全可以考虑直接自己写一个脚本来代替它,例如

1
2
3
4
5
6
7
8
9
#!/bin/bash

SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
LOCAL_FOLDER_PATH="$SCRIPT_DIR/.deploy_git"
cd "$LOCAL_FOLDER_PATH"

# git remote add origin git@github.com:<user>/<user>.github.io.git

git push -u origin main

在Windows推送时,遇到了git换行符的问题,暂时把safecrlf从true改成warn,目前windows中的.deploy_git的仓库级配置为

1
2
autocrlf = input
safecrlf = warn

云服务器配置

本地 hexo 推送到远程仓库 git@fenglielie.top:/home/git/blog.git,即通过 git 用户登陆云服务器的 ip。 这里需要先在云服务器中创建git用户,给 git 用户设置相应的 ssh 的密钥,并且在本地也设置相应的密钥,对于 git 用户通常还会禁用 shell 登陆。

具体的步骤如下:

  • 第一步,部署到/home/git/blog.git文件夹,这是远程仓库的 git 数据库;
  • 第二步,调用blog.git里面的 hook(具体而言是 hooks/post-receive,也就是推送之后自动执行的动作),hook 的需要添加的内容如下,含义是将仓库的所有内容(也就是静态网站的所有内容,实际上就是public/中的所有内容)在/home/open/blog文件夹中展开
    1
    git --work-tree=/home/open/blog --git-dir=/home/git/blog.git checkout -f

这里涉及到几个目录:

  • /home/git 以及它的子目录(git:git),存放当前云服务器上的所有远程仓库,包括/home/git/blog.git博客仓库;
  • /home/open 目录(root:root),存放了当前云服务器上所有的公开内容;
  • /home/open/blog 目录(git:git),博客网站的主目录,里面存放了所有的网页文件。由于需要通过 git hook 进行读写操作,需要注意让git拥有权限。

注意,这里的blog.git仓库的大小可能随着推送次数增多而膨胀,可以过一段时间直接清空重置,但是要注意维护hooks/post-receive

可以考虑用另一个独立的git仓库备份所有博客及配置,单独调用git push将 hexo 博客以及其它配置整体(不含node_modules/)作为一个 git 仓库推送到远程仓库 git@fenglielie.top:blog_src.git,这个远程仓库不需要设置 hook。

这里仅仅是把通过Hexo生成的静态网站所有内容放在了云服务器上的指定位置,为了让网站可以被外界通过网址成功访问,还需要考虑很多问题: 域名购买,服务器的公网ip与域名解析,SSL证书,腾讯云的防火墙+Linux系统防火墙,Nginx配置等。

Github Page配置

Github Page是Github免费提供的,可以部署静态网站的服务,外界可以通过<user>.github.io访问到网站。(当然也可以改成自定义域名,只需要改一下域名解析即可,这里暂不考虑)

Github Page的实质和上面的云服务器配置是类似的,即本地将Git仓库推送到远程仓库,Github将远程仓库的内容放置在一个网站的根目录下,并且可以通过<user>.github.io访问。 虽然原理类似,但是这个特殊仓库的配置,以及自动部署的过程还是值得注意的。

关于部署的内容,可以指定为仓库的某个分支,还可以进一步指定为某个文件夹。

有两种发布方式:

  • (传统的)推送到特定分支时自动发布站点(使用默认的pages build and deployment工作流)
  • (beta)编写 GitHub Actions 工作流来发布站点(使用自定义工作流)

前者直接把仓库指定分支的所有内容拷贝过去,用于部署网站;后者则基于Github Actions,即提供一个临时的虚拟机,可以在其中对仓库内容执行一些自定义的命令,然后将再部署网站。 目前选择前者即可。

自动化脚本

顺便记录一下我为了简化博客使用流程所使用的自动化脚本,大致分成两类:

  • 备份与提交:提供bash脚本和等价的powershell脚本
    • 备份:backup.shbackup.ps1
    • 推送:deploy.shdeploy.ps1
  • 博客格式检查:使用Python脚本实现
    • 内容格式检查:blogcheck.py,关注列表开头空行,代码结尾空行等影响渲染的格式细节
    • 头部信息检查:headcheck.py,检查博客分类是否与文件系统中的实际位置匹配,分类名称和标签名称的合法性等

几个脚本放在fenglielie/scripts仓库仓库中,便于版本控制。