关于 dora-metrics
dora-metrics 获取已配置项目的部署频率和变更前置时间数据,并按仓库分别报告。它读取 GitHub、GitLab、Bitbucket 和 Azure 的提供方 API,支持配置部署标记并可生成 Markdown 报告。
主要功能
- 01测量部署频率和变更前置时间
- 02支持四种代码仓库提供方
- 03可配置 Release、标签或合并标记
- 04按仓库独立报告指标
- 05输出摘要并可保存 Markdown 报告
适用场景
- 01校准项目的部署指标
- 02测量跨多个提供方的仓库
- 03跟踪以合并为标记的部署节奏
- 04保存每个仓库的指标报告
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ somnio-software/ somnio-ai-tools --skill dora-metrics安装后在 Claude Code 中输入/dora-metrics确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
README.mdagents/report-writer.mdassets/report-template.mdconfig/projects.jsonevals/evals.jsonreferences/deployment-frequency.mdlead-time-for-changes.mdpractice-guidance.mdtroubleshooting.mdscripts/dora_metrics.pypractice_guidance.pytroubleshooting.pytests/test_dora_metrics.pytest_practice_guidance.pytest_troubleshooting.pytests/e2e/run_e2e.py详细介绍
DORA 指标 — 部署频率和变更前置时间
此技能仅获取并汇总数据——不会解读、排名或比较人员或项目。这是单独的后续步骤,不在此技能的范围内。
背景
每个项目都会衡量两项 DORA 指标:部署频率(我们部署到生产环境的频率)和变更前置时间(一项变更到达生产环境需要多长时间)。这是校准阶段:目标是让团队获得一个一致的数字,供团队自行采取行动,而不是用来评估人员。获取数据和解读数据是刻意分开的步骤:一旦某项指标被用于评估人员,它就不再是一个好的指标(古德哈特定律)。
CI 是统一的(GitHub Actions),但 CD 各不相同(移动端、Web 和后端的部署方式不同),因此我们不衡量实际的 CD,而是使用统一的标记:每个仓库的生产分支上的带有 semver 标签 vX.Y.Z 的 Release(提供方没有 Releases API 时使用普通标签);或者,对于那些集成分支在每次合并时都会自动部署,且既不发布 releases 也不发布标签的仓库,使用合并到该分支的每个 PR/MR(deploy_source: "merge")。所有数据都来自仓库的提供方 API——绝不使用本地 git 克隆——因为前置时间取决于每个 PR/MR 的真实首个提交,只有从提供方的 PR/MR 对象中读取才能确保可靠(即使采用 squash 合并,数据仍然准确;main 的 git 日志无法保证这一点)。
每个仓库都属于四种受支持的提供方之一,在 config/projects.json 中为每个仓库声明(provider,默认值为 "github"):
| 提供方 | 仓库标识符格式 | 部署标记 |
|---|---|---|
github(默认) | org/repo | GitHub Release、普通标签或合并 |
gitlab | 项目路径,例如 group/project 或 group/subgroup/project | GitLab Release、普通标签或合并 |
bitbucket | workspace/repo_slug | 普通标签或合并——Bitbucket Cloud 没有 Releases API |
azure | organization/project/repository(Azure DevOps) | 普通标签或合并——Azure DevOps 没有 Releases API |
每个仓库的 deploy_source 用于选择标记:"release"(GitHub 和 GitLab 的默认值)、"tag"(Bitbucket 和 Azure DevOps 的默认值)或 "merge"。使用 "merge" 时,测量窗口内合并到 prod_branch 的每个 PR/MR 都算作一次部署,日期按其合并时间确定,前置时间从首个提交算到合并时间——这适用于每次合并都会自动部署、且没有发布或打标签流程的分支(develop、staging)。这会改变该数字的含义:它衡量的是集成/CD 节奏,而非生产环境发布,因此报告时请说明这一点。
单个项目——甚至单次运行——都可以在不同仓库中混用不同提供方:项目的前端可以托管在 GitHub 上,而后端托管在 GitLab 上,每个仓库都使用自己的凭据进行衡量。Bitbucket Server/Data Center、Azure DevOps Server(本地部署)以及超出 api_base 覆盖范围的 GitHub Enterprise 均不在支持范围内——此工具连接的是 GitHub.com、GitLab.com(或通过 api_base 连接可自托管实例)、Bitbucket Cloud 和 dev.azure.com。
项目可以采用单仓库或多仓库模式。在多仓库模式下,每个仓库都会独立衡量和报告,绝不合并:如果一个项目的前端和后端在不同时间部署——或部署在不同的提供方上——将它们合并为一个数字会掩盖校准原本要揭示的信号。
每项指标的正式定义(属性、总体、精确计算方法):references/deployment-frequency.md 和 references/lead-time-for-changes.md。
输入
- 项目名称(例如“示例项目”)。如果用户未指定,则运行
config/projects.json中的所有项目。 - 测量窗口:默认固定为 14 天(配置项
window_days)——除非用户明确要求其他设置,否则不要询问。
输出
-
在控制台以人类可读的形式显示摘要,按项目和仓库分别列出:部署频率、中位交付时间和边缘情况警告。
-
可选:如果用户要求保存结果,则在
--out-dir指定的文件夹中为每个仓库生成一个 Markdown 报告文件(YYYY-MM-DD-<repo>-dora-metrics.md),内容为相同的摘要——便于单独打开和阅读。仓库是衡量单位(绝不会与同项目的其他仓库合并),因此也是保存单位:多仓库项目会为每个仓库生成一个文件,每个文件仅包含对应仓库的数据。
<repo>是将仓库名称转换为 kebab-case 后的标识——example-org/example-frontend会变成example-frontend——仅当同一次运行中的两个仓库名称相同时,才会回退为包含组织名的example-org-example-frontend。
工作流
步骤 1 — 确认项目
读取 config/projects.json。按名称查找请求的项目(不区分大小写)。
如果项目不在配置中:不要编造仓库,也不要假定映射关系。不要就此停止并让用户去编辑另一个文件,而是直接询问用户缺少的信息,并由你将其添加到 config/projects.json:
- 项目名称。
- 构成项目的一个或多个仓库(多仓库),以及每个仓库使用的提供商(
github、gitlab、bitbucket或azure——默认使用github,但用户未说明时要确认,不能自行假定)。标识符格式取决于提供商:GitHub 使用org/repo,GitLab 使用项目路径,Bitbucket 使用workspace/repo_slug,Azure DevOps 使用organization/project/repository。 - 每个仓库的类型(web / mobile / backend)。
- 每个仓库的生产分支(合理默认值为
main,但要确认,不能自行假定)。 - 可选:仓库是否使用普通标签而非 Releases(
deploy_source: "tag");是否将每次合并到prod_branch都计为一次部署(deploy_source: "merge"——适用于自动部署且没有发布/打标签流程的分支);或是否使用与全局设置不同的tag_pattern。默认情况下继承全局tag_pattern;GitHub/GitLab 默认使用deploy_source: "release"——但 Bitbucket 和 Azure DevOps 没有 Releases API,因此默认使用deploy_source: "tag"(不接受"release";"merge"则可以)。 - 可选:如果仓库不在 github.com / gitlab.com 上,可填写自托管的 GitHub Enterprise 或 GitLab 实例的
api_base。Bitbucket 和 Azure DevOps 不支持此项(仅支持云端)。 - 可选:如果项目有不显而易见之处,可添加简短备注(例如,多个仓库分属不同组织、各仓库的部署相互独立、仓库分布在不同提供商上);备注会写入项目的
"notes"字段。如果没有特别需要说明的内容,则无需填写。
保存前先显示生成的 JSON 并征求确认(该文件由整个团队共用)。保存后,照常继续执行步骤 2。
步骤 2 — 显示确认表
调用 API 前,先显示一张表,列出即将衡量的内容。与生成文档的技能不同(此类技能在创建难以撤销的内容前必须先通过表格确认),此处的表格仅供参考:该技能只读取数据并生成报告,因此除非发现异常情况(例如,不应包含的仓库或不合理的分支),否则可以直接继续。无论如何都要显示该表,让执行者一目了然地了解衡量范围。始终包含 Provider 列——即使所有仓库都使用 GitHub,这也毫不费力;而在项目混用多个提供商时,这一列尤为重要:
| 项目 | 仓库 | 提供商 | 类型 | 生产分支 | 时间范围 |
|---|---|---|---|---|---|
| Example Project | example-org/example-frontend | github | web, mobile | main | 最近 14 天 |
| Example Project | example-partner-org/example-backend | github | backend | main | 最近 14 天 |
如果表格中的内容与用户预期不符,请先停止并询问,再运行脚本。
步骤 3 — 验证身份验证
脚本(scripts/dora_metrics.py)需要为确认表中出现的每个提供商提供凭据,且每个凭据都必须对该提供商在项目中的仓库所属的所有组织/群组/工作区具有读取权限(例如,如果 GitHub 涉及多个组织:example-org 和 example-partner-org)。各提供商的优先级顺序如下,自动选择:
| 提供商 | 优先级 1 | 优先级 2 |
|---|---|---|
github | GITHUB_TOKEN 环境变量 | gh auth token(已登录的 GitHub CLI) |
gitlab | GITLAB_TOKEN 环境变量 | glab auth token(已登录的 GitLab CLI) |
bitbucket | BITBUCKET_TOKEN 环境变量(bearer) | BITBUCKET_USERNAME + BITBUCKET_APP_PASSWORD(必须同时提供;无 CLI 回退选项) |
azure | AZURE_DEVOPS_PAT 环境变量(具有 Code: Read 权限的 PAT) | 针对 Azure DevOps 资源运行 az account get-access-token(已登录的 Azure CLI) |
一次运行可以混用多个提供商:对于没有可解析凭据的提供商,其仓库会报告为 measured: false,并附带 no_credential 问题(格式与其他无法测量的仓库相同);而有凭据的提供商所对应的所有仓库仍会正常测量。 只有当表中没有任何提供商具有凭据时,脚本才会完全跳过测量,并生成一份仅包含该问题的报告——仍以代码 1 退出,仍会生成输出(如果传入了 --out-dir,也会保存输出)。
像报告其他问题一样报告缺少凭据的情况——如果流程使用 Cowork,不要让用户在聊天中粘贴令牌;如果使用本地 Claude Code,且用户尚未登录,请建议运行 gh auth login / glab auth login / az login(Bitbucket 没有可建议的 CLI 登录方式,请改为指出相关环境变量)。
步骤 4 — 运行脚本
pip install requests --break-system-packages # if needed
python3 scripts/dora_metrics.py --project "Example Project" --out-dir reports
可用标志:
--config:配置文件路径(默认值:config/projects.json)。--project:精确的项目名称(默认值:运行配置中的所有项目)。--out-dir:需要报告时建议使用(可选)。如果传入此参数,除向 stdout 打印外,还会在该目录下为每个仓库保存一个文件——YYYY-MM-DD-<repo>-dora-metrics.md(与可读文件中的摘要相同)。请注意,此参数用于选择保存位置,而--project用于选择要测量的对象;文件名取自仓库,而非项目。每个仓库单独保存的文件就是要分享的产物:按原样上传,每个仓库一个文件,切勿合并。--branch <branch>:仅对本次运行临时覆盖prod_branch(需要--project)。不会修改配置——仅用于针对不同于已配置分支的分支进行一次性测试。--deploy-source {release,tag,merge}:仅对本次运行临时覆盖deploy_source(需要--project)。不会修改配置。--provider {github,gitlab,bitbucket,azure}:仅对本次运行临时覆盖provider(需要--project)。不会修改配置——此设置会应用于该项目中的每个仓库,因此仅适用于单一提供商的项目。--window-days N:仅对本次运行临时覆盖窗口天数。不会修改配置。
退出代码 1 不表示运行失败:它表示有内容无法测量(impact: blocked),但报告仍已生成——请从 stdout(或已保存的文件)中读取报告,并按照步骤 5 的说明原样报告给用户,与其他问题的处理方式相同。不要因为 Bash 返回非零退出代码就丢弃输出或告知用户运行失败。唯一不会生成任何报告的情况是用法错误——未知的 --project、未搭配 --project 使用的 --branch/--deploy-source/--provider、无效的 deploy_source 或 provider、在 Bitbucket 或 Azure 仓库上设置 deploy_source: "release"、Azure 仓库未按 organization/project/repository 格式填写,或在 Bitbucket 或 Azure 仓库上设置 api_base——这些情况会向 stderr 打印错误,并在进行任何测量之前以代码 1 退出。
config/projects.json 字段参考(也记录在 README.md 中,供打开此文件夹的人查看;此处作了简要说明,确保即使安装时只包含 SKILL.md,此技能也能独立使用):
| 字段 | 层级 | 省略时的默认值 | 含义 |
|---|---|---|---|
tag_pattern | 全局 | —(必填) | 标签必须匹配的正则表达式,匹配后才计为一次部署。 |
window_days | 全局 | —(必填) | 测量窗口的天数。 |
projects[].name | 项目 | —(必填) | 用于查找项目的名称(不区分大小写)。 |
projects[].notes | 项目 | 无 | 自由文本:该项目特有的理由或说明。 |
repos[].repo | 仓库 | —(必填) | 仓库标识符,格式取决于 provider:org/repo(github)、项目路径(gitlab)、workspace/repo_slug(bitbucket)、organization/project/repository(azure)。 |
repos[].provider | 仓库 | "github" | "github"、"gitlab"、"bitbucket" 或 "azure"。 |
repos[].type | 仓库 | [] | 信息性列表(web/mobile/backend),仅用于输出中的展示。 |
repos[].prod_branch | 仓库 | —(必填) | 该仓库的生产分支。 |
repos[].deploy_source | 仓库 | "release"(github/gitlab)、"tag"(bitbucket/azure) | "release" = 带有 semver 标签的 Release(Bitbucket 或 Azure DevOps 不支持)。"tag" = 不带 Release 的普通标签,适用于打标签但不发布 Release 的项目。"merge" = 窗口期内合并到 prod_branch 的每个 PR/MR 均计为一次部署(适用于任何提供方)。 |
repos[].tag_pattern | 仓库 | 全局 tag_pattern | 如果该仓库使用不同的标签格式,则覆盖此项(例如,包含构建编号)。 |
repos[].api_base | 仓库 | 提供方的公共 API 根地址 | 自托管 GitHub Enterprise 或 GitLab 实例的 API 根地址。bitbucket 或 azure 不支持(仅限云端)。 |
步骤 5 — 报告
不要自行编写面向用户的摘要。调用 report-writer 子 Agent(通过 Agent 工具),将脚本打印到 stdout 的 JSON(以及使用了 --out-dir 时保存的文件路径)传给它,并让它按照 assets/report-template.md 生成回复。
调用时使用的模型(可配置): 默认使用 model: sonnet 调用 report-writer。如果用户明确要求更快或更省钱,则使用 model: haiku。如果用户明确要求更全面,则使用 model: opus。不要自行切换模型;仅在用户明确提出要求时切换。
report-writer 只负责格式化数字;绝不会解读、排名、评分或比较项目、仓库或人员。它必须遵循的规则(且此步骤可确保这些规则得到遵守)如下:
- 始终在聊天中直接回复每个仓库的两个数值(部署频率和中位前置时间),即使 JSON 也已保存——绝不能只回复“我已保存文件,请查看文件”。
- 按脚本生成的原样展示数值——不要重新解读或排序,那是后续步骤,不属于此技能的范围。
- 脚本在测量时会诊断仓库的设置:凭据能否访问仓库、
prod_branch是否存在、是否存在部署标记,以及它们是否与tag_pattern或配置的deploy_source匹配。发现的每个问题都会在issues中返回,并附带从references/troubleshooting.md读取的修复步骤。report-writer会逐字呈现每条message,并在其下方列出步骤——问题(影响为blocked/partial)和备注(影响为none)分别置于不同标题下。它不会查找任何信息,也不会自行编写步骤:若某个问题的guidance: null,则会报告该问题没有指导信息。这些是本校准阶段要揭示的流程差距信号,而不是应当隐藏的噪声;这些步骤严格聚焦于测量设置——绝不会评论某个数值是好是坏。 - 对于脚本完全无法测量的仓库,返回结果中会包含
measured: false,且不含指标字段。应如实报告,并列出问题——绝不能省略该仓库或用零代替。 - 如果文件已保存,除了报告数值之外,还要说明文件保存的位置(每个仓库对应的
.md文件)。 - 结果还包含一个固定的根级
practice_guidance目录——其中列出通常会影响变更前置时间或部署频率的工程实践,而非对本次运行数值的评判(以references/practice-guidance.md为准;脚本会不加筛选地附上完整目录,每次运行的条目都相同)。report-writer会将其呈现为**必需的“如何改进这些指标”**部分,每份报告仅出现一次,绝不能每个仓库都出现一次;置于最后一个项目章节之后,并按dimension分组,每个条目都必须与 JSON 中的内容完全一致。即使本次运行无法测量任何内容,也必须显示此部分,因为它与本次运行的数据无关。它绝不会根据测量结果选择、筛选、重排或省略条目,也不会自行添加实践建议;如果practice_guidance缺失或为空,仍须显示标题,并明确说明没有可用的指导信息,同时指出references/practice-guidance.md,绝不能编造条目。 - 回复必须以
## Report Metadata表格结尾,作为最后一个区块(“保存至”行应置于该表格之前)。脚本也会在每个已保存的.md文件末尾写入同一张表格。这是固定且不随运行变化的区块,不是对数值的解读,因此不与“仅报告 JSON 中的内容”相冲突:report-writer会根据 JSON 中的skill、generator_version、generated_at(日期部分)和window_days生成该表格。表格行包括Generated by(Somnio CLI v<generator_version>)、Skill(dora-metrics)、Date、Measurement window(N days)和Somnio AI Tools(仓库 URL)。请保持Generated by和Skill完全不变,因为报告处理管道会读取它们。
维护配置
config/projects.json 是项目 → 仓库映射的唯一事实来源——无需维护单独的文档以保持同步。新增项目可通过此工作流的第 1 步(与用户对话)完成,也可以直接编辑 JSON。此技能不会自行推断映射:如果映射缺失,它会提出询问。
已知限制(试点阶段)
- 仓库的首个历史 Release:不纳入 Lead Time 计算 (无法界定它之前的 PR/MR 范围)。
- 仅限 GitHub:使用 Search API(速率限制低于常规 REST API);有 1-2 个项目时应该不会有问题,但扩展到更多 项目可能需要分批处理/缓存。GitLab 和 Bitbucket 没有这种 特定的不对称问题,但 Bitbucket 有其下文所述的额外成本。
- 首次运行时 Lead Time 会偏高——这是校准期间的预期情况,在连续 3-4 个 正常窗口之前,不要将其视为性能表现。
deploy_source: "tag":如果 tag/release 在合并后 1-2 秒才创建 (例如,自动打 tag 的流水线),某个 PR/MR 可能会被排除,或被错误归到 下一个时间区间。详见scripts/dora_metrics.py的文档字符串。 对于实际发布节奏(数天/数周)则无关紧要。- 设置诊断每个仓库会额外调用 2 次 REST API(检查仓库 + 分支是否存在);只有在某个仓库未产生任何部署标记,且脚本需要证据来说明原因时, 才会再多调用 2 次。这些是开销很低的 REST API 调用,不是受速率限制的 Search API(仅限 GitHub)。
- GitLab 可自托管版本:在合并请求列表中使用
merged_after/merged_before筛选 需要 GitLab >= 13.1;较旧的实例可能需要 升级,才能正确测量 Lead Time。 - GitLab tags:当 API 返回 tag 自身的
created_at时,脚本会使用该时间为 tag 计时(附注释的 tag 会设置此值,轻量级 tag 则为 null);否则会回退到目标提交的日期。因此,在提交很久之后创建的轻量级 tag,会被视为发生在提交时。 - Bitbucket:完全没有 Releases API——所有 Bitbucket 仓库都通过普通
tags 进行测量(
deploy_source会被强制设为"tag")。其拉取请求对象也 没有直接的“合并时间”时间戳;脚本会在有合并提交时使用其日期,否则回退到 PR 的updated_on——这是合理的近似值,但不如 GitHub/GitLab 精确。由于没有服务器端的“在这两个日期之间合并”筛选器来将时间窗口限定在服务提供方侧,因此在 Bitbucket 上测量 Lead Time 比在 GitHub/GitLab 上需要更多 API 调用。 - Bitbucket 已删除的源分支/派生仓库:源分支删除后,PR commits 端点会返回空列表;源派生仓库删除后,则会返回 404;此时该 PR 会从 Lead Time 中排除
(
pr_first_commit_unfetchable),因此,合并时会删除分支的团队可能会看到大量排除项,或看到no data in the window。 详情:references/troubleshooting.md。 - Bitbucket Server/Data Center、Azure DevOps Server 和 GitHub Enterprise 不在支持范围内,GitHub Enterprise 的
api_base覆盖项除外;Bitbucket 和 Azure DevOps 仅支持云端版本。 - E2E 测试(
tests/e2e/)目前仅覆盖 GitHub 流程——GitLab、 Bitbucket 和 Azure DevOps 由使用模拟 HTTP 响应的单元测试覆盖,不进行实时的端到端测试。
重要说明
- 此技能只获取数据并报告。绝不解读、排名或比较人员——将数据获取与评估混在一起会污染数据 (古德哈特定律)。
- 多仓库:每个仓库单独测量并报告,绝不合并 ——即使仓库位于不同的服务提供方上也是如此。
- 唯一数据源:仓库自身的服务提供方 API(GitHub、GitLab 或 Bitbucket)。
绝不读取克隆仓库本地的
.git。 - 如果请求的项目不在
config/projects.json中,不要擅自编造—— 请询问详细信息并将其添加进去(步骤 1);绝不要自行假设仓库、分支或 服务提供方。 - 如果某个仓库的服务提供方没有可用凭据,则报告为
measured: false,并附带no_credential问题——当其他仓库所在的服务提供方有可用凭据时,它不会阻止其余部分继续运行。