利用 GitHub Actions 自动部署 Hexo 到 GitHub Pages 并通过 WebHook 安全更新服务器

利用 GitHub Actions 自动部署 Hexo 到 GitHub Pages 并通过 WebHook 安全更新服务器

XuanRan Lv2

利用 GitHub Actions 自动部署 Hexo 到 GitHub Pages 并通过 WebHook 安全更新服务器

最近想恢复写博客,然后就在在折腾 Hexo,我一直希望能够实现自动化部署,实现我博客内容更新后自动构建自动部署到我的服务器中*。最初尝试过直接用 FTP 上传网站,但问题是FTP需要开放额外的端口,而且 FTP 本身也存在安全隐患。于是我决定尝试通过 GitHub Actions 打包 Hexo 站点,然后推送到 GitHub Pages,同时通过 WebHook 通知服务器下载最新压缩包更新网站目录。整个流程安全稳定,因此记录。

下面我就分享整个流程的实现方法,包括每个命令和配置的详解。


一、为什么不用 FTP

起初我考虑用 FTP 上传 Hexo 生成的 public 文件夹到服务器。理论上这是最简单的方式,但实践中有几个问题:

  1. 安全性问题
    FTP 需要开放 21 端口,密码传输容易被抓包,而且需要开放额外的传输端口。即使使用 FTPS,也要额外配置证书。
  2. 维护复杂
    每次部署都需要判断哪些文件被删除、哪些文件新增,FTP 本身不能轻松做到增量同步。
  3. 效率问题
    上传大量静态文件容易耗时,如果网络不稳定,部署可能中断。

所以我决定走另一条路线:GitHub Actions + GitHub Pages + WebHook 下载压缩包到服务器。这样服务器只需要访问 GitHub 或加速下载地址,省去 FTP 的安全和端口问题。

我最开始还不知道FTP必须得开放额外的数据传输端口…


二、GitHub Actions 自动打包 Hexo 并发布 GitHub Pages

在仓库根目录下新建 .github/workflows/deploy.yml,此文件将被Github Action自动识别为任务:

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
name: Deploy Hexo to GitHub Pages and FTP

on:
workflow_dispatch:
push:
branches:
- master

permissions:
contents: write

jobs:
build:
runs-on: ubuntu-latest

steps:
- name: Checkout source
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20

- name: Install dependencies
run: |
npm install -g hexo-cli
npm install

- name: Build Hexo
run: |
hexo clean
hexo generate

# --- 部署到 GitHub Pages ---
- name: Deploy to GitHub Pages
env:
TOKEN: ${{ secrets.HEXO_TOKEN }}
run: |
cd public
git init
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add .
git commit -m "Deploy from GitHub Actions"
git branch -M gh-pages
git push --force "https://${{ secrets.HEXO_TOKEN }}@github.com/xuanrandev/XuanRanDev.github.io.git" gh-pages

# 通知宝塔 WebHook
- name: Notify BaoTa WebHook
run: |
echo "Notify WebHook..."
curl -X GET "${{ secrets.WEBHOOK_ADDRESS }}"

关键点解析

  • actions/checkout@v4:检出仓库代码。
  • actions/setup-node@v3:安装指定版本 Node.js。
  • Build Hexo:生成静态站点到 public
  • git init + git add + git commit + git push:把生成的内容推送到 gh-pages 分支,实现 GitHub Pages 发布(记得修改XuanRanDev.github.io这个值为你想要部署的仓库名称)。
  • curl 通知 WebHook:GitHub Actions 执行完成后调用服务器 WebHook。

通过这种方式,服务器不需要开放 FTP 端口,也不需要用户直接上传文件。

记得在Github仓库的设置中添加HEXO_TOKEN 和 WEBHOOK_ADDRESS 这两个值


三、服务器端部署脚本

在服务器上,我创建了一个专门的部署用户,因为直接通过root执行命令会有很大的安全风险:

1
2
3
4
5
# 创建不可登录系统用户 hexodeploy
useradd -r -s /usr/sbin/nologin -d /home/hexodeploy hexodeploy
mkdir -p /home/hexodeploy
chown hexodeploy:hexodeploy /home/hexodeploy
chmod 750 /home/hexodeploy
  • -r 表示系统账户,不会分配登录 shell。
  • -s /usr/sbin/nologin 禁止 SSH 或 su 登录。
  • /home/hexodeploy 是家目录,用于存放脚本和临时文件。

然后部署目录设置为该用户可写:

1
2
chown -R hexodeploy:hexodeploy /www/wwwroot/blog.example.com
chmod -R 755 /www/wwwroot/blog.example.com

部署脚本 deploy.sh

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
#!/bin/bash
set -e # 遇到错误立即退出

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

LOG_DATE=$(date '+%Y-%m-%d %H:%M:%S')

if [ ! -f .env ]; then
echo "❌ [${LOG_DATE}] 错误: .env 文件不存在"
exit 1
fi

source .env

TMP_DIR="$SCRIPT_DIR/tmp"
ZIP_FILE="$TMP_DIR/site.zip"

echo "========================================="
echo "🚀 Hexo 自动部署启动"
echo "执行用户: $(whoami)"
echo "时间: ${LOG_DATE}"
echo "下载地址: $ZIP_URL"
echo "目标目录: $TARGET_DIR"
echo "========================================="

mkdir -p "$TMP_DIR"

echo "⬇️ 下载站点压缩包..."
if ! wget -q -O "$ZIP_FILE" "$ZIP_URL"; then
echo "❌ 下载失败: $ZIP_URL"
rm -rf "$TMP_DIR"
exit 1
fi

echo "🧹 清理旧文件(保留 .user.ini)..."
find "$TARGET_DIR" -mindepth 1 ! -name '.user.ini' -exec rm -rf {} + || true

echo "📦 解压新文件..."
unzip -q "$ZIP_FILE" -d "$TMP_DIR"

EXTRACTED_DIR=$(find "$TMP_DIR" -mindepth 1 -maxdepth 1 -type d | head -n 1)
if [ -z "$EXTRACTED_DIR" ]; then
echo "❌ 解压失败:未找到内容"
rm -rf "$TMP_DIR"
exit 1
fi

echo "🚚 拷贝到目标目录..."
cp -rf "$EXTRACTED_DIR"/* "$TARGET_DIR/"

rm -rf "$TMP_DIR"

if [ "$NGINX_RELOAD" = "true" ]; then
systemctl reload nginx 2>/dev/null || echo "⚠️ Nginx 重载失败或未启用"
fi

LOG_DATE_END=$(date '+%Y-%m-%d %H:%M:%S')
echo "✅ [${LOG_DATE_END}] 部署完成!"
echo "========================================="

.env文件

1
2
3
4
5
6
7
8
9

# 网站部署目录
TARGET_DIR=/www/wwwroot/blog.example.com

# ZIP 下载地址
ZIP_URL=xxxxx

# 部署完成后是否自动重载 Nginx
NGINX_RELOAD=false

说明

  1. wget 下载:直接从 GitHub 或加速地址获取压缩包,无需 FTP。

    我这里是把打包好的博客静态页面上传到了我的Github Page,然后Github支持直接下载代码压缩包,再套一个Github加速,就不用担心网络问题了

  2. find … ! -name ‘.user.ini’:确保不会删除 PHP 配置文件。

  3. cp -rf:覆盖旧站点文件。

注意,请将你的域名进行替换


调用方式

在WebHook 配置中,若收到请求直接执行以下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#!/bin/bash

echo "接收到部署请求"
ZIP_URL="${1:默认下载地址}"
echo "下载地址: $ZIP_URL"

# 切换到 hexodeploy 用户执行真正部署
sudo -u hexodeploy bash /home/hexodeploy/deploy.sh


if [ $? -eq 0 ]; then
echo "部署成功"
exit 0
else
echo "部署失败"
exit 1
fi

  • sudo -u hexodeploy:切换到部署用户执行

WebHook 本身是支持传递参数过来,若传递的有参数则使用参数的下载地址,我这里直接使用默认了,记得替换上面的默认下载地址


四、效果

然后你就可以专注于博客的内容了,而无需关注打包和部署了,下面是我的运行效果

执行日志

一旦你往仓库push新的代码了,就会触发自动构建和更新

执行效果

然后博客内容就会进行更新

博客内容更新

  • 标题: 利用 GitHub Actions 自动部署 Hexo 到 GitHub Pages 并通过 WebHook 安全更新服务器
  • 作者: XuanRan
  • 创建于 : 2025-11-08 14:15:00
  • 链接: https://blog.xuanran.cc/2025/11/08/利用 GitHub Actions 自动部署 Hexo 到 GitHub Pages 并通过 WebHook 安全更新服务器/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。