最近正好想要使用HEXO在部署一次自己的博客,但每次都要受到提交运行太麻烦了,好在GitHub Actions可以帮助我们自动化这个过程,而且目前还是免费的。
最终目标
- Hexo 源码仓库可以保持私有(文章源文件、主题配置、脚本都在这里维护)
- 对外访问的站点仓库保持公开(只存放构建后的静态文件,给 GitHub Pages 发布)
- 只要我 push 源码,GitHub Actions 就自动构建并把
public/推送到 Pages 仓库
下面按这个思路把整套流程梳理一遍(也适用于"源码公开/私有"两种情况)。
方案概览
仓库职责
- 源码仓库(例如:
hexo-blog,可私有)- 存放:Hexo 项目源码(
source/_posts、主题、_config.yml、package.json等) - 负责:GitHub Actions 构建与部署
- 存放:Hexo 项目源码(
- Pages 仓库(用户站点):
<你的用户名>.github.io(必须公开)- 存放:Hexo 生成的静态文件(
public/目录里的 HTML/CSS/JS/图片等) - 负责:开启 GitHub Pages 对外访问
- 存放:Hexo 生成的静态文件(
自动化流程
- 你在源码仓库写文章并 push
- GitHub Actions 触发:安装依赖 →
hexo generate生成静态站点到public/ - Actions 把
public/推送到 Pages 仓库指定分支(通常main) - GitHub Pages 直接从 Pages 仓库发布
费用与计费简单说明
具体计费情况可以查看 GitHub Actions 计费页面 了解详情。
- 公共仓库的 Actions 通常免费
- 私有仓库使用 GitHub 托管运行器有免费额度,超出可能计费(分钟数/存储与账号计划相关)
- 缓存/工件存储也可能产生占用,删除后不会继续累积
对个人博客来说,构建频率不高、站点不大时,通常不会超过免费额度。想更省:减少触发频率、开启依赖缓存、不要上传大工件。
前置准备
- GitHub 账号
- 本地安装 Node.js(建议 LTS)与 Git
- 一个可正常本地运行的 Hexo 项目(本文默认依赖已在
package.json中)
创建并配置两个仓库
1) 创建 Pages 仓库(公开)
- 新建仓库:仓库名必须是
<你的用户名>.github.io - 仓库可见性:Public
- Settings → Pages
- Source:Deploy from a branch
- Branch:
main/(root)(推荐)
2) 创建/准备源码仓库(可私有)
把 Hexo 源码提交到源码仓库即可,文章建议放在:
source/_posts/(Hexo 默认文章目录)
配置 GitHub Actions:构建后推送到另一个仓库
关键点是:Actions 在源码仓库里构建完,需要"跨仓库写入" Pages 仓库。
常见做法:
- Fine-grained PAT(更直观)
- Deploy Key(SSH,适合更严格的权限隔离)
这里使用 Fine-grained PAT。
1) 创建 Fine-grained PAT
GitHub:Settings → Developer settings → Personal access tokens → Fine-grained tokens:
- 选择只授权给你的 Pages 仓库(
<你的用户名>.github.io) - Permissions 至少给:
- Contents: Read and write
- 生成后复制保存(只显示一次)
2) 在源码仓库添加 Secrets
源码仓库 → Settings → Secrets and variables → Actions → New repository secret:
PAGES_TOKEN:填写上一步生成的 PAT
3) 添加工作流文件
在源码仓库创建文件:.github/workflows/deploy-to-pages-repo.yml。
如果你使用 pnpm(推荐锁定依赖,构建更可复现),可以用下面这个版本(把仓库名替换成你自己的):
yaml
name: Build and Deploy Hexo
on:
push:
branches:
- main
workflow_dispatch:
permissions:
contents: read
concurrency:
group: hexo-deploy
cancel-in-progress: true
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout source repo
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "20.19.0"
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 10
run_install: false
- name: Get pnpm store directory
id: pnpm-cache
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
- name: Setup pnpm cache
uses: actions/cache@v4
with:
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build site
run: |
pnpm run clean
pnpm run build
- name: Deploy to Pages repo
uses: peaceiris/actions-gh-pages@v4
with:
personal_token: ${{ secrets.PAGES_TOKEN }} #.后面的值要与你设置的Token名词保持一致
external_repository: {你的公开仓库地址}
publish_branch: main
publish_dir: ./public
commit_message: "deploy: ${{ github.sha }}"
说明:
concurrency用来避免连续 push 时部署互相覆盖publish_dir指向 Hexo 生成目录publiccache: pnpm+--frozen-lockfile用来保证依赖安装更稳定
Pages 仓库设置检查
在 <你的用户名>.github.io 仓库里确认:
- Settings → Pages
- Source:Deploy from a branch
- Branch:与你工作流推送的分支一致(上面示例是
main/(root))
第一次部署通常需要等待一会儿生效,地址是:
https://<你的用户名>.github.io
主题管理:如何"直接链接主题仓库"并保持可更新
很多 Hexo 主题都有独立仓库。你当然可以"链接它"来及时更新,但要先想清楚:你是否需要改主题源码,或者仅仅是想跟进主题版本。
方案 A:通过包管理器安装主题(更省心,推荐)
不少主题提供了 npm/pnpm 安装方式(主题包名与主题名不一定相同,以主题文档为准):
bash
pnpm add <theme-package>
然后在 Hexo 的 _config.yml 里设置:
yml
theme: <theme-name>
更新主题也很简单:只要更新依赖并提交 pnpm-lock.yaml,CI 就能拿到同一版本的主题,且你可以随时升级:
bash
pnpm update <theme-package>
适用场景:
- 你希望主题跟随上游更新,但不直接改主题源码
- 你更在意构建可复现(锁文件控制版本)
方案 B:用 Git submodule 链接主题仓库(可跟进上游,但要把 CI 配齐)
如果你想把主题代码放在 themes/ 目录下,同时又希望能"指向上游仓库",可以用 submodule(主题更新由你控制,避免每次构建都隐式漂移到最新提交):
bash
git submodule add <theme-repo-url> themes/<theme-dir>
然后 _config.yml 里使用对应主题目录名:
yml
theme: <theme-dir>
更新主题时:
bash
git submodule update --remote --merge
但要注意:CI 必须把 submodule 一起拉下来,否则构建时主题目录会是空的/不完整,生成结果就会异常(后面会讲到 0KB 问题)。
方案 C:把主题源码直接提交到源码仓库(最直观、也最稳)
如果你频繁改主题源码,并且不想引入 submodule 复杂度,可以直接把 themes/<theme-dir> 当作普通目录提交到源码仓库:
- 优点:CI 不需要任何 submodule 配置,最不容易踩坑
- 缺点:跟进上游更新需要你手动合并/同步
日常发布流程
- 写文章:
source/_posts/xxx.md - 本地预览(可选):
bash
pnpm run clean
pnpm run build
pnpm run server
- push 到源码仓库
main - 等 Actions 跑完后访问站点即可
常见问题排查
Actions 成功但页面不更新
- 检查 Pages 设置的分支/目录是否与工作流一致(例如
main/(root)) - 检查 Pages 仓库是否收到最新提交(Actions 会推送一个 commit)
- 等待 Pages 缓存刷新或清理浏览器缓存
部署报 403 / permission denied
- PAT 权限不足:确认 token 对 Pages 仓库有 Contents 写权限
- PAT 过期/撤销:重新生成并更新
PAGES_TOKEN external_repository写错:必须是用户名/用户名.github.io
Actions 成功但页面白屏:Pages 仓库里很多 JS/HTML 变成 0KB
这个问题看起来像"部署把文件写坏了",但本质通常发生在更早的"构建阶段":
- Hexo 构建时拿不到主题 layout(常见日志是大量
No layout: index.html等) - 产物生成出来就是空/异常文件
- 部署动作只是把
public/原样推送到 Pages 仓库,所以会看到index.js等文件是 0KB
根因(一个非常常见的坑)
主题目录 themes/xxx 被当成"独立 Git 仓库"或 "Git submodule(gitlink)"在管理,但 CI 并没有把主题内容拉下来:
- checkout 没有开启 submodules
- 或
.gitmodules缺失/不完整(例如 submodule 没有 url)
最终导致 CI 工作区里主题为空/不完整,Hexo 渲染时找不到 layout,生成就会异常。
修复方式(推荐顺序)
-
如果你不需要改主题源码:优先用"包管理器安装主题"(上面方案 A),最稳。
-
如果你要改主题源码:
- 不要把主题目录留成 "gitlink 子模块"但不配置 CI
- 要么把主题作为普通目录提交到源码仓库(主题目录里不要有
.git) - 要么继续用 submodule,但工作流必须拉取 submodule
以工作流为例,至少要做到两点:
(1) CI 在构建前确认主题存在(以 themes/<theme-dir> 为例)
yaml
- name: Verify theme is present
shell: bash
run: |
set -euo pipefail
test -f themes/<theme-dir>/layout/layout.ejs
test -f themes/<theme-dir>/layout/index.ejs
(2) CI 在部署前确认产物不为空(避免把空产物推到 Pages)
yaml
- name: Verify build output is not empty
shell: bash
run: |
set -euo pipefail
test -f public/index.html
test -s public/index.html
zero_core_count=$(find public -type f \( -name '*.html' -o -name '*.js' -o -name '*.xml' -o -name '*.json' \) -size 0 -print | wc -l | tr -d ' ')
if [ "${zero_core_count}" != "0" ]; then
echo "Found ${zero_core_count} 0-byte core files (*.html/*.js/*.xml/*.json) under public/. Showing first 50:"
find public -type f \( -name '*.html' -o -name '*.js' -o -name '*.xml' -o -name '*.json' \) -size 0 -print | head -n 50
exit 1
fi
如果你选择 submodule 方案,还需要在 checkout 时开启 submodules(否则主题仍然拉不下来):
yaml
- uses: actions/checkout@v4
with:
submodules: recursive
引用
- https://docs.github.com/zh/billing/concepts/product-billing/github-actions
- https://docs.github.com/zh/pages
- https://github.com/peaceiris/actions-gh-pages
- https://zhuanlan.zhihu.com/p/60578464
- https://www.cnblogs.com/xango/p/18909673
- https://zhuanlan.zhihu.com/p/161969042
- https://zhangmengwei.com/2025/07/18/05-hexo-cicd-automation/