本文最后更新于 2026年2月1日 晚上
背景 对于本网站,我是这么进行管理的:
所有文章(markdown),写在source/_posts目录下。项目作为一个node应用,整体用git进行版本管理,托管在自己的服务器上(也可以是github)。这些只是管理了项目的文章内容和配置,不负责网站的展示。
在项目根目录配置 _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
这样虽然也能部署成功,但是还是有点繁琐,因为:
如果我不commit代码,我换一台电脑并pull代码后,我没法同步最新的文章内容。
部署我又需要运行一遍deploy命令,比较麻烦。
所以,本文就是在探究,如何在commit代码时,自动完成网站的部署。为此,需要先探究一下hexo deploy到底做了什么。以及如何等效地替代 它。
一. hexo deploy (Git 方式) 究竟做了什么? hexo deploy 命令的核心功能是将 Hexo 生成的静态网站文件(位于 public/ 目录下)推送到远程 Git 仓库,从而实现网站的托管和发布。这个过程通常是通过 hexo-deployer-git 插件来完成的。当执行 hexo d 时,hexo-deployer-git 插件在幕后完成了以下几步:
初始化/进入隐藏目录 :在项目根目录的 .deploy_git 文件夹中维护一个独立的 Git 仓库。
内容复制 :将本地 public/ 文件夹(即 hexo g 生成的所有静态 HTML、JS、CSS)全部拷贝到 .deploy_git。
Git 提交 :在 .deploy_git 中执行 git add -A 和 git commit -m "Site updated: YYYY-MM-DD"。
强制推送 :执行 git push -f 将这些静态文件强制推送到你在 _config.yml 中配置的远程仓库和分支(比如 GitHub Pages 的 main 分支)。
核心本质 :hexo deploy 是一个将“生成结果,也就是/public文件夹”同步到“远程托管平台”的传输工具 。它利用 Git 的版本控制特性来确保远程服务器上的文件是最新的。那么,如果在git裸仓库所在的服务器上,生成一个post-receive钩子,将项目复制到工作区文件夹中,然后在服务器上用脚本完成hexo g生成静态文件,并将生成的文件复制到Nginx的站点目录,就可以省去hexo d这一步。
二. 等效替换思路
在服务器上建立裸仓库,用于接收项目的推送
在该仓库中,配置post-receive钩子,收到新的提交之后,将与本地一模一样的项目文件检出到工作区目录
用脚本,在工作区目录里面执行如下操作:
进行一个幂等的包安装
执行hexo clean清理旧文件
执行hexo g生成静态文件
将生成的public/目录内容复制到Nginx的站点目录
三. 具体实现细节 经过实践之后,我的实现细节如下:
1. 在服务器上建立相应目录 1 2 3 4 mkdir -p /srv/[项目/bare] mkdir -p /srv/[项目/source] mkdir -p /srv/[项目/www]
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"