Appearance
GitHub Actions 自动发布 VitePress:适合个人博客的轻量 CI/CD 方案
如果你的博客项目已经放在 GitHub 上,那么最省心的一条路,通常不是自己额外搭 Jenkins,而是先用 GitHub Actions 把自动构建和自动发布跑起来。
对个人博客来说,这套方案最大的优点是:
- 足够轻
- 配置集中
- 上手成本低
先说结论
像 VitePress 这种静态站点,GitHub Actions 很适合做下面这条流水线:
text
push 到 main -> 安装依赖 -> 构建站点 -> 通过 rsync 或 scp 同步到服务器 -> 线上 curl 校验如果你只是想把博客稳定发布出去,而不是立刻搭一整套企业级平台,这个方案往往已经很够用。
一、先确认这条自动发布链路的前提
在真正写 workflow 之前,最好先确认下面几件事已经稳定:
- 站点本地可以正常
npm run docs:build - 服务器已经能稳定托管静态文件
- Nginx 的线上目录已经固定
- SSH 登录和发布用户权限已经确认
如果这些前提还没稳,先把部署链路手工跑通,后面再自动化会更顺。
对当前这套技术站来说,更贴近实际的发布目录是:
text
/mydata/nginx/html/mumu-wiki也就是说,workflow 真正要同步的是 wiki 这套静态站,而不是把整个 /mydata/nginx/html 根目录一起覆盖。
二、为什么个人博客优先考虑 GitHub Actions
1. 仓库和流水线配置天然放在一起
项目代码在 GitHub,流水线配置也在仓库里:
text
.github/workflows/deploy.yml这意味着:
- 配置跟着项目走
- 换电脑不影响
- 项目迁移时也更直观
2. 不需要先维护一台 Jenkins
对个人项目来说,先维护博客本身已经够花时间了。
如果再多维护一个 CI 平台,负担会明显变重。
3. 和 VitePress 的构建模型很契合
VitePress 发布本来就是:
- 安装依赖
- 构建静态文件
- 同步到静态目录
这类流程天然适合放进 Actions。
三、一条最小可用流水线通常怎么设计
1. 触发条件
最常见是:
- 推送到
main - 手动触发
例如:
yaml
on:
push:
branches:
- main
workflow_dispatch:2. 构建步骤
Node 项目推荐尽量固定版本并使用干净安装:
yaml
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
- run: npm ci
- run: npm run docs:build3. 发布步骤
构建完成后,把 docs/.vitepress/dist 同步到服务器。
更推荐 rsync,因为它能:
- 覆盖变更文件
- 删除线上旧文件
- 保持目录一致
四、一个更接近实战的示例
yaml
name: deploy-vitepress
on:
push:
branches:
- main
workflow_dispatch:
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
- name: install dependencies
run: npm ci
- name: build site
run: npm run docs:build
- name: setup ssh key
run: |
mkdir -p ~/.ssh
echo "${{ secrets.DEPLOY_SSH_KEY }}" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
ssh-keyscan -H ${{ secrets.DEPLOY_HOST }} >> ~/.ssh/known_hosts
- name: backup current site
run: |
ssh ${{ secrets.DEPLOY_USER }}@${{ secrets.DEPLOY_HOST }} \
'ts=$(date +%Y%m%d-%H%M%S) && mkdir -p /mydata/nginx/backups/$ts && cp -a /mydata/nginx/html/mumu-wiki /mydata/nginx/backups/$ts/'
- name: sync dist
run: |
rsync -av --delete docs/.vitepress/dist/ \
${{ secrets.DEPLOY_USER }}@${{ secrets.DEPLOY_HOST }}:/mydata/nginx/html/mumu-wiki/
- name: smoke check
run: |
ssh ${{ secrets.DEPLOY_USER }}@${{ secrets.DEPLOY_HOST }} \
'curl -k --max-time 10 --resolve wiki.mumuxiaojia.site:443:127.0.0.1 https://wiki.mumuxiaojia.site/ && \
curl -k --max-time 10 --resolve wiki.mumuxiaojia.site:443:127.0.0.1 https://wiki.mumuxiaojia.site/posts/first-post >/dev/null'这份示例的核心点有三个:
- 先备份当前 wiki 站点
- 再用
rsync --delete保持线上目录和构建产物一致 - 最后直接从 HTTPS 子域名入口做校验
五、Secrets 应该怎么放
不要把下面这些信息写死在仓库里:
- 服务器 IP
- SSH 私钥
- 用户名
- 发布路径中的敏感变量
更稳妥的做法是把它们放进 GitHub Actions Secrets,例如:
DEPLOY_HOSTDEPLOY_USERDEPLOY_SSH_KEY
这样至少能做到:
- 不把敏感信息提交进仓库
- 更换服务器时修改成本更低
六、为什么我推荐加“备份 + 校验”
很多个人项目会把流水线简化成:
- 构建成功
- 直接覆盖线上
这虽然能用,但缺两个关键动作。
1. 备份
如果这次发布后发现页面异常,最好能快速回到上一版。
2. 校验
发布成功不等于页面可访问。
至少建议补:
- 首页
curl -I - 关键栏目页
curl -I - 一篇代表性文章页校验
七、对当前个人技术站,最容易忽略的两个边界
1. 不要把发布路径写成 Nginx 根目录
如果你的服务器上不只一个站点,直接同步到 /mydata/nginx/html/ 根目录风险很大。
更稳的做法是像现在这样,把 wiki 子站固定在:
bash
/mydata/nginx/html/mumu-wiki这样即使同机还有别的站点,也不容易误覆盖。
2. 校验入口最好走真实 HTTPS 子域名
如果只在服务器里 curl http://127.0.0.1/,有时会命中默认站点,不能真正代表线上访问结果。
更稳的方式是:
- 指定子域名
- 指定 HTTPS
- 直接校验代表性页面
这样更接近真实用户访问链路。
八、常见坑
1. 线上文件没删干净
如果只是 scp 复制,旧文件可能会残留。
VitePress 资源名带 hash,一旦旧文件残留过多,排查时会很混乱。
2. Node 版本不一致
本地能构建,不代表 Actions 环境就一定能构建。
所以 Node 版本最好明确指定。
3. 依赖安装不稳定
在流水线里优先用 npm ci,比 npm install 更适合可重复构建。
4. 发布成功但浏览器还是旧内容
这种情况常见于:
- 浏览器缓存
- CDN 缓存
- 页面资源缓存
5. 构建成功,但校验路径没有覆盖文章页
首页能打开,不代表文章页和专题页都正常。
所以 smoke check 至少建议覆盖:
- 首页
- 一个专题页
- 一篇文章页
九、和站点搭建、HTTPS 这两步怎么衔接
如果把整个个人技术站专题串起来看,更推荐的顺序是:
- 先手工把 VitePress 站点跑起来
- 再补域名和 HTTPS
- 最后把构建和发布自动化
这样一旦自动发布失败,你更容易判断问题到底出在:
- 构建
- SSH 权限
- 发布路径
- Nginx 与 HTTPS
对应的上一篇和前置文章可以一起看:
所以发布后最好先用服务器本机访问再判断。
十、它的边界在哪里
GitHub Actions 很适合:
- 个人博客
- 小型官网
- 文档站
- 轻量前后端项目
但如果后面你要做:
- 多环境复杂审批
- 大量团队协作
- 很细的权限分层
那 GitLab CI、Jenkins 或更完整的平台会更合适。
一句话总结
对个人博客来说,GitHub Actions 最大的价值不是“高级”,而是用最小复杂度把构建、发布和校验稳定串起来。
如果你现在已经把 VitePress 跑起来了,那下一步最值得补的自动化,往往就是它。