--- 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 启动
空工作目录"] --> B["git clone 仓库"] B --> C["git fetch 指定 commit 的引用"] C --> D["git reset --hard 精确 SHA"] D --> E[".git 隐藏目录
pnpm install 可用"] ``` | 步骤 | Git 命令等价物 | 作用 | |------|---------------|------| | Clone | `git clone ` | 拉取全量历史到 action 工作区 | | Fetch | `git fetch origin ` | 获取触发 workflow 的具体 commit 引用 | | Reset | `git reset --hard ` | 将工作树精确还原到目标提交(而非默认分支最新) | > [!TIP] PR 场景下尤其要注意:如果 workflow 被 pull_request 事件触发,Checkout 会自动检出 **PR 合并后的结果**(即 `refs/pull//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["❌ 立即失败
提示手动运行 pnpm install"] style E fill:#ffe8e8 style D fill:#e8ffe8 ``` > [!TIP] 正确的工作流:开发者改完依赖 → `pnpm install` 自动更新 lockfile → 同时提交两个文件。CI 只负责验证一致性,不负责修锁。 ## 关联笔记 - [[CICD 在 Slidev 中的应用]] - [[deployment-strategy]]