12 分钟
项目全解析

给本产品写一条 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。

Python
# .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 / 产物的分工

lint:挡未用变量、明显低级错误,提交阶段就拦住build:挡类型错误和打包失败,这是能不能合并的硬门槛产物上传:把 out/ 存一份,部署时直接拿,不用再构建一遍

上面那段竖排对照的意思是:lint 管代码味道,build 管能不能编译,artifact 管把构建结果留下来给部署用。三步顺序不能乱——先 lint 再 build,失败早发现早省时间。

CI 红灯了怎么办:缓存 / 依赖 / 并发

CI 第一次跑红了别慌,先看报错落在哪个步骤。最常见的三类:依赖装不上(npm ci 红)、缓存污染(node_modules 缓存了旧版本导致构建怪象)、并发冲突(两个人同时 push,互相把对方的产物覆盖了)。依赖问题先看是不是锁文件 package-lock.json 没提交;缓存问题去 Actions 面板手动清掉缓存再重跑;并发问题靠 matrix 和 artifact 按分支隔离,别让两个 job 写同一个路径。

💡CI 红灯排查顺序

第一步:看红在哪一步(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 版本就够。

Python
# 矩阵构建概念示意(同时跑 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 Actions 计费与分钟数

免费额度和每分钟扣费标准随 GitHub 政策调整,具体数字(公开仓库是否免费、私有仓库每月多少免费分钟、macOS runner 几倍计费)以 GitHub 官方最新文档为准,别记过期数字。一般原则:少用昂贵的 runner、多用缓存、把不需要的 job 关掉,额度就够用。

选择题

一条靠谱的 CI 流水线应该包含哪些步骤?

选择题

CI 本地能跑、Actions 上却红了,最不该先做的是?

填空题填写空白处的代码
# GitHub Actions 的一个步骤 - name: Build run: npm run # push 后在干净机器上跑生产构建
💡本节是本仓库的最小 CI,完整 CI/CD 与容器归 s15-m8-l1

这份 ci.yml 复制进仓库就跑通 lint→build→产物;s15-m8-l1 系统讲 Docker 镜像/Dockerfile/Compose、CI 流水线各阶段(含测试、部署、健康检查、自动回滚)和 CD 自动化演进。这里管「跑通一条」,那边管「为什么分阶段、怎么容器化」。

资深工程师加餐

底层原理 · 大厂视角 · 工程经验,点卡片展开

客户端把进度存到本地(localStorage / SQLite)后,下一版字段结构一变,老用户存档就可能解析失败、进度全丢。专业做法是给存档加 version 字段,读取时按版本依次执行迁移函数(补默认值、改名、重组结构),并对解析全程 try/catch、坏档能回退到安全默认。写入时还要防御 NaN/undefined(如 JSON.stringify(NaN) 会变成 null),这正是「数据比代码活得久」的含义。

挑战任务

CI 红灯定位器

简单+50 XP

实现 ci_block_reason(lint_ok, build_ok, test_ok):按 lint→build→test 的顺序检查,第一个不通过的返回它的名字("lint"/"build"/"test");三项全过返回 "pass"。

CI 红灯定位器
1 个测试用例

课后作业

判断对错:CI 做法评价

中等+25 XP

下面 5 个说法,对的写 True、错的写 False,顺序①~⑤。 ① CI 上 build 红了,本地能跑,可以直接合并 ② 把 node_modules 按 lock 文件 hash 缓存能加速 CI ③ lint/build/test 应该串在一个 job 里跑完,不能并行 ④ artifact 上传后,部署 job 可以直接下载不用重新 build ⑤ 跑到第 2 天看实验组略高就提前全量是对的 AB 纪律

判断对错:CI 做法评价
1 个测试用例