跳到主要内容

CI / CD 与 GitHub Actions

工程化里很容易有一个阶段:本地一切正常,换到别的电脑、CI 环境或者发布环境之后,问题就开始出现。

所以 CI / CD 这一层关心的,不是“自动跑个脚本”这么简单,而是把这些事情稳定下来:

  • 提交后能不能自动验证
  • 构建产物是不是可重复
  • 发布流程是不是可追踪
  • 回滚和环境隔离是不是清楚

先把 CI 和 CD 分开

CI 是什么

CI,Continuous Integration,通常先解决这些问题:

  • 每次 push / PR 是否自动跑 lint、typecheck、test、build
  • 依赖安装和 Node 版本是否一致
  • 团队提交进来的代码能不能尽早发现问题

CD 是什么

CD,通常会分成两种理解:

  • Continuous Delivery:产物随时可发布,但是否上线还要人工确认
  • Continuous Deployment:产物验证通过后自动上线

所以一个更稳的理解是:

  • CI 负责验证
  • CD 负责交付和发布

为什么前端项目更需要 CI / CD

前端项目的“不稳定”通常不只来自代码本身,还来自这些地方:

  • Node 版本不一致
  • lockfile 漂移
  • 构建脚本只在某个人电脑上跑得过
  • 环境变量缺失
  • 打包产物在 CI 和本地不一致
  • 发布流程全靠手动点按钮

CI / CD 的价值,就是把这些隐性差异尽量提前暴露出来。

GitHub Actions 在这条链路里的位置

GitHub 官方文档把 Actions 定义成一套 CI / CD 平台:工作流由事件触发,工作流里包含 job,job 里再按 step 执行脚本或 action。

先记住这几层就够了:

  • workflow:一份自动化流程
  • event:触发条件
  • job:一个独立任务
  • step:任务里的具体步骤
  • action:可复用的自动化单元
  • runner:真正执行任务的机器

一个最常见的前端 CI 工作流

前端仓库最常见的一条线通常是:

  1. 拉代码
  2. 安装依赖
  3. 跑 lint
  4. 跑 typecheck
  5. 跑 test
  6. 跑 build

对应到 GitHub Actions,最小工作流大概会长这样:

name: ci

on:
pull_request:
push:
branches:
- main
- develop

jobs:
verify:
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 20
cache: pnpm

- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 10

- name: Install
run: pnpm install --frozen-lockfile

- name: Lint
run: pnpm run lint

- name: Typecheck
run: pnpm run typecheck

- name: Test
run: pnpm run test

- name: Build
run: pnpm run build

这已经能覆盖大多数前端项目最基础的验证链路。

workflow 文件一般放哪

GitHub Actions 的 workflow 文件通常放在:

.github/workflows/
ci.yml
deploy.yml

一个仓库里可以有多份 workflow,不需要把所有流程都塞进同一份 YAML。

触发条件怎么理解

最常见的 on 一般是这些:

  • push
  • pull_request
  • workflow_dispatch
  • schedule

push

适合主分支或开发分支的持续验证。

pull_request

适合在合并前拦截问题,也是前端项目里最常见的一种。

workflow_dispatch

适合手动触发,例如:

  • 手动部署
  • 手动同步数据
  • 手动跑一次全量检查

schedule

适合:

  • 定时巡检
  • 定时同步
  • 周期性依赖检查

job 和 step 怎么拆更合理

小仓库

通常一个 verify job 就够了。

中大型仓库

更常见的是拆成:

  • lint
  • typecheck
  • test
  • build

这样做的好处是:

  • 哪一步失败会更清楚
  • job 之间可以并行
  • 后面加 matrix 和缓存时更灵活

例如:

jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- uses: pnpm/action-setup@v4
with:
version: 10
- run: pnpm install --frozen-lockfile
- run: pnpm run lint

typecheck:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- uses: pnpm/action-setup@v4
with:
version: 10
- run: pnpm install --frozen-lockfile
- run: pnpm run typecheck

依赖缓存怎么理解

GitHub 官方文档把 cache 和 artifact 分得很清楚。

  • cache:为了加速下次 workflow
  • artifact:为了保存本次 workflow 的产物

前端项目最常见的缓存,是:

  • pnpm store
  • npm cache
  • yarn cache

如果使用 actions/setup-node 配合包管理器缓存,很多常见场景已经能自动接住。

artifact 适合保存什么

这类文件比较常见:

  • 构建产物
  • 测试报告
  • 截图
  • coverage 报告
  • 调试日志

一个最小示例:

- name: Upload build artifact
uses: actions/upload-artifact@v4
with:
name: web-build
path: build

如果想在 PR 里回看产物或下载测试报告,这层会很有用。

matrix 在前端项目里什么时候有意义

GitHub Actions 支持 matrix。最常见的前端用法通常是:

  • 多 Node 版本测试
  • 多操作系统验证
jobs:
verify:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest]
node: [20, 22]

steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}

不过前端项目也不一定一上来就开 matrix。多数情况下,先把单一主线跑稳更重要。

environment、secret 和变量

CI / CD 真正上线时,环境管理会变得很关键。

通常要分清三层:

  • repository variables
  • secrets
  • environments

secrets

适合:

  • token
  • API key
  • 部署密钥

environments

适合:

  • preview
  • staging
  • production

环境层通常还会配:

  • 审批
  • 环境变量
  • 环境级 secret
  • 部署保护规则

这对生产环境发布尤其重要。

一个更接近真实项目的前端 CI 配置

name: frontend-ci

on:
pull_request:
push:
branches:
- main

concurrency:
group: frontend-ci-${{ github.ref }}
cancel-in-progress: true

jobs:
verify:
runs-on: ubuntu-latest
timeout-minutes: 20

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 10

- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 20
cache: pnpm

- name: Install
run: pnpm install --frozen-lockfile

- name: Lint
run: pnpm run lint

- name: Typecheck
run: pnpm run typecheck

- name: Test
run: pnpm run test -- --runInBand

- name: Build
run: pnpm run build

这里加了两个比较实用的点:

  • concurrency:同一分支重复触发时,自动取消旧任务
  • timeout-minutes:避免 workflow 挂太久

CD 怎么接会更稳

前端项目的 CD 很常见的几种路线是:

  • GitHub Actions 里直接部署
  • GitHub Actions 调平台 CLI 部署
  • GitHub Actions 只负责验证,平台自己接管发布

1. Actions 直接发布静态站点

适合:

  • GitHub Pages
  • 自托管静态站
  • 对部署步骤有强控制需求的仓库

2. Actions 调部署平台

例如:

  • Vercel
  • Netlify
  • Cloudflare Pages

这类方式更常见的做法是:

  • CI 仍然在 GitHub Actions 里跑
  • deploy job 用平台 token 或 CLI 调起部署

3. 只在主分支或打 tag 时发布

这是更稳的默认做法。通常不会让任意分支 push 都直接进生产。

一个最小 deploy workflow

name: deploy

on:
workflow_dispatch:
push:
branches:
- main

jobs:
deploy:
runs-on: ubuntu-latest
environment: production

steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: 10
- uses: actions/setup-node@v4
with:
node-version: 20
cache: pnpm
- run: pnpm install --frozen-lockfile
- run: pnpm run build
- run: echo "deploy step here"

真实项目里最后一行一般会换成:

  • 平台 CLI
  • rsync / scp
  • Docker build + push
  • 调用内部发布脚本

前端项目里最常见的几类 CI / CD 任务

CI

  • lint
  • typecheck
  • unit test
  • e2e test
  • build
  • bundle size check

CD

  • preview deploy
  • staging deploy
  • production deploy
  • 回滚
  • 通知

常见误区

1. CI 只跑 build,不跑 typecheck 和 test

这会让很多问题拖到后面才暴露。

2. workflow 太大,一份 YAML 什么都做

一开始看着省事,后面维护会越来越重。分 ci.ymldeploy.ymlnightly.yml 往往更清楚。

3. secret 和普通变量混用

部署 token、私钥、云凭证最好都明确走 secrets。

4. 没有并发控制

PR 连续 push 时,老任务还在跑,新任务又起来,成本和噪音都会变大。

5. 生产环境没有 environment 保护

只要能自动发生产,就最好把审批和环境级 secret 管起来。

一个更实际的采用顺序

  1. 先把 lint + typecheck + build 跑通
  2. 再补 test
  3. 再加 cache、artifact、concurrency
  4. 再拆 preview / staging / production
  5. 最后再看自动部署和环境保护

这条顺序更稳,也更适合从零开始把仓库接上 CI / CD。

参考来源