实战:完整 CI/CD 流水线
恭喜你走到这里!前 17 章我们已经认识了事件触发、作业与步骤、矩阵、缓存、Artifacts、并发、环境审批与安全。本章要把它们全部串成一条线——以一个典型的 Node/前端项目为例,设计一条"接近生产"的 CI/CD 流水线,并给出一份逐段讲解的综合 workflow。
学完这一章,你手里就拿到了一张"从代码到上线"的完整地图。
1. 我们要解决什么:一条线的全景
先想清楚这条流水线要干什么。对一个前端项目来说,一条像样的流水线应当覆盖:
- 触发:有人 push、有人开 PR、或者手动触发时,就跑。
- CI(持续集成):拉代码、装依赖、lint、跑测试——而且是多版本矩阵地跑,确保兼容性。
- 构建:测试全过之后,打包出
dist/,作为 artifact 留存。 - 部署:只有
main分支才允许部署;生产环境要人工审批;用concurrency防撞车;用 OIDC/secret 对接云。 - 多环境:staging 自动部署,production 审批后才部署。
- 发布:打
v*tag 时,自动出 GitHub Release。
下面我们一段一段把它落成 YAML。
2. 触发与权限:入口先定好
先从文件头开始:定义名称、触发条件,以及默认最小权限。
name: CI/CD Pipeline
on:
push:
branches: [main]
tags: ['v*']
pull_request:
workflow_dispatch:
permissions:
contents: read要点对照前面的章节:
on.push.branches: [main]与on.pull_request:对应第 2 章"事件触发"——push 和 PR 都会启动流水线。on.push.tags: ['v*']:为第 6 节的"发布"做准备,打版本 tag 时也要跑。on.workflow_dispatch:允许你在 Actions 页面手动点一下就触发,方便运维紧急重跑。permissions: contents: read:呼应第 17 章,入口就收窄,需要写权限的 job 再单独放开。
'v*' 里的 * 是 YAML 里的特殊字符(别名标记),写在 Flow 风格里容易出问题。用单引号包成字符串是稳妥写法,避免 YAML 解析歧义。这类细节是"能跑"和"编译失败"的分界。
3. CI 阶段:矩阵跑 lint 与 test
CI 是整条线的地基。我们用 matrix(矩阵) 在 Node 18 / 20 / 22 三个版本上同时验证,再借助缓存加速 npm ci。
jobs:
ci:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18, 20, 22]
steps:
- uses: actions/checkout@v4
- name: 设置 Node 并启用 npm 缓存
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: npm
- run: npm ci
- run: npm run lint
- run: npm test逐段对照:
strategy.matrix.node-version: [18, 20, 22]:第 7 章矩阵——同一个 job 自动展开成 3 个并行运行,覆盖多个 Node 版本。actions/setup-node@v4的cache: npm:第 9 章缓存——自动按package-lock.json缓存node_modules,大幅缩短npm ci。npm ci:比npm install更严格、更快,且要求 lock 文件存在,适合 CI。lint与test:质量门禁,挂了就红灯,后面部署根本不会触发。
想更狠一点,可以把 OS 也并进来:
strategy:
matrix:
node-version: [18, 20, 22]
os: [ubuntu-latest, windows-latest]这会展开成 3×2 = 6 个组合。注意不是所有项目都要跨 OS,按需开启即可,避免白白烧运行分钟数。
4. 构建:产出 artifact 留给部署
CI 全绿后,进入构建,并把产物上传为 artifact,供后续部署下载——这样部署 job 不必再构建一次,也更可复现。
build:
needs: ci
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- run: npm ci
- run: npm run build
- name: 上传构建产物
uses: actions/upload-artifact@v4
with:
name: dist
path: dist/对照要点:
needs: ci:第 4 章作业依赖——CI 不过,构建不启动。upload-artifact@v4的path: dist/:第 10 章 Artifacts——把打包结果存起来,部署阶段用同名 artifact 取回,保证"测的是这份、上的也是这份"。
5. 部署:if 守门 + 环境审批 + 并发控制
部署是整条线里最敏感的一步。我们用三道闸:
if:只让main分支部署(tag 触发时不重复部署)。environment: production触发人工审批门禁(第 16 章)。concurrency: deploy防止并发部署互相覆盖(第 16 章)。
deploy:
needs: build
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
environment: production
concurrency: deploy
permissions:
id-token: write
contents: read
steps:
- name: 下载构建产物
uses: actions/download-artifact@v4
with:
name: dist
path: dist/
- name: 用 OIDC 换取云临时凭证
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789012:role/github-actions-deploy
aws-region: ap-east-1
- run: aws s3 sync ./dist s3://my-bucket --delete逐段对照:
if: github.ref == 'refs/heads/main':第 4 章条件执行——只有推到main才部署,feature 分支和 tag 触发都不会误上线。environment: production+concurrency: deploy:第 16 章组合拳——生产部署需审批、且同一时刻只有一个。permissions: id-token: write:第 17 章 OIDC——只在这个 job 放开换取云凭证所需的权限,其余 Job 仍是只读。download-artifact:取回第 4 节上传的dist/,部署的就是刚测过的同一份产物。aws s3 sync:这里用 OIDC 思路对接云(实际字段以官方 action 为准),仓库里不存长期密钥。
if 决定"要不要部署",environment 决定"部署前要不要审批"。二者职责不同:只写 if 不写 environment,生产部署就没有人工确认;只写 environment 不写 if,任何分支都能触发审批。把 if: github.ref == 'refs/heads/main' 与 environment: production 一起用,才同时守住了"分支"和"审批"两道门。
6. 多环境:staging 自动、production 审批
真实团队通常有多个环境。区别只在于:staging 自动部署、production 审批部署。我们可以用两个 job(或一个 job 两个 environment)来演示。
deploy-staging:
needs: build
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
environment: staging # 无审批,自动过
concurrency: deploy-staging
steps:
- uses: actions/download-artifact@v4
with:
name: dist
path: dist/
- run: ./deploy.sh staging
deploy-production:
needs: deploy-staging # 先过 staging,再上生产
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
environment: production # 有审批门禁
concurrency: deploy-production
permissions:
id-token: write
contents: read
steps:
- uses: actions/download-artifact@v4
with:
name: dist
path: dist/
- run: ./deploy.sh production要点:deploy-production 用 needs: deploy-staging 形成环境升级链——staging 成功且生产审批通过后,才真正上线。每个环境用各自的 concurrency 组,互不干扰。
7. 发布:打 tag 自动出 Release
当开发者推送一个 v1.2.0 这样的 tag,我们希望自动生成 GitHub Release。这里用社区常用的 softprops/action-gh-release 一笔带过(以官方文档为准),注意它需要 contents: write。
release:
needs: build
if: startsWith(github.ref, 'refs/tags/v')
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/download-artifact@v4
with:
name: dist
path: dist/
- name: 创建 GitHub Release
uses: softprops/action-gh-release@v2
with:
files: dist/**对照:if: startsWith(github.ref, 'refs/tags/v') 只在打 v* tag 时触发;permissions: contents: write 仅此 job 放开写权限(第 17 章最小权限)。
8. 综合 workflow 一览
把上面各段拼起来,就是一条接近生产的流水线(OIDC 与 release 的具体字段以官方 action 文档为准):
name: CI/CD Pipeline
on:
push:
branches: [main]
tags: ['v*']
pull_request:
workflow_dispatch:
permissions:
contents: read
jobs:
ci:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18, 20, 22]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: npm
- run: npm ci
- run: npm run lint
- run: npm test
build:
needs: ci
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- run: npm ci
- run: npm run build
- uses: actions/upload-artifact@v4
with:
name: dist
path: dist/
deploy-staging:
needs: build
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
environment: staging
concurrency: deploy-staging
steps:
- uses: actions/download-artifact@v4
with:
name: dist
path: dist/
- run: ./deploy.sh staging
deploy-production:
needs: deploy-staging
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
environment: production
concurrency: deploy-production
permissions:
id-token: write
contents: read
steps:
- uses: actions/download-artifact@v4
with:
name: dist
path: dist/
- name: 用 OIDC 换取云临时凭证
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789012:role/github-actions-deploy
aws-region: ap-east-1
- run: aws s3 sync ./dist s3://my-bucket --delete
release:
needs: build
if: startsWith(github.ref, 'refs/tags/v')
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/download-artifact@v4
with:
name: dist
path: dist/
- uses: softprops/action-gh-release@v2
with:
files: dist/**这份 YAML 的每一块,都呼应了前面某一章:事件触发(第 2 章)、作业与步骤(第 3–4 章)、矩阵(第 7 章)、缓存(第 9 章)、Artifacts(第 10 章)、并发与环境审批(第 16 章)、安全与 OIDC(第 17 章)。
9. 小结:从事件到上线的完整链路地图
至此,整门课的知识点已经连成一张图:
push / PR / 手动 ──► checkout 拉代码
│
▼
setup-node + 缓存 npm + npm ci
│
▼
矩阵 lint & test(Node 18/20/22)
│ needs: ci
▼
build → 上传 dist/ artifact
│ needs: build
┌────────────┼─────────────┐
▼ ▼ ▼
deploy-staging deploy-prod release
(自动,无审批) (审批+并发+OIDC) (打 v* tag)
一句话收束:事件触发 → 拉代码/装环境 → 矩阵测试 → 构建 → 审批 → 部署 → (打 tag)发布。你已经掌握了设计一条生产级流水线所需的全部积木。
进阶学习路径建议
如果你想继续深入,下面几个方向最值得投入:
- 自托管 Runner / Larger Runners:当公共 runner 不够快、或需要特定硬件/内网访问时,用自己托管的机器跑 job。
- 可复用模板仓库(Reusable workflows):把通用流水线抽成
workflow_call模板,多个仓库复用,避免复制粘贴。 - 用 Actions 做 issue/PR 自动化:标签管理、自动回复、stale bot、自动合并等,把协作用 Git 也自动化起来。
- 监控与告警:把流水线失败接到 Slack/飞书/邮件,配合
workflow_run事件做状态汇总。
感谢你一路学完《GitHub Actions》这门课。从第一次 on: push 到写出一条带审批、带 OIDC、带矩阵的安全流水线,你已经具备把"自动化"真正落到团队生产里的能力。去把你的项目接上 Actions 吧,实践是巩固这些知识最好的方式。祝发布顺利,永不失联!
10. 练习
1. 在上面的综合 workflow 里,如果想"只在 push 到 main 且不是 tag 触发时才部署",deploy-production 的 if: 该怎么写?(提示:github.ref 与 github.ref_type)
2. 为什么 build job 上传的 artifact 要在 deploy job 里重新 download-artifact,而不是让 deploy 自己再 npm run build 一次?
3. 综合 workflow 中,release job 的 permissions 为什么必须放开成 contents: write,而顶层却写的是 contents: read?
4. 如果要新增一个 deploy-staging 到 deploy-production 之间的"回归测试"job,且要求它只在 staging 部署成功后、生产审批前运行,请写出它与相邻 job 的 needs 关系。