6.9 KiB
tags, create time
| tags | create time | |||
|---|---|---|---|---|
|
2026-05-27 19:30 |
Workflow 配置详解
概述
本文档对 CICD 在 Slidev 中的应用 中的 Workflow YAML 进行逐块解析,涵盖从触发条件到构建部署的每一步配置逻辑。
[!NOTE] 如需了解部署架构和容器通信模式,详见 deployment-strategy。
正文
1. 触发条件
on:
push:
branches: [main]
pull_request:
branches: [main]
向 main 分支推送代码或提交 PR 时都会触发。这意味着开发者通过特性分支提 PR,CI 会先验证构建是否通过——只有合并到主干后才执行部署。
[!TIP] PR 事件只适合跑验证性步骤(build、lint),实际部署仍保留在
push: main中,因为 PR 上下文通常没有写入权限。如果 Gitea Actions 的 PR 事件无法读取 secret,可改用pull_request_target,但需避免直接使用 PR 代码中的敏感操作以防止 token 泄露。
[!QUESTION] 思考:如果你的 Slidev 项目需要同时支持 staging 和 production 环境,你会如何设计触发规则?
2. 运行环境与容器
jobs:
build-and-deploy:
runs-on: aliyun
container: node:22-alpine
runs-on: aliyun— 使用了自定义的 Gitea runner,运行在一台阿里云机器上(而非官方 GitHub-hosted runner)。container: node:22-alpine— 在 Alpine Linux 上的 Node.js 22 容器中执行,体积小、启动快,适合纯前端构建场景。
3. 安装系统工具
- name: Install tools
run: |
sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
apk add --no-cache docker-cli git
Alpine 默认的软件源在海外,先用 sed 替换为阿里云镜像源加速下载。然后安装三个必要的工具:
| 工具 | 用途 |
|---|---|
docker-cli |
指挥宿主机 Docker daemon,管理其他容器的生命周期 |
git |
版本控制操作所需 |
apk |
Alpine 的包管理器(已内置) |
[!NOTE] 这里只安装了
docker-cli(客户端),没有安装完整的docker。完整的 Docker 包包含 daemon 本身(约 80MB),而我们需要的是轻量级 CLI(约 50MB on Alpine)——因为 daemon 已经在宿主机上运行了。
4. 检出代码 (Checkout)
- name: Checkout
uses: http://47.121.181.112:3000/wonder/checkout@v4
with:
github-server-url: http://47.121.181.112:3000
使用私有 Gitea 实例的 checkout action,指向地址 http://47.121.181.112:3000。通过 github-server-url 参数告知 action 正确的 Gitea 服务器地址。
Checkout 到底做了什么?
很多开发者误以为 workflow 启动后工作目录里已经有代码了——实际上 runner 初始环境是空的。Checkout action 会在幕后执行一系列 git 操作:
flowchart LR
A["Runner 启动<br/>空工作目录"] --> B["git clone 仓库"]
B --> C["git fetch 指定 commit 的引用"]
C --> D["git reset --hard 精确 SHA"]
D --> E[".git 隐藏目录<br/>pnpm install 可用"]
| 步骤 | Git 命令等价物 | 作用 |
|---|---|---|
| Clone | git clone <repo-url> |
拉取全量历史到 action 工作区 |
| Fetch | git fetch origin <commit-sha> |
获取触发 workflow 的具体 commit 引用 |
| Reset | git reset --hard <commit-sha> |
将工作树精确还原到目标提交(而非默认分支最新) |
[!TIP] PR 场景下尤其要注意:如果 workflow 被 pull_request 事件触发,Checkout 会自动检出 PR 合并后的结果(即
refs/pull/<number>/merge),而不是 PR 分支本身。这确保了 CI 验证的是"合并后能否构建",而非"分支本身能否构建"。
常用配置参数
实际项目中经常需要调整 checkout 的行为,以下是高频参数及适用场景:
| 参数 | 默认值 | 用途 |
|---|---|---|
ref |
触发 workflow 的 commit SHA | 指定检出的分支、标签或 commit |
clean |
true |
检出前是否清理工作目录残留文件 |
persist-credentials |
true |
是否在后续步骤中保持 git 认证信息 |
fetch-depth |
1 |
只拉取最近一次提交(fast clone) |
[!QUESTION] 什么时候应该设置
fetch-depth: 0?答案:当你需要完整的 git 历史时——比如用
git log生成 changelog、执行语义化版本计算 (conventional-changelog),或者需要git blame追溯代码来源。fetch-depth: 1是默认值,因为它更快更省空间;只有确实需要历史记录时才改为0。
[!WARNING] 安全注意:CI 中的 checkout 会拿到完整的 repo token。如果 workflow 包含不可信的第三方 action(如开源社区提供的 step),建议设置
persist-credentials: false,防止 token 被泄露给非官方代码。
5. 安装依赖与构建
- name: Install pnpm
run: npm install -g pnpm
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build all decks
run: pnpm run build
三步完成从包管理到构建的过程:
- 全局安装 pnpm — 容器初始状态只有 Node.js,需要先装包管理器
- 安装项目依赖 —
--frozen-lockfile确保依赖版本精确锁定(详见下方说明) - 执行构建 —
pnpm run build对应slidev build,将所有.mddeck 编译为静态站点
[!TIP] Slidev 的
build命令输出目录默认为dist/,每个 slide deck 一个子目录。这就是为什么最后一步可以直接cp dist/. nginx:/usr/share/nginx/html/。
--frozen-lockfile 的 CI 哲学
# 不加参数:lockfile 被悄悄更新 → 下次构建可能用不同版本
pnpm install
# 加 --frozen-lockfile:严格一致,否则立即失败
pnpm install --frozen-lockfile
| 场景 | 不加 --frozen-lockfile |
加上 --frozen-lockfile |
|---|---|---|
| lockfile 正常 | ✅ 安装 | ✅ 安装 |
| lockfile 缺失 | 🔧 自动生成一个新的 | ❌ 报错退出 |
| lockfile 与 package.json 不匹配 | 🔧 悄悄更新 lockfile | ❌ 报错退出 |
CI 环境追求的是 构建可复现性 (Build Reproducibility)。同一个 commit,不同的人、不同的时间、不同的机器上构建出来的结果必须完全一致——否则线上 bug 将无法定位是"代码问题"还是"环境差异"。
flowchart LR
A["开发者修改 package.json"] --> B{"push"}
B --> C["CI: frozen-lockfile 检查"]
C -->|lockfile 已同步| D["✅ 构建通过"]
C -->|lockfile 未同步| E["❌ 立即失败<br/>提示手动运行 pnpm install"]
style E fill:#ffe8e8
style D fill:#e8ffe8
[!TIP] 正确的工作流:开发者改完依赖 →
pnpm install自动更新 lockfile → 同时提交两个文件。CI 只负责验证一致性,不负责修锁。
关联笔记
- CICD 在 Slidev 中的应用
- deployment-strategy