Files
cs-note/hzh/CICD/CICD 在 Slidev 中的应用/workflow-configuration.md
T

171 lines
6.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
tags:
- CI/CD
- Gitea Actions
- Slidev
create time: 2026-05-27 19:30
---
# Workflow 配置详解
## 概述
本文档对 `CICD 在 Slidev 中的应用` 中的 Workflow YAML 进行逐块解析,涵盖从触发条件到构建部署的每一步配置逻辑。
> [!NOTE] 如需了解部署架构和容器通信模式,详见 [[deployment-strategy]]。
## 正文
### 1. 触发条件
```yaml
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. 运行环境与容器
```yaml
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. 安装系统工具
```yaml
- 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)
```yaml
- 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 操作:
```mermaid
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. 安装依赖与构建
```yaml
- name: Install pnpm
run: npm install -g pnpm
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build all decks
run: pnpm run build
```
三步完成从包管理到构建的过程:
1. **全局安装 pnpm** — 容器初始状态只有 Node.js,需要先装包管理器
2. **安装项目依赖** — `--frozen-lockfile` 确保依赖版本精确锁定(详见下方说明)
3. **执行构建** — `pnpm run build` 对应 `slidev build`,将所有 `.md` deck 编译为静态站点
> [!TIP] Slidev 的 `build` 命令输出目录默认为 `dist/`,每个 slide deck 一个子目录。这就是为什么最后一步可以直接 `cp dist/. nginx:/usr/share/nginx/html/`。
#### --frozen-lockfile 的 CI 哲学
```bash
# 不加参数:lockfile 被悄悄更新 → 下次构建可能用不同版本
pnpm install
# 加 --frozen-lockfile:严格一致,否则立即失败
pnpm install --frozen-lockfile
```
| 场景 | 不加 `--frozen-lockfile` | 加上 `--frozen-lockfile` |
|------|------------------------|-------------------------|
| lockfile 正常 | ✅ 安装 | ✅ 安装 |
| lockfile 缺失 | 🔧 自动生成一个新的 | ❌ **报错退出** |
| lockfile 与 package.json 不匹配 | 🔧 悄悄更新 lockfile | ❌ **报错退出** |
CI 环境追求的是 **构建可复现性 (Build Reproducibility)**。同一个 commit,不同的人、不同的时间、不同的机器上构建出来的结果必须完全一致——否则线上 bug 将无法定位是"代码问题"还是"环境差异"。
```mermaid
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]]