服务器自部署Hexo网站实现自动化构建

本文最后更新于 2026年2月1日 晚上

背景

对于本网站,我是这么进行管理的:

  1. 所有文章(markdown),写在source/_posts目录下。项目作为一个node应用,整体用git进行版本管理,托管在自己的服务器上(也可以是github)。这些只是管理了项目的文章内容和配置,不负责网站的展示。
  2. 在项目根目录配置 _config.yml,配置好网站信息、主题、插件等,以及,和部署相关的信息(deploy部分),然后,配置服务器,具体可参考Hexo的部署(使用git)。当我觉得写好内容时,我会先清理内容,然后生成新静态文件并推送到远程仓库完成网站的部署。
1
2
3
4
5
6
7
8
9
10
11
12
# 1. 清理旧文件
hexo clean

# 2. 生成新静态文件
hexo generate

# 3. 部署到远程仓库
hexo deploy

# 2和3可以合并成一步
hexo g -d

这样虽然也能部署成功,但是还是有点繁琐,因为:

  1. 如果我不commit代码,我换一台电脑并pull代码后,我没法同步最新的文章内容。
  2. 部署我又需要运行一遍deploy命令,比较麻烦。

所以,本文就是在探究,如何在commit代码时,自动完成网站的部署。为此,需要先探究一下hexo deploy到底做了什么。以及如何等效地替代它。

一. hexo deploy (Git 方式) 究竟做了什么?

hexo deploy 命令的核心功能是将 Hexo 生成的静态网站文件(位于 public/ 目录下)推送到远程 Git 仓库,从而实现网站的托管和发布。这个过程通常是通过 hexo-deployer-git 插件来完成的。当执行 hexo d 时,hexo-deployer-git 插件在幕后完成了以下几步:

  1. 初始化/进入隐藏目录:在项目根目录的 .deploy_git 文件夹中维护一个独立的 Git 仓库。
  2. 内容复制:将本地 public/ 文件夹(即 hexo g 生成的所有静态 HTML、JS、CSS)全部拷贝到 .deploy_git
  3. Git 提交:在 .deploy_git 中执行 git add -Agit commit -m "Site updated: YYYY-MM-DD"
  4. 强制推送:执行 git push -f 将这些静态文件强制推送到你在 _config.yml 中配置的远程仓库和分支(比如 GitHub Pages 的 main 分支)。

核心本质hexo deploy 是一个将“生成结果,也就是/public文件夹”同步到“远程托管平台”的传输工具。它利用 Git 的版本控制特性来确保远程服务器上的文件是最新的。那么,如果在git裸仓库所在的服务器上,生成一个post-receive钩子,将项目复制到工作区文件夹中,然后在服务器上用脚本完成hexo g生成静态文件,并将生成的文件复制到Nginx的站点目录,就可以省去hexo d这一步。

二. 等效替换思路

  1. 在服务器上建立裸仓库,用于接收项目的推送
  2. 在该仓库中,配置post-receive钩子,收到新的提交之后,将与本地一模一样的项目文件检出到工作区目录
  3. 用脚本,在工作区目录里面执行如下操作:
    1. 进行一个幂等的包安装
    2. 执行hexo clean清理旧文件
    3. 执行hexo g生成静态文件
    4. 将生成的public/目录内容复制到Nginx的站点目录

三. 具体实现细节

经过实践之后,我的实现细节如下:

1. 在服务器上建立相应目录

1
2
3
4
mkdir -p /srv/[项目/bare]  # 裸仓库,[]内自行定义
mkdir -p /srv/[项目/source] # 工作区
mkdir -p /srv/[项目/www] # Nginx站点目录

2. 在裸仓库中配置post-receive钩子

以下内容是个例子,有详细注释,根据实际情况做修改:

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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
#!/bin/bash
# 如果是用nvm管理的node版本,建议明确指定nvm路径,避免环境变量问题
# 1. 明确定义 HOME 变量,确保 NVM 能找到配置文件
export HOME="/home/[你的用户名]"

# 2. 明确定义 NVM 路径,不要依赖 $HOME
export NVM_DIR="/home/[你的用户名]/.nvm"

# 3. 加载 NVM(这步会自动把 node 和 npx 加入 PATH)
if [ -s "$NVM_DIR/nvm.sh" ]; then
. "$NVM_DIR/nvm.sh"
else
echo ">>>>>> [Error] 找不到 nvm.sh,请检查 NVM_DIR 路径是否正确"
exit 1
fi

# 遇到任何错误立即停止脚本执行,防止由于中间步骤失败导致部署了错误的版本
set -e

# --- STEP1:定义变量和函数,方便后面代码做引用 ---
# 路径定义
GIT_DIR="/srv/[项目]/bare"
WORK_TREE="/srv/[项目]/source"
WWW_DIR="/srv/[项目]/www"
MD5_FILE="$GIT_DIR/pnpm-lock.md5"

export PATH=$PATH:/usr/local/bin:/usr/bin

# 全局变量,记录总启动时间
GLOBAL_START=$(date +%s)

# 时间统计函数,前面2个参数是步骤名称和开始时间
function log_time() {
local step_name=$1
local start_time=$2
local end_time=$(date +%s)
local duration=$((end_time - start_time))
echo ">>>>>> [Time] $step_name 耗时: ${duration}s"
}

echo ">>>>>> [Deploy] 启动自动化构建流程..."

# --- STEP2 检出源码 ---
STEP_START=$(date +%s)
echo ">>>>>> [Step 1] 正在同步源码到工作区..."
mkdir -p $WORK_TREE
# 使用 -f 强制检出,确保干净
git --work-tree=$WORK_TREE --git-dir=$GIT_DIR checkout -f
log_time "源码检出" $STEP_START

cd $WORK_TREE

# --- STEP3 幂等依赖安装,我用的是pnpm,如果是npm,这里要做适当修改 ---
STEP_START=$(date +%s)
if [ -f "pnpm-lock.yalm" ]; then
CURRENT_MD5=$(md5sum pnpm-lock.yaml | cut -d ' ' -f 1)
OLD_MD5=$( [ -f "$MD5_FILE" ] && cat "$MD5_FILE" || echo "" )

if [ "$CURRENT_MD5" != "$OLD_MD5" ]; then
echo ">>>>>> [Step 2] 检测到 package.json 变更,执行增量安装..."
pnpm install --frozen-lockfile --quiet
echo "$CURRENT_MD5" > "$MD5_FILE"
log_time "依赖安装" $STEP_START
else
echo ">>>>>> [Step 2] 依赖项无变更,跳过安装阶段。"
fi
else
pnpm install --quiet
fi

# --- 4. Hexo 清理与生成 ---
# 建议:如果不是更换了主题或大规模修改配置,clean 这一步可以按需执行
# 这里为了稳妥默认保留
STEP_START=$(date +%s)
echo ">>>>>> [Step 3] 正在生成静态文件 (hexo clean && g)..."
pnpm exec hexo clean > /dev/null
pnpm exec hexo generate
log_time "Hexo 生成" $STEP_START

# --- 5. 同步至 Nginx 目录 ---
STEP_START=$(date +%s)
echo ">>>>>> [Step 4] 正在使用 rsync 同步至站点目录..."
# 确保目标目录存在
mkdir -p $WWW_DIR
# --delete 确保服务器上没有残留的旧文件
rsync -avz --delete public/ $WWW_DIR/ > /dev/null
log_time "站点同步" $STEP_START

# --- 总结 ---
GLOBAL_END=$(date +%s)
TOTAL_DURATION=$((GLOBAL_END - GLOBAL_START))
echo ">>>>>> [Success] 部署圆满完成!"
echo ">>>>>> [Total] 总计总耗时: ${TOTAL_DURATION}s"



服务器自部署Hexo网站实现自动化构建
https://www.xiebingyuan.cn/2025/12/6269ede92416/
作者
bingyuan
发布于
2025年12月29日
更新于
2026年2月1日
许可协议