2. GitLab数据模型与API基础

要做交付效率度量,第一步不是写查询,而是搞清楚“度量对象在 GitLab 里长什么样”。GitLab 把研发交付过程抽象成一组相互引用的核心对象:Project 是容器,Issue 描述需求与缺陷,Merge Request(MR)承载代码变更与评审,Pipeline 与 Job 记录每次提交的构建测试结果,Deployment 与 Environment 记录发布去向。这些对象通过 ID、IID、引用路径彼此关联,构成了度量数据的骨架。

核心对象的数据结构

理解对象结构的关键,是分清“全局 ID”和“项目内 IID”。Project 用全局唯一的数字 id 标识,也可以用 path_with_namespace(如 group/subgroup/app)作为可读引用。Issue 和 MR 则同时拥有全局 id 与项目内 iid,日常看到的 !128、#45 都是 IID。度量脚本里如果混用两者,很容易取错对象,因此建议统一用 project_id + iid 定位。

Issue 的关键字段包括 title、state(opened/closed)、created_at、closed_at、labels、author、assignees、milestone、weight。其中 created_at 到 closed_at 的差值就是需求交付周期的基础数据,而 labels 常被用来区分需求类型或优先级。

Merge Request 的字段更贴近交付效率:source_branch、target_branch、state(opened/merged/closed/locked)、created_at、merged_at、closed_at、merge_commit_sha、diff_stats(增删行数)、changes_count、reviewers、pipeline。从 created_at 到 merged_at 是 MR 存活时间,从首次提交到合并则反映评审等待。MR 上挂载的 pipeline 字段直接指向该变更对应的流水线,是打通“代码变更—构建结果”的桥梁。

Pipeline 由 id、status(created/running/success/failed/canceled/skipped)、ref、sha、created_at、started_at、finished_at、duration、queued_duration 组成。Job 是 Pipeline 的子对象,带有 stage、name、status、started_at、finished_at、runner。Deployment 则记录 environment、ref、sha、status(success/failed/running/canceled)、created_at、deployed_at、user,它把“代码合并”与“真正上线”连接起来。

GitLab 核心对象关系与度量时间字段 Project id / path_with_namespace default_branch 容器:承载全部对象 Issue iid / state / labels created_at → closed_at 需求交付周期 Merge Request iid / state / diff_stats created_at → merged_at MR 存活时间 Pipeline / Job status / ref / sha duration / queued_duration 构建测试结果 Deployment environment / status created_at → deployed_at 发布去向与结果 Environment name / tier production / staging 发布目标 包含 关联 触发 产出 部署到 关闭 度量链路: Issue 创建 → MR 提交 → Pipeline 执行 → MR 合并 → Deployment 上线 关键时间戳:created_at / merged_at / started_at / finished_at / deployed_at,是计算周期、等待、时长的原始依据。

REST API 与 GraphQL 的取舍

GitLab 同时提供 REST 与 GraphQL 两套接口。REST 以资源路径组织,语义直观、调试方便,适合按对象逐个拉取;GraphQL 以单一端点 /api/graphql 暴露,允许一次请求取回嵌套对象,适合批量聚合与减少往返。

REST 的典型调用如下,用私有令牌访问项目下的 MR 列表:

curl --header "PRIVATE-TOKEN: <your_token>" \
  "https://gitlab.example.com/api/v4/projects/123/merge_requests?state=merged&per_page=100&page=1"

返回体是 JSON 数组,每个元素包含 iid、created_at、merged_at、author 等字段。分页通过 per_page 与 page 控制,响应头 X-Total-Pages 给出总页数。若要取某个 MR 的流水线,可继续请求 /projects/123/merge_requests/128/pipelines。

GraphQL 则把对象关系写进查询。下面这段查询一次取回项目下已合并 MR 及其流水线状态:

query {
  project(fullPath: "group/app") {
    mergeRequests(state: merged, first: 50) {
      nodes {
        iid
        createdAt
        mergedAt
        diffStatsSummary { additions deletions }
        headPipeline { status duration }
      }
    }
  }
}

可以看到,GraphQL 用 nodes 表达列表,用嵌套字段表达关联,避免了 REST 中“先取 MR 再逐个取 Pipeline”的 N+1 请求。代价是查询语法更严格,字段名与 REST 不完全一致,需要对照 schema 编写。

分页、限流与增量采集

无论用哪套接口,都要处理三个工程问题。第一是分页:REST 用 page/per_page,GraphQL 用 first/after 游标,采集脚本必须循环直到无下一页。第二是限流:GitLab 默认对认证用户有每分钟请求数限制,超限返回 429,应在脚本中加入退避重试。第三是增量:度量任务不应每次全量拉取,而应记录上次采集的最大 updated_at 或 id,下次只取增量。

建议为度量任务单独创建一个只读的 Project Access Token 或 Group Access Token,权限限定为 read_api,避免使用个人令牌导致人员离职后采集中断。

从对象到指标:一次完整取数示例

假设要计算某项目最近一批已合并 MR 的“提交到合并时长”和“流水线成功率”。采集流程是:先用 REST 拉取 state=merged 的 MR 列表,拿到每个 MR 的 iid、created_at、merged_at;再对每个 MR 请求其 pipelines,取最新一条的 status 与 duration。若改用 GraphQL,则上述两步合并为一次查询。

得到数据后,用 merged_at - created_at 得到 MR 存活时长,用 status == success 的比例得到流水线成功率。需要注意时区:API 返回的时间是 UTC 的 ISO 8601 字符串,计算前应统一转换,避免跨时区项目出现偏差。

不要把 created_at 当作“首次提交时间”。MR 的 created_at 是 MR 被创建的时刻,若团队习惯先推分支后开 MR,两者会相差数小时甚至数天,直接影响评审等待时长的判断。

对象关系带来的度量视角