998 字
5 分钟
用 GitHub Actions 搭建静态博客自动部署流水线

静态博客的发布流程本应是「写完、push、完事」,但很多人还在手动 npm run build、手动 rsync 上传——每次发文都多一步出错的机会。GitHub Actions 可以把这条链路完全自动化:push 到主分支后,构建、部署、通知全部自动完成。本文以一个 Node 系静态站(Astro/Hugo 同理)为例走通全流程。

工作流全貌#

push 到 main
│
▼
GitHub Actions Runner 启动
├─ 1. 拉代码
├─ 2. 装依赖、构建静态产物
├─ 3. 部署产物(rsync 到服务器 / 推到 Pages)
└─ 4. 失败时通知

工作流文件放在仓库的 .github/workflows/deploy.yml:

name: Deploy Blog
on:
push:
branches: [main]
workflow_dispatch: # 允许在网页上手动触发
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: 拉取代码
uses: actions/checkout@v4
- name: 安装 Node
uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
- name: 安装依赖并构建
run: |
npm ci
npm run build
- name: 部署到服务器
uses: burnett01/[email protected]
with:
switches: -avzr --delete
path: dist/
remote_path: /var/www/blog/
remote_host: ${{ secrets.DEPLOY_HOST }}
remote_user: ${{ secrets.DEPLOY_USER }}
remote_key: ${{ secrets.DEPLOY_KEY }}

逐段拆解#

触发器 on:push: branches: [main] 表示推送到 main 分支时触发;workflow_dispatch 加一个手动按钮,改完配置想立刻验证时不用特意造一个 commit。

npm ci 而不是 npm install:CI 环境的铁律。ci 严格按 package-lock.json 安装,保证每次构建依赖版本完全一致;install 可能按 semver 范围悄悄升级某个包,让「本地能过、CI 挂掉」变成悬案。搭配 setup-node 的 cache: npm,依赖下载在多次运行间缓存,构建快很多。

部署环节的安全:服务器地址、用户名、SSH 私钥全部放仓库的 Settings → Secrets and variables → Actions 里,以 ${{ secrets.XXX }} 引用。私钥内容绝不能写进 YAML——这个文件在仓库里是公开的。

--delete 参数让 rsync 删除服务器上多余的旧文件,保持目录与构建产物一致;它对部署目录是安全的,但务必确认 remote_path 没填错——填成 /var/www/ 会把兄弟站点的目录清掉。

为部署准备专用 SSH 密钥#

不要在多个地方复用同一把私钥,给 Actions 单独生成一对:

Terminal window
ssh-keygen -t ed25519 -C "github-actions-blog" -f ~/.ssh/gh-actions-deploy

公钥追加到服务器部署用户的 ~/.ssh/authorized_keys,私钥全文粘贴到仓库 Secret DEPLOY_KEY。这样即便 CI 侧泄露,也只暴露这一把受限密钥,可以随时吊销更换。更严格的做法是在服务器上给该用户配置 command= 前缀限制它只能执行 rsync。

不想维护服务器?推到 GitHub Pages#

如果博客就托管在 GitHub Pages,部署步骤换成官方 Action,连服务器和密钥都不用管:

- name: 配置 Pages
uses: actions/configure-pages@v5
- name: 上传产物
uses: actions/upload-pages-artifact@v3
with:
path: dist/
- name: 部署到 Pages
id: deployment
uses: actions/deploy-pages@v4

(需要在仓库 Settings → Pages 里把来源选为 GitHub Actions。)

让失败可见#

流水线最大的谎言是「绿钩即一切正常」,第二大的谎言是「失败了会有人发现」。加一步失败通知,比如用 curl 调用任意 webhook(Server 酱、Telegram Bot、企业微信均可):

- name: 失败通知
if: failure()
run: |
curl -s "https://api.day.app/${{ secrets.BARK_KEY }}/博客部署失败/${{ github.sha }}?level=critical"

if: failure() 表示仅当前面步骤失败才执行。同理 if: success() 可以发成功确认——初期建议两种都开,跑稳定后只留失败通知,避免消息疲劳。

值得加的两个增强#

  1. 构建前内容校验:在 build 步骤前加 npm run check(如果项目提供),让 Front Matter 缺字段这类问题在 CI 阶段就拦住,而不是部署后才发现页面异常。
  2. 并发控制:连续 push 两个 commit 时不希望两个部署任务打架:
concurrency:
group: deploy
cancel-in-progress: true

同一时刻只允许一个部署在跑,新的 push 会取消未完成的旧任务——反正部署的目标是「最新状态」,中间的半成品没有意义。

延伸阅读#

用 GitHub Actions 搭建静态博客自动部署流水线
https://meow.us.kg/posts/github-actions-auto-deploy-blog/
作者
もゆちゃん
发布于
2026-10-18
许可协议
CC BY-NC-SA 4.0

分享文章

生成精美分享图或复制链接,与更多人分享本文。

继续阅读

沿着主题读

基于共同的标签与分类

换条路线

从其他文章中稳定抽取