给本产品写一条 CI 流水线
补一份可直接复制进本仓库的 .github/workflows/ci.yml
- 理解 push 后 CI 自动跑一遍的意义
- 能读懂并复用一份最小 GitHub Actions YAML
- 知道 lint / build / 产物上传各自挡什么
- 明白 CI 绿灯才允许合并的工程纪律
考试交卷前你会自查:名字写了吗?选择题涂卡了吗?作文够字数吗?CI 就是把这套「交卷前自查」自动化——你一 push,它就在另一台干净机器上把检查全跑一遍,不过就不许合并。
为什么要 CI
「我本地能跑」不代表合并到 main 后能跑:你本机装过的依赖、忘了提交的文件、换了个 Node 版本,都可能让别人拉下来就崩。CI 在每次 push / PR 时开一台干净机器,重新装依赖、重新构建,等于给每次合并做一次体检。
一条最简流水线做三件事
对本产品(Next.js 静态导出 + node server.js),最小闭环就是:装依赖 → lint → build → 把 out/ 存成产物。读者可以直接把下面这份复制到自己仓库的 .github/workflows/ci.yml。
# .github/workflows/ci.yml(可直接复制,字段以官方最新语法为准)
print("""
name: CI
on:
push:
branches: [main]
pull_request:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm ci
- run: npm run lint
- run: npm run build
- uses: actions/upload-artifact@v4
with:
name: out-dir
path: out/
""")actions/checkout、setup-node、upload-artifact 的大版本号(v4 等)会随官方更新,Node 版本也建议与本地一致。复制后请到 GitHub Actions 官方文档核对最新写法,别照抄过期版本。
三步各自挡掉什么
lint:挡未用变量、明显低级错误,提交阶段就拦住build:挡类型错误和打包失败,这是能不能合并的硬门槛产物上传:把 out/ 存一份,部署时直接拿,不用再构建一遍
上面那段竖排对照的意思是:lint 管代码味道,build 管能不能编译,artifact 管把构建结果留下来给部署用。三步顺序不能乱——先 lint 再 build,失败早发现早省时间。
CI 红灯了怎么办:缓存 / 依赖 / 并发
CI 第一次跑红了别慌,先看报错落在哪个步骤。最常见的三类:依赖装不上(npm ci 红)、缓存污染(node_modules 缓存了旧版本导致构建怪象)、并发冲突(两个人同时 push,互相把对方的产物覆盖了)。依赖问题先看是不是锁文件 package-lock.json 没提交;缓存问题去 Actions 面板手动清掉缓存再重跑;并发问题靠 matrix 和 artifact 按分支隔离,别让两个 job 写同一个路径。
第一步:看红在哪一步(lint/build/test)第二步:本地 checkout 同一个 commit,npm ci 一遍能不能复现第三步:能复现就是代码问题,去改代码第四步:本地不能复现,多半是 CI 环境差异——Node 版本、缓存、依赖版本漂移,清缓存重跑
缓存:让 CI 跑得快一点
每次 push 都 npm ci 全量装依赖,又慢又耗分钟数。可以把 node_modules 或 npm 缓存目录按 lock 文件 hash 缓存下来,下次 lock 没变就直接用缓存,跳过安装。缓存命中能把 CI 从 3 分钟压到 1 分钟以内。具体缓存 key 写法以 GitHub Actions 官方最新示例为准。
并行与矩阵构建
一个 job 从头到尾串行跑,所有 PR 都排队等。两个常用加速手段:并行(parallel)——把 lint、build、test 拆成三个 job 同时跑,谁先红谁先报;矩阵(matrix)——一份 YAML 同时在 Node 18 和 Node 20 上各跑一遍,验证兼容性。矩阵特别适合库项目,应用项目通常一个 Node 版本就够。
# 矩阵构建概念示意(同时跑 Node 18 和 20)
print("""
strategy:
matrix:
node-version: [18, 20]
runs-on: ubuntu-latest
steps:
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- run: npm ci
- run: npm run build
""")构建产物上传与下载
build 那一步产出的 out/ 目录,用 upload-artifact 存到 Actions 上,下一个 job(或部署 job)用 download-artifact 取下来,不用重新 build。这样「构建」和「部署」解耦:构建跑一次,部署 job 直接拿现成产物。artifact 有保留天数,临时产物别存太久。
免费额度和每分钟扣费标准随 GitHub 政策调整,具体数字(公开仓库是否免费、私有仓库每月多少免费分钟、macOS runner 几倍计费)以 GitHub 官方最新文档为准,别记过期数字。一般原则:少用昂贵的 runner、多用缓存、把不需要的 job 关掉,额度就够用。
一条靠谱的 CI 流水线应该包含哪些步骤?
CI 本地能跑、Actions 上却红了,最不该先做的是?
这份 ci.yml 复制进仓库就跑通 lint→build→产物;s15-m8-l1 系统讲 Docker 镜像/Dockerfile/Compose、CI 流水线各阶段(含测试、部署、健康检查、自动回滚)和 CD 自动化演进。这里管「跑通一条」,那边管「为什么分阶段、怎么容器化」。
资深工程师加餐
底层原理 · 大厂视角 · 工程经验,点卡片展开
客户端把进度存到本地(localStorage / SQLite)后,下一版字段结构一变,老用户存档就可能解析失败、进度全丢。专业做法是给存档加 version 字段,读取时按版本依次执行迁移函数(补默认值、改名、重组结构),并对解析全程 try/catch、坏档能回退到安全默认。写入时还要防御 NaN/undefined(如 JSON.stringify(NaN) 会变成 null),这正是「数据比代码活得久」的含义。
挑战任务
CI 红灯定位器
实现 ci_block_reason(lint_ok, build_ok, test_ok):按 lint→build→test 的顺序检查,第一个不通过的返回它的名字("lint"/"build"/"test");三项全过返回 "pass"。
课后作业
判断对错:CI 做法评价
下面 5 个说法,对的写 True、错的写 False,顺序①~⑤。 ① CI 上 build 红了,本地能跑,可以直接合并 ② 把 node_modules 按 lock 文件 hash 缓存能加速 CI ③ lint/build/test 应该串在一个 job 里跑完,不能并行 ④ artifact 上传后,部署 job 可以直接下载不用重新 build ⑤ 跑到第 2 天看实验组略高就提前全量是对的 AB 纪律