GitHub Actions deploy.yml 实战解析:从构建到 PM2 自动化发布
以Next.js项目为例,逐段拆解 deploy.yml:从触发器与并发控制、构建打包、SCP 上传,到 SSH 远程发布与 PM2 启动
完整的“构建 → 上传 → 远程发布 → PM2 启动”流程,我按“语法 + 功能”拆开讲。
1) 先看 YAML 语法骨架
GitHub Actions 工作流的基本结构是:
name:工作流名字(在 Actions 页面显示)on:触发条件jobs:要执行的任务集合steps:每个任务里的执行步骤(顺序执行)
YAML 本身最关键是缩进(空格,不是 tab)。
文件层级大概是:on/jobs 同级,steps 在 jobs.deploy 下面,run/uses/with 在每个 step 下面。
2) 触发与并发控制
on
push到main时自动部署workflow_dispatch支持在 GitHub 页面手动点“Run workflow”
concurrency
group: blog-prodcancel-in-progress: true
作用:如果连续 push 多次,只保留最新一次部署,旧的自动取消,避免“后发先至/覆盖错版本”。
3) Job 运行环境
jobs.deploy.runs-on: ubuntu-latest
表示这个 CI 任务跑在 GitHub 提供的 Ubuntu 虚机上,不是服务器本机。
4) 构建阶段(在 GitHub Runner 上)
Checkout
actions/checkout@v4:拉取仓库代码。
Setup Node
actions/setup-node@v4:安装 Node 22.16.0,并启用 npm 缓存。
Install deps
npm ci:按 package-lock.json 严格安装,适合 CI,稳定可复现。
Build
npm run build:执行 Next.js 生产构建。
Prune dev dependencies
npm prune --omit=dev:删除开发依赖,减小上传体积。
Pack artifact
打包 release.tar.gz,内容包括:
.nextpublicnode_modulespackage.jsonpackage-lock.jsonnext.config.ts
这就是“待发布产物”。
5) 上传阶段(CI -> 服务器)
appleboy/scp-action
通过 SCP 把 release.tar.gz 上传到:
- 服务器:
secrets.HOST - 用户:
deploy - 路径:
/home/deploy/releases
6) 发布阶段(在服务器上执行)
appleboy/ssh-action
通过 SSH 登录服务器后执行 script 里的 shell。
主要逻辑:
- 定义变量:
APP_NAME、APP_DIR、APP_PORT等 - 确保目录存在:
mkdir -p - 补 PATH(兼容 CentOS 的 node 安装路径)
- 检查
node/pm2是否可用,不可用就失败退出 - 清理旧产物目录(
.next/public/node_modules) - 解压新包到应用目录
- 校验
DEEPSEEK_API_KEY是否存在 - 写入
.env.production - 启动/重启 PM2:
- 存在旧进程就删掉
- 重新用指定端口启动
blog
pm2 save持久化进程列表
7) 这个脚本的核心价值
它解决了 4 件事:
- 自动化发布(不用手工传文件)
- 版本一致(每次都来自 CI 产物)
- 可重复(失败就停,避免半发布)
- 运行环境可控(端口、API Key、PM2 名称固定)
8) 后续最常改的地方
优先级最高的可配置项:
- Node 版本:
node-version - 端口默认值:
BLOG_PORT默认3002 - 打包内容:
tar -czf ... - 服务器路径:
/home/deploy/apps/blog - PM2 进程名:
APP_NAME=blog
9) 需要在 GitHub Secrets 里保证有
HOSTSSH_KEYDEEPSEEK_API_KEYBLOG_PORT(可选,不填用 3002)