HogoZhang
·

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 同级,stepsjobs.deploy 下面,run/uses/with 在每个 step 下面。

2) 触发与并发控制

on

  • pushmain 时自动部署
  • workflow_dispatch 支持在 GitHub 页面手动点“Run workflow”

concurrency

  • group: blog-prod
  • cancel-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,内容包括:

  • .next
  • public
  • node_modules
  • package.json
  • package-lock.json
  • next.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_NAMEAPP_DIRAPP_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 里保证有

  • HOST
  • SSH_KEY
  • DEEPSEEK_API_KEY
  • BLOG_PORT(可选,不填用 3002)