🚀 小白搭建个人博客教程(二)—— 自动化部署

上一篇教程里,我们从零开始搭建好了一个博客,也成功把它发布到了网上。但每次写完文章,都要手动敲一串命令去部署,说实话……还是有点烦的😅

1
hexo clean && hexo generate && hexo deploy

那有没有一种方式,写完文章往 GitHub 上一推,博客就自动更新了呢?

答案是:有的!用 GitHub Actions 就能实现全自动部署。今天我们就来把这套流程配好,以后写博客就只需要专注于写作本身 ✍️

在开始之前,先搞明白我们要做什么

先来理解一下整个自动化流程,其实就像一条”流水线”:

1
2
3
4
5
6
7
8
9
10
11
你写好 Markdown 文章

推送到 GitHub(git push)

GitHub Actions 自动触发(相当于一个云端机器人)

机器人帮你执行 hexo generate(把文章编译成网页)

机器人帮你把生成的网页部署到 GitHub Pages

博客自动更新,读者就能看到新文章了 🎉

简单来说:你只管写文章、推代码,剩下的事情交给机器人。


第一步:理解”两个分支”的概念

这一步不需要你操作什么,但理解它非常重要,不然后面会一头雾水。

我们会在同一个 GitHub 仓库里使用两个分支

分支 存什么 谁来管
source 博客源码(你写的 Markdown 文章、配置文件等) 来管
main 生成的静态网页(HTML/CSS/JS) 机器人自动管

🤔 为什么要分两个分支?

你可以这样理解:source 分支是你的**”厨房”,放着原材料(Markdown 文章、配置文件);main 分支是“餐桌”**,放着做好的菜(编译好的网页)。

GitHub Pages(也就是展示你博客的服务)只看”餐桌”上有什么。而你平时只需要在”厨房”里干活——写文章、改配置。至于”把菜端上桌”这件事,GitHub Actions 会帮你自动完成。

所以这两个分支不需要合并,它们的内容完全不同——一个是原材料,一个是成品,各管各的。


第二步:更新 .gitignore 文件

在推送源码之前,我们需要告诉 Git:”有些文件不需要上传”。

用编辑器打开项目根目录下的 .gitignore 文件(如果没有就新建一个),把内容改成这样:

1
2
3
4
5
6
7
.DS_Store
Thumbs.db
db.json
*.log
node_modules/
public/
.deploy*/

逐行解释一下:

  • .DS_Store / Thumbs.db:macOS 和 Windows 系统自动生成的缓存文件,没用
  • db.json:Hexo 的数据库缓存文件,每次 hexo generate 都会重新生成
  • *.log:日志文件,不需要上传
  • node_modules/:依赖包文件夹,体积巨大(几百 MB),别人拿到你的源码后执行 npm install 就能自动下载,所以不用上传
  • public/:Hexo 生成的静态文件,机器人会在云端重新生成,不需要上传
  • .deploy*/:Hexo 部署时产生的临时文件夹

第三步:创建 GitHub Actions 工作流文件

这一步是整个自动化的核心——我们要写一个”剧本”,告诉 GitHub 的机器人该怎么帮我们部署。

首先,在项目根目录下创建文件夹和文件:

1
2
3
4
📁 你的博客项目/
└── 📁 .github/
└── 📁 workflows/
└── 📄 deploy.yml ← 就是这个文件

💡 .github/workflows/ 是 GitHub Actions 的固定路径,GitHub 会自动识别这个目录下的 .yml 文件并执行。

然后,在 deploy.yml 里填入以下内容:

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
name: Deploy Hexo Blog

on:
push:
branches:
- source # 当 source 分支有推送时触发

jobs:
build-and-deploy:
runs-on: ubuntu-latest

steps:
# 1. 检出源码
- name: Checkout source
uses: actions/checkout@v4
with:
fetch-depth: 0 # 获取完整的 git 历史记录

# 2. 恢复文件修改时间(使用 Git 提交历史)
- name: Restore file modification time
run: |
git config core.quotePath false
git ls-files | while IFS= read -r file; do
time=$(git log -1 --format='%ai' -- "$file")
if [ -n "$time" ]; then
touch -d "$time" "$file"
fi
done

# 3. 设置 Node.js 环境
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'

# 4. 安装依赖
- name: Install dependencies
run: npm ci

# 5. 生成静态文件
- name: Generate static files
run: npx hexo clean && npx hexo generate

# 6. 部署到 GitHub Pages
- name: Deploy to GitHub Pages
uses: peaceiris/actions-gh-pages@v4
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./public
publish_branch: main
user_name: 'github-actions[bot]'
user_email: 'github-actions[bot]@users.noreply.github.com'
commit_message: 'Deploy: ${{ github.event.head_commit.message }}'

别被这一大段吓到,我来逐块解释:

触发条件(on 部分)

  • push + branches: source:只有当你往 source 分支推送代码时,才会触发这个流程。推到其他分支不会有反应。

运行环境(runs-on

  • ubuntu-latest:GitHub 提供的免费云服务器,用的是最新版 Ubuntu 系统。相当于 GitHub 借你一台电脑用一会儿。

步骤详解(steps 部分)

  1. Checkout source:把你的源码从 GitHub 下载到这台云服务器上。fetch-depth: 0 表示获取完整的历史记录(这一点很重要,后面第 2 步要用到)。
  2. Restore file modification time(⚠️ 重要!):恢复每个文件的真实修改时间。详细解释见下面的「踩坑提醒」。
  3. Setup Node.js:在云服务器上安装 Node.js 20 版本。cache: 'npm' 会缓存依赖,下次构建更快。
  4. Install dependencies:执行 npm ci 安装依赖。这和你本地的 npm install 类似,但更适合自动化环境(更干净、更快)。
  5. Generate static files:先清理旧文件,再生成新的静态网页,和你本地手动执行的命令一模一样。
  6. Deploy to GitHub Pages:把 public/ 文件夹里生成的内容,推送到 main 分支。
    • github_token:这是 GitHub 自动提供的令牌,你不需要额外配置,它让机器人有权限往你的仓库推送代码。
    • publish_dir: ./public:告诉机器人要部署的是 public 文件夹(Hexo 生成的静态文件都在这里)。
    • publish_branch: main:部署目标是 main 分支。

⚠️ 踩坑提醒:为什么需要”恢复文件修改时间”这一步?

Hexo 默认用文件的系统修改时间(mtime)来生成文章的”更新于”时间。但 Git 有个特点:它不会保存文件的原始修改时间。当 GitHub Actions 的机器人执行 checkout(检出代码)时,所有文件的修改时间都会被设为检出那一刻的时间——也就是说所有文章的”更新于”会变成同一个时间!

第 2 步的脚本通过 git log 查询每个文件最后一次被提交的时间,再用 touch -d 把文件的修改时间恢复回去,这样 Hexo 就能拿到正确的更新时间了。

其中 git config core.quotePath false 这一行也很关键——Git 默认会把中文文件名转义成 \345\237\272... 这样的乱码格式,导致 touch 命令找不到文件。关闭这个选项后,文件名就会正常输出中文了。

这也是为什么第 1 步的 fetch-depth: 0 不能省略——它确保拉取了完整的 Git 历史,git log 才能查到每个文件真正的最后修改时间。


第四步:在 GitHub 上配置权限

工作流文件写好了,但还需要在 GitHub 上开一个”门禁”,让机器人有权限往仓库里推送代码。

  1. 打开你的 GitHub 仓库页面(比如 https://github.com/你的用户名/你的用户名.github.io
  2. 点击上方的 Settings(设置)
  3. 在左侧菜单找到 Actions → General
  4. 往下滚动,找到 Workflow permissions(工作流权限)
  5. 选择 Read and write permissions
  6. Save 保存

📌 这一步的作用是:允许 GitHub Actions 机器人往你的仓库写入内容(也就是把生成的网页推送到 main 分支)。默认情况下机器人只有”读”的权限,没有”写”的权限。


第五步:确认 GitHub Pages 的设置

确保 GitHub Pages 知道该从哪个分支读取网页内容。

  1. 还是在仓库的 Settings 页面
  2. 在左侧菜单找到 Pages
  3. Build and deployment 下面:
    • Source 选择 Deploy from a branch
    • Branch 选择 main,目录选 / (root)
  4. Save 保存

这一步告诉 GitHub Pages:”请从 main 分支的根目录读取网页文件来展示。”


第六步:推送源码到 GitHub

万事俱备,现在把你的博客源码推到 GitHub 上吧!

打开终端(Git Bash),进入你的博客项目目录,然后依次执行以下命令:

1
2
# 进入你的博客项目目录
cd D:\MyProjection\My_Blog
1
2
# 初始化 Git 仓库(如果你之前已经 git init 过了,会提示 Reinitialized,没关系)
git init

git init 的作用是在当前文件夹里创建一个 Git 仓库,让 Git 开始管理这个项目的所有文件变动。

1
2
# 关联远程仓库
git remote add origin https://github.com/你的用户名/你的用户名.github.io.git

git remote add origin 的意思是:给这个本地项目”绑定”一个远程 GitHub 仓库,origin 是远程仓库的别名(约定俗成就叫这个名字)。

⚠️ 如果提示 fatal: remote origin already exists.,说明之前已经绑过了,可以跳过这一步,或者先执行 git remote remove origin 再重新绑定。

1
2
# 创建并切换到 source 分支
git checkout -b source

git checkout -b source 做了两件事:创建一个名为 source 的新分支,并切换到这个分支上。以后你的所有操作都在 source 分支上进行。

1
2
# 把所有文件加入暂存区
git add .

git add . 的意思是:把当前目录下所有改动过的文件都标记为”准备提交”(. 代表当前目录下的所有文件)。被 .gitignore 列出的文件会自动排除。

1
2
# 提交到本地仓库
git commit -m "Initial commit: Hexo blog source"

git commit -m "消息" 的意思是:把暂存区的文件正式保存到本地仓库,-m 后面引号里的内容是这次提交的说明,方便以后回看。

1
2
# 推送到 GitHub 的 source 分支
git push -u origin source

git push 就是把本地的代码上传到 GitHub。-u origin source 的意思是:推送到远程仓库 originsource 分支,并且设置为默认推送目标(以后直接 git push 就行了,不用再写后面那一串)。

推送完成后,去你的 GitHub 仓库页面看一下:

  1. 切换到 Actions 标签页,你应该能看到一个正在运行的工作流(小黄圈转啊转的🟡)
  2. 等它变成绿色的勾 ✅,就说明部署成功了!
  3. 打开你的博客网址 https://你的用户名.github.io,确认一下页面是否正常

🟡 第一次运行可能需要 1-2 分钟,耐心等待就好。


以后发布新文章的流程

配置完成后,以后每次写新文章,只需要三步:

1
2
3
4
5
6
7
8
9
# 1️⃣ 创建新文章
npx hexo new "你的文章标题"

# 2️⃣ 用编辑器打开 source/_posts/你的文章标题.md,愉快地写作......

# 3️⃣ 写完后推送到 GitHub,自动部署!
git add .
git commit -m "新文章:你的文章标题"
git push

就这么简单!推送后等 1-2 分钟,刷新博客页面就能看到新文章了 🎉

再也不用手动 hexo clean && hexo generate && hexo deploy 了~


💡 常见问题

Q1:source 和 main 两个分支需要合并吗?

不需要,也不能合并。

这两个分支虽然在同一个仓库里,但它们的内容完全不同

  • source 存的是源码(Markdown 文件、配置文件等),是”原材料”
  • main 存的是生成的网页(HTML/CSS/JS),是”成品”

如果你把 source 合并到 main,GitHub Pages 就找不到正确的网页文件了,博客会直接挂掉。

它们是平行存在、互不干扰的关系。你只需要关心 source 分支,main 分支交给机器人自动管理就好。

Q2:为什么用同一个仓库的两个分支,而不是两个仓库?

用同一个仓库更方便:

  • 管理简单:一个仓库搞定所有事情
  • 权限方便:GitHub Actions 天然有权限操作同一个仓库的所有分支,不需要额外配置 Token
  • 社区惯例:这是 GitHub Pages 博客最常见的做法

Q3:部署失败了怎么办?

  1. 去 GitHub 仓库的 Actions 页面查看失败的工作流
  2. 点进去看具体的错误日志
  3. 常见原因:
    • 权限问题:检查第四步的 Workflow permissions 是否设置成了 Read and write
    • 依赖安装失败:检查 package.json 是否正确
    • 分支名不对:确认工作流文件里的分支名和你实际推送的分支名一致

Q4:我能不能在本地预览文章再推送?

当然可以!推送之前先在本地看看效果:

1
npx hexo server

然后打开浏览器访问 http://localhost:4000 就能预览了。确认没问题后再推送。


🎉 总结

恭喜你!现在你的博客已经实现了全自动部署。整个流程就是:

写文章 → git push → 自动部署 → 博客更新

你需要做的,就只是专注于写作本身了。

希望这篇教程对你有帮助,如果遇到问题欢迎留言交流~