关于 flaker-storage-cache-on-ci
此技能介绍如何在 GitHub Actions 中保存 flaker 的 DuckDB 历史数据,供后续 CI 运行使用。内容涵盖缓存配置、工作流分工、报告导入及问题排查。
主要功能
- 01使用 actions/cache@v6 持久化 DuckDB 数据
- 02匹配缓存与 flaker 存储路径
- 03为混合模式运行传入变更文件
- 04从 CI 工作流导入报告
- 05排查历史数据丢失
适用场景
- 01在 GitHub Actions 运行间保留 flaker 历史
- 02配置 CI 报告导入 flaker
- 03调查 CI 历史数据缺失或重置
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ mizchi/ skills --skill flaker-storage-cache-on-ci安装后在 Claude Code 中输入/flaker-storage-cache-on-ci确认已加载。
附带文件README.md
详细介绍
GitHub Actions 上的 flaker 存储缓存
flaker 将数据保存在 DuckDB 文件中,路径由 flaker.toml 的 [storage] path 声明(默认值为 .flaker/data)。要让不稳定测试检测 / KPI / 隔离功能正常工作,该文件必须在 CI 运行之间持久保存。GitHub Actions 没有一流的运行时存储,因此通常使用 actions/cache@v6 配合滑动键。
此技能适用场景
- “在新的 repo 中通过 CI 运行 flaker”
- “flaker 的历史记录在 CI 中丢失了 / 每次都从零开始”
- “添加一个将
导入 flaker 的工作流” - “想将 VRT / playwright / custom-adapter 报告导入 flaker”
缓存键格式
- name: Cache flaker data
if: always()
uses: actions/cache@v6
with:
path: .flaker/data
key: flaker-data-${{ github.run_id }}
restore-keys: |
flaker-data-
key使用github.run_id,因此每次运行保存时都会写入一个新的缓存条目(GH Actions 会在作业结束时保存)。restore-keys: flaker-data-会匹配之前写入的任何内容,并回退到最新条目 — 这样下一次运行就能看到最新累积状态,无论该状态由之前的哪次运行生成。- 使用
if: always(),这样即使之前的步骤失败,也会触发保存(保留失败时的历史记录完全没问题,而且通常比只保留成功时的历史记录更有用)。
path 必须与 flaker.toml 的 [storage] path 相同。默认值为 .flaker/data(这是文件,不是目录 — 预先将其创建为目录会导致 DuckDB 无法打开)。
在 pnpm 工作区单体仓库中:在缓存 path 前加上包目录前缀,例如 packages/<pkg>/.flaker/data。flaker 不会向上查找 flaker.toml — 它必须位于调用 flaker 的位置旁边。缓存路径与 flaker.toml 位置不匹配时,会出现 Config file not found(从仓库根目录运行时)或“每次运行历史记录都会消失”(缓存和 flaker 对存储位置的认知不一致时)。
触发写入
actions/cache@v6 会在其 post 步骤中自动保存。调用方无需显式执行 cache save。保存键使用运行 ID,因此重复写入不会发生冲突。
用于推导 --changed 的 fetch-depth
任何使用混合 / 受影响策略的 flaker 调用(CI 配置文件中默认使用 flaker run --gate merge)都需要 --changed <files,...>。如果不提供:Error: hybrid mode requires resolver and changedFiles。
- uses: actions/checkout@v7
with:
fetch-depth: 0 # need history for `git diff` against the PR base
- name: flaker run --gate merge
env:
BASE_REF: ${{ github.event.pull_request.base.ref }}
run: |
changed=$(git diff --name-only "origin/${BASE_REF}...HEAD" | tr '\n' ',' | sed 's/,$//')
pnpm exec flaker run --gate merge --changed "$changed"
差异为空(仅修改配置的 PR 等)也没问题 — 混合模式会回退到已配置的回退策略。
工作流:哪些会持久化,哪些会读取
按触发器拆分工作流的参考布局:
| 工作流 | 触发器 | 读取缓存 | 写入缓存 | 备注 |
|---|---|---|---|---|
flaker-nightly.yml | cron + workflow_dispatch | 是 | 是 | 运行 flaker apply。vitest 历史记录的规范写入方。 |
<source>-baseline.yml | 推送到 main + cron | 是 | 是 | 通过 flaker import --adapter <name> 导入 custom-adapter 报告。使用相同的缓存键。 |
flaker-pr.yml | pull_request | 是 | (会隐式写入,无害) | 仅供参考,continue-on-error: true。 |
<source>-pr-gate.yml | pull_request | 否 | 否 | PR 范围内的运行会扭曲总体数据 — 应保持为临时运行。 |
经验法则:任何涉及 main 或定时任务的流程都会写入;PR 范围内的检查应只读或不使用缓存。
添加新的摄取来源
常见模式(custom adapter,适配器约定见 flaker#79):
- 生成报告文件(例如
<source>-report.json、vitest-report.json)。 - 恢复缓存(或者依赖同一作业中较早的恢复步骤)。
- 导入:
- name: Import <source> into flaker if: always() run: | pnpm exec flaker import <report-file> \ --adapter <name> \ --commit "${{ github.sha }}" \ --branch "${{ github.ref_name }}" \ --source ci - 作业结束时的缓存保存会自动包含新增记录。
if: always() 可确保即使运行部分失败,也会记录失败前已获取的数据。
不要这样做
- 不要在运行 flaker 之前将
.flaker/data预创建为目录。 DuckDB 预期将其作为文件打开;IO Error: Could not read from file ... Is a directory就是这种情况的表现。缓存path: .flaker/data指向尚不存在的文件没有问题——如果该文件存在,actions/cache 会恢复它;如果不存在,flaker 会创建它。 - 添加
flaker import到仅处理 PR 的工作流前,请仔细考虑。 每次向 PR 提交提交都会触发 PR 运行,这会让数据量占主导。PR 作业应对缓存保持只读。 - 在 flaker ≥ 0.10.7 中不要使用
NODE_OPTIONS=--preserve-symlinks-main。 这曾是针对 pnpm 符号链接 Bug 的变通办法,该 Bug 已在 0.10.7 中修复;在 0.11.x 中,该环境变量会悄悄让 CLI 变成空操作,退出码为 0(没有输出,也不执行任何操作)。此问题通过下游使用情况暴露出来,现已从 flaker 上游移除该环境变量。 - 不要尝试从 PR fork 写入缓存。 出于安全原因,GH Actions 不允许 fork 写入;缓存仍会恢复,但保存操作会悄悄变成空操作。
诊断
如果历史记录“消失”:
- 检查工作流运行日志中的缓存命中行——如果显示
Cache not found for input keys,说明 restore-keys 前缀与已保存的内容不匹配。 - 检查
flaker.toml中的[storage] path是否与缓存的path:完全一致。 - 在同一个作业中运行
flaker doctor——确认文件可以打开。 flaker status --markdown会显示行数,让你查看实际加载了哪些内容。
参考
flaker自身的docs/contributing.md,了解存储路径约定。- 关于
--adapter系统的文档,了解如何编写自定义报告导入器。