本文汇总 Hexo 博客的部署运维要点,涵盖服务器端自动化部署(Termux proot-distro Ubuntu)与 Windows 本地端部署脚本。

目录


1. 服务器端自动化部署(Termux Ubuntu)

1.1 脚本整体功能

适用于 Termux proot-distro Ubuntu 环境,全自动流程:

  1. DNS 异常自动修复
  2. Gitee 私有仓库 pull / clone 源码
  3. 智能判断依赖变更,不每次重复安装依赖
  4. 保留 hexo clean && hexo generate 完整编译
  5. 强制校验编译结果,杜绝空目录报错
  6. 安全同步 public 静态文件到 Nginx 站点目录
  7. 智能 Nginx 管理:运行则重载、未运行则启动

1.2 本次解决的核心报错(重点)

报错:

cp: cannot stat '/xxx/public/*': No such file or directory

根本原因:

hexo clean 会清空 public 目录,如果 hexo generate 编译异常/警告中断,public 为空,* 通配符匹配失败直接报错。

最终两套防护方案:

  1. 编译后强制校验 public/index.html,不存在直接终止脚本
  2. 复制命令由 public/* 改为 public/.,空目录也不会报错

1.3 依赖优化机制(关键改进)

不再无脑 pnpm install,采用 md5 哈希对比:

  • 监控文件:package.json、pnpm-lock.yaml

  • 文件无变化 → 跳过安装,速度极快

  • 文件变更(插件新增/升级/版本变动)→ 自动执行 pnpm install

  • 首次运行 → 自动安装依赖

  • 缓存文件:zzcblog-source/.pkg.md5sum

  • 手动强制重装依赖:rm .pkg.md5sum 再运行脚本

1.4 Nginx 智能启停逻辑

  • 检测到 Nginx 正在运行 → nginx -s reload 平滑重载
  • Nginx 未运行 → nginx -c /etc/nginx/nginx.conf 2>/dev/null 静默启动

1.5 已关闭功能

  • 关闭 Git 自动安装(环境预装 Git)
  • 关闭强制 pnpm install,改为按需检测

1.6 最终完整脚本(可直接 cat 生成)

cat > deploy-blog.sh <<'EOF'
#!/bin/bash
# ============================================================
# Hexo 完整自动化部署脚本【最终稳定版】
# 功能:Gitee私有仓库拉取源码 → 按需pnpm依赖 → Hexo完整编译 → Nginx部署
# 运行环境:Termux proot-distro Ubuntu
# 前置依赖:git、pnpm、nodejs、nginx 已预装
# ============================================================

set -euo pipefail

# ===================== 配置区域 =====================
REPO_URL="${REPO_URL:-git@gitee.com:wszack/zzcblog-source.git}"
REPO_BRANCH="${BRANCH:-main}"
CLONE_DIR="/root/zzcblog-source"
WEB_TARGET="/var/www/hexo-blog"
LOG_FILE="/var/log/deploy-blog.log"
NGINX_CONF="/etc/nginx/nginx.conf"
PKG_HASH_FILE="${CLONE_DIR}/.pkg.md5sum"
# ==============================================================

# 日志输出
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOG_FILE"
}

# 脚本结束回调
cleanup() {
log "脚本执行结束"
}
trap cleanup EXIT

# 自动修复DNS
fix_dns_if_needed() {
if ! ping -c 1 -W 2 mirrors.aliyun.com >/dev/null; then
log "DNS解析异常,切换公共DNS"
echo "nameserver 8.8.8.8" > /etc/resolv.conf
echo "nameserver 223.5.5.5" >> /etc/resolv.conf
fi
}

log "=============================================="
log " Hexo 全自动部署脚本启动"
log "=============================================="

fix_dns_if_needed

# 1. 更新/克隆源码仓库
log "[1/4] 处理源码仓库"
if [ -d "$CLONE_DIR/.git" ]; then
log "仓库已存在,执行 git pull 更新"
cd "$CLONE_DIR"
git stash push -m "auto-stash before pull" 2>/dev/null || true
git fetch origin
git checkout "$REPO_BRANCH"
git pull origin "$REPO_BRANCH"
git stash pop 2>/dev/null || true
log "源码更新完成"
else
log "首次克隆私有仓库"
rm -rf "$CLONE_DIR"
git clone -b "$REPO_BRANCH" "$REPO_URL" "$CLONE_DIR"
log "克隆完成"
fi

# 2. 按需安装依赖(文件哈希对比)
log "[2/4] 检测依赖文件变更"
cd "$CLONE_DIR"
CURRENT_HASH=$(md5sum package.json pnpm-lock.yaml 2>/dev/null || echo "missing")

if [ -f "$PKG_HASH_FILE" ]; then
LAST_HASH=$(cat "$PKG_HASH_FILE")
if [ "$CURRENT_HASH" != "$LAST_HASH" ]; then
log "依赖已变更,执行 pnpm install"
pnpm install
echo "$CURRENT_HASH" > "$PKG_HASH_FILE"
else
log "依赖无变动,跳过安装"
fi
else
log "首次初始化依赖,执行 pnpm install"
pnpm install
echo "$CURRENT_HASH" > "$PKG_HASH_FILE"
fi

# 3. 完整编译 Hexo(保留 clean)
log "[3/4] 执行 Hexo 清理+编译"
cd "$CLONE_DIR"
hexo clean && hexo generate

# 强制校验编译结果,防止空目录复制报错
if [ ! -f "${CLONE_DIR}/public/index.html" ];then
log "❌ Hexo 编译失败,无 index.html,终止部署"
exit 1
fi
log "✅ Hexo 编译成功"

# 4. 同步静态文件到 Nginx 站点目录
log "[4/4] 同步静态资源至 Nginx 目录"
mkdir -p "$WEB_TARGET"
cp -r "${CLONE_DIR}/public/." "${WEB_TARGET}/"
chown -R root:root "$WEB_TARGET"

# Nginx 智能启停/重载
if pgrep -x "nginx" > /dev/null; then
log "Nginx 运行中,执行重载"
nginx -s reload
else
log "Nginx 未运行,启动 Nginx"
nginx -c "${NGINX_CONF}" 2>/dev/null
fi

log ""
log "✅ 博客部署完成!访问地址:http://127.0.0.1:8080"
log "=============================================="
EOF

1.7 使用命令

生成脚本 + 授权 + 运行:

chmod +x deploy-blog.sh
./deploy-blog.sh

1.8 手动重置依赖缓存(备用)

rm /root/zzcblog-source/.pkg.md5sum

1.9 脚本最终特性汇总

  • ✅ 保留 hexo clean 完整清理编译
  • ✅ 解决 public 空目录 cp 报错
  • ✅ 智能依赖检测,不重复安装
  • ✅ 编译失败自动终止,不破坏站点
  • ✅ Nginx 智能启停/重载
  • ✅ 自动 DNS 修复
  • ✅ 完整日志记录
  • ✅ proot Ubuntu 权限完美适配

2. Windows 本地部署脚本

2.1 版本 1(未设置全局 Git,使用环境变量方案)

@echo off
echo 清理旧静态文件(加载 _config.github.yml 配置)...
set GIT_AUTHOR_NAME=zzc-blog
set GIT_AUTHOR_EMAIL=2542434555@qq.com
set GIT_COMMITTER_NAME=zzc-blog
set GIT_COMMITTER_EMAIL=2542434555@qq.com

call hexo clean --config _config.yml,_config.github.yml
if %errorlevel% neq 0 (
echo 清理文件失败!
pause
exit /b %errorlevel%
)

echo 正在生成静态文件(加载 _config.github.yml 配置)...
call hexo g --config _config.yml,_config.github.yml
if %errorlevel% neq 0 (
echo 静态文件生成失败!
pause
exit /b %errorlevel%
)

echo 开始部署至Github Pages...
call hexo d --config _config.yml,_config.github.yml
if %errorlevel% neq 0 (
echo 部署失败!
pause
exit /b %errorlevel%
)

echo ✅ 部署完成!
pause

2.2 版本 2(已配置全局 Git,精简版本)

@echo off
echo 清理旧静态文件(加载 _config.github.yml 配置)...
call hexo clean --config _config.yml,_config.github.yml
if %errorlevel% neq 0 (
echo 清理文件失败!
pause
exit /b %errorlevel%
)

echo 正在生成静态文件(加载 _config.github.yml 配置)...
call hexo g --config _config.yml,_config.github.yml
if %errorlevel% neq 0 (
echo 静态文件生成失败!
pause
exit /b %errorlevel%
)

echo 开始部署至Github Pages...
call hexo d --config _config.yml,_config.github.yml
if %errorlevel% neq 0 (
echo 部署失败!
pause
exit /b %errorlevel%
)

echo ✅ 部署完成!
pause

关于 hexo d 身份报错(Author identity unknown)的详细解决方案,见《Git 配置笔记》。


3. 部署故障处理

  1. 部署报错优先执行清理缓存:
    rmdir /s /q .deploy_git
  2. yaml 配置报错:禁止使用 Windows 记事本编辑,避免隐形特殊字符、中文空格、Tab 制表符
  3. Hexo 编译参数格式说明:
    hexo g --config _config.yml,_config.github.yml
    主配置在前,扩展配置在后,后者覆盖同名 url 等参数