代码仓度量指标统计解决方案
1. 功能入口
开发管理 → 代码仓管理 → 仓库分支管理 页面,每个分支行展示 5 项代码度量指标列。
2. 指标概览
| 指标名称 | 单位 | 含义 | 检测工具 | 支持语言 |
|---|---|---|---|---|
| 代码规模 | 行 | 仓库有效代码行数(排除空行和注释行) | scc | 200+ 种(scc 驱动) |
| 平均函数代码行 | 行 | 函数体有效代码行的算术平均值(排除函数签名行) | lizard | 26 种(lizard 驱动) |
| 平均圈复杂度 | — | 函数级圈复杂度的算术平均值 | lizard | 同上(lizard 驱动) |
| 总代码重复率 | % | 跨文件重复代码行占有效代码行的百分比 | 内置实现(行级滑动窗口 + md5 哈希,口径与 scc 一致) | 语言无关 |
| 总文件重复率 | % | 内容完全一致文件占总文件数的百分比 | 文件内容 md5 哈希比对 | 语言无关 |
💡 支持语言说明:5 项指标由不同能力驱动——代码规模走
scc(200+ 语言),平均函数代码行 / 平均圈复杂度走lizard(26 种语言),两项重复率走内置文本比对(语言无关)。因此能完整产出全部 5 项指标的语言即scc 与 lizard的支持交集。lizard未覆盖的语言仍可统计代码规模与两项重复率,但无法产出函数级指标。
lizard 支持(可完整扫描 5 项指标)的 26 种语言:
C/C++ C# Java JavaScript/JSX TypeScript/TSX Python Ruby PHP Go Rust
Kotlin Scala Swift Objective-C Lua Perl R PL/SQL Vue.js Erlang
Fortran GDScript Solidity Structured Text TTCN-3 Zig
💡 5 项指标含义速览:
- 代码规模:反映仓库的总体代码量,是评估项目规模的基础指标
- 平均函数代码行:反映函数粒度,过大的函数通常意味着可读性差、维护成本高
- 平均圈复杂度:反映代码分支密度,过高表示逻辑复杂、测试覆盖难度大
- 总代码重复率:反映代码段重复情况,过高表示存在可抽象/可合并的冗余代码
- 总文件重复率:反映完全一致文件比例,过高通常表示存在备份/拷贝代码
页面展示效果如图:


其中,总代码重复率可展示具体的重复代码片段:

3. 页面展示说明
3.1 概览页面
仓库分支管理页面以列表形式展示每个分支的最新一次扫描概览数据,每行代表一个分支的扫描记录:
| 展示字段 | 说明 |
|---|---|
| 代码仓名称 | 被扫描代码仓的别名 |
| 分支名称 | 被扫描的分支名 |
| 流水线运行编号 | 触发扫描的流水线运行编号,可点击跳转至 GitCode Actions 流水线详情页 |
| 5 项度量指标 | 每项指标以蓝色可点击链接形式呈现,点击下钻到文件级详情 |
| 检测完成时间 | 扫描完成的时间戳 |
⚠️ 各指标数值均为最新一次扫描结果,每个指标独立携带来源流水线信息;同一分支多次扫描仅展示最新一次结果。
3.2 下钻详情
点击概览行中的指标值,可下钻查看该次扫描中每个文件的详细度量数据。5 项指标各自展示不同的度量字段,点击流水线编号可跳转到对应的 GitCode Actions 流水线。
3.2.1 代码规模
| 字段 | 类型 | 说明 | 交互能力 |
|---|---|---|---|
| filePath | String | 文件名称(全路径) | 支持模糊搜索 |
| loc | Integer | 行数 | 支持排序 |
3.2.2 平均函数代码行
| 字段 | 类型 | 说明 | 交互能力 |
|---|---|---|---|
| filePath | String | 文件名称 | 支持模糊搜索 |
| functionName | String | 函数名 | — |
| functionLoc | Integer | 函数行数 | 支持排序 |
| startLine | Integer | 开始行 | 支持排序 |
| endLine | Integer | 结束行 | 支持排序 |
3.2.3 平均圈复杂度
| 字段 | 类型 | 说明 | 交互能力 |
|---|---|---|---|
| filePath | String | 文件名称 | 支持模糊搜索 |
| avgCyclomaticComplexity | Float | 平均圈复杂度 | 支持排序 |
3.2.4 总代码重复率
点击概览行"总代码重复率"下钻,展示每个文件的重复情况(重复率为 0 的文件不展示),默认按重复率降序排列:
| 字段 | 类型 | 说明 | 交互能力 |
|---|---|---|---|
| 文件名称(filePath) | String | 文件路径 | 支持模糊搜索、点击进入重复代码详情页 |
| 重复代码行数(duplicationLineCount) | Integer | 该文件的重复代码行数 | 支持排序 |
| 有效代码行数(totalLines) | Integer | 该文件的有效代码行数(loc) | 支持排序 |
| 重复率(duplicationRate) | Float | 重复代码行数 ÷ 有效代码行数 × 100% | 支持排序 |
| 重复代码块数量(duplicationBlockCount) | Integer | 该文件的重复代码块组数(按 group 去重) | 支持排序 |
点击"文件名称"列可进入该文件的重复代码详情页(见 3.2.6)。 代码重复率计算逻辑见 5.2。
3.2.5 总文件重复率
点击概览行"总文件重复率"下钻,按"每组一条记录"展示内容完全一致的文件组,前端将每组内的文件展开为多行:
| 字段 | 类型 | 说明 | 交互能力 |
|---|---|---|---|
| 重复文件序列号 | — | 展开后每行在列表中的序号(前端分页行号) | — |
| 文件名 | String | 内容完全一致的文件路径(同一组内的文件在相邻行展示) | 支持模糊搜索 |
⚠️ 展示说明:总文件重复率按"文件组"维度统计,一组完全一致的文件仅返回一条记录(含原始文件与重复副本),展示时展开为多行并赋予连续序号。
3.2.6 重复代码详情页
在"总代码重复率"详情中点击某行"文件名称",进入该文件的重复代码详情页。页面左侧展示整文件源码,右侧展示选中重复代码块的出现位置详情:
左侧:整文件源码视图
- 按快照(snapshot)渲染源码,非重复片段以省略号(
⋯ N lines omitted ⋯)占位,仅展示与重复相关的片段及5行上下文。 - 所有重复代码块以彩色条纹标注在行号左侧:当前选中的块为红色(
#e74c3c),其余块为黄色(#f1c40f)。 - 重叠的重复块会被分配到不同轨道,避免条纹重叠遮挡,可点击不同条纹进行切换。
- 交互:点击条纹或代码区中的重复行即可选中该重复块并打开右侧详情;顶部提供"上一个/下一个"按钮在重复块间循环切换。
右侧:重复代码块详情
- 以"源代码块"为基准,展示该重复块组中重复代码的其他出现位置(源代码块自身被排除)。
- 顶部以多页签展示每个出现位置,标签为
文件名(起始行-结束行);可通过页签或"上/下一个"按钮切换。 - 每个出现位置展示其代码内容(含上下文),并高亮重复区间。
- 联动:切换左侧选中块时,右侧高亮区与左侧选中块首行对齐滚动,便于对照重复内容。
- 点击放大/缩小可将重复代码详情页进行全屏切换。
4. 数据配置说明
页面上的指标数据由 code-metrics-action 插件通过 GitCode Actions 流水线扫描上报。以下说明如何配置以使数据出现在页面上。
4.1 工作流配置示例
最简接入:在仓库 .gitcode/workflows/ 下创建工作流文件,checkout 当前仓后调用插件即可。该插件已开源,流水线中可直接通过 openlibing/code-metrics-action@95623b0e88eaaed34eac28da21df52f4ac33a2b8 引用。
name: code-metrics-scan
on:
workflow_dispatch:
push:
branches:
- master
schedule:
- cron: "0 0 0 * * ?"
permissions:
id-token: write
jobs:
code-metrics-scan:
name: 代码度量检测
runs-on: ["codearts-hosted", "ubuntu-latest", "x64", "slim"]
steps:
- name: Checkout
uses: checkout
- name: code-metrics-scan
uses: openlibing/code-metrics-action@95623b0e88eaaed34eac28da21df52f4ac33a2b8
with:
# 可选:排除指定目录/文件,不传 exclude-dirs 默认全路径扫描
# exclude-dirs: "node_modules,target,dist,.git,.gitcode,.mvn,.trae,vendor"
# 可选:指定扫描的文件扩展名(覆盖式:只扫指定扩展名 + 文件名特例,
# 如 CMakeLists.txt / Makefile 等构建文件始终放行);
# 不传时按内置"常见代码扩展名白名单 + 文件名特例"扫描
# allowed-extensions: ".c,.cpp,.h"
4.2 触发方式
| 触发方式 | 配置关键字 | 适用场景 |
|---|---|---|
| 手动触发 | workflow_dispatch |
按需验证、首次接入验证 |
| 代码推送触发 | push.branches / pull_request.branches |
合入主分支时自动检测 |
| 定时任务触发 | schedule.cron |
Nightly 流水线,每日定期检测跟踪趋势 |
4.3 Action 输入参数
| 参数 | 必填 | 默认值 | 说明 |
|---|---|---|---|
| exclude-dirs | 否 | - | 排除目录/文件列表,逗号分隔,支持通配符,不填时默认扫描全部路径 |
| allowed-extensions | 否 | - | 允许的文件扩展名,逗号分隔。覆盖式语义:指定后只扫描这些扩展名的文件 + 文件名特例(CMakeLists.txt / Makefile / Dockerfile 等始终放行);不填时按内置缺省白名单(50+ 常见代码扩展名)+ 文件名特例扫描 |
| output | 否 | metrics.json | 结果输出文件路径 |
| apig-app-key | 否 | - | 已废弃:APIG 网关 AppKey(AK)。请使用新的 OIDC 认证方式(见下方「上传认证」),无需任何凭证 |
| apig-app-secret | 否 | - | 已废弃:APIG 网关 AppSecret(SK),与 apig-app-key 配套。请使用新的 OIDC 认证方式,无需任何凭证 |
| obs-ak | 否 | - | 已废弃:OBS 访问密钥 AK。请使用新的 OIDC 认证方式,无需任何凭证 |
| obs-sk | 否 | - | 已废弃:OBS 访问密钥 SK,与 obs-ak 配套。请使用新的 OIDC 认证方式,无需任何凭证 |
上传功能默认开启:全量上报文件(元数据 + 总体指标 + fileDetails + identicalFileDetails + duplicationOccurrences)先上传到 OBS,再通过 APIG 接口上报元数据与 OBS 下载链接。
上传认证
上传功能默认开启,插件使用 OIDC 联邦认证,无需任何凭证,workflow 中声明 permissions: id-token: write 即可(见 4.1 示例)。未适配的存量工作流保持原样即可:继续在 with 中传入 apig-app-key / apig-app-secret(APIG)与 obs-ak / obs-sk(OBS),插件走旧接口和 AK/SK 认证,行为与升级前完全一致。
存量 AK/SK 模式需在 项目设置 -> Actions 密钥与变量 -> 仓库密钥 中配置对应密钥,如下:

排除规则(exclude-dirs)
exclude-dirs 用逗号分隔,支持通配符,大小写敏感,匹配目标为条目相对扫描根的路径:
| 写法 | 匹配范围 |
|---|---|
裸名称(如 test.ts、docs) |
仅扫描根顶层同名条目 |
通配符名称(如 *.test.js、*test*) |
任意层级同名条目 |
路径(如 src/opensource/grapesjs、src/opensource/*) |
锚定扫描根,匹配相对路径,命中目录即整棵子树剪枝 |
通配符说明:* 匹配单段内任意字符(不含 /)、** 跨目录、? 单字符、[abc]/[!abc] 字符类。前导 / 为可选锚定写法(/test.ts 仅匹配顶层),尾部 / 忽略(docs/ ≡ docs)。目录与文件统一适用同一条规则。
示例:exclude-dirs: "*.test.js,src/opensource/*" 排除任意层级的 .test.js 文件和 src/opensource 直属子目录。
💡 扫描输入形态说明:目录扫描时,路径式规则锚定扫描根、命中目录即整棵子树剪枝;直接以单个文件作为扫描输入时,路径式规则按调用方传入的相对路径匹配——若传入的是与规则同基准的相对路径即可命中,否则仅文件名(裸名称 / 通配符名称)规则生效。
扫描范围示例
通过 exclude-dirs、allowed-extensions 控制哪些文件参与指标计算。典型值:
exclude-dirs: "node_modules,target,dist,.git,.gitcode,.mvn,.trae,vendor,__pycache__,scripts,.idea,.vscode,openspec"
allowed-extensions: ".js,.jsx,.ts,.tsx,.vue,.py,.java,.go,.c,.cpp,.h"
💡 留空与显式指定的差异:
allowed-extensions留空:按内置缺省白名单(50+ 常见代码扩展名)+ 文件名特例扫描,已天然排除二进制、文档等非代码文件- 显式指定:覆盖式,扫描范围 = 指定扩展名 + 文件名特例(不再叠加缺省白名单),适合只关注特定语言的场景
4.5 Action 输出
扫描完成后,以下输出可用于后续 step 引用(steps.<id>.outputs.<name>):
| 输出 | 说明 |
|---|---|
code-scale |
代码规模(有效代码行数) |
avg-function-loc |
平均函数代码行数 |
avg-cyclomatic-complexity |
平均圈复杂度 |
total-code-duplication-rate |
代码重复率(%) |
total-file-duplication-rate |
文件重复率(%) |
5. 指标计算规则
5.1 计算方式
| 指标 | 计算公式 | 工具 |
|---|---|---|
| 代码规模 | 所有有效文件的代码行数之和(排除空行、注释行) | scc |
| 平均函数代码行 | 所有函数的代码行数之和 ÷ 函数总数(排除函数签名行) | lizard |
| 平均圈复杂度 | 所有函数的圈复杂度之和 ÷ 函数总数 | lizard |
| 总代码重复率 | 重复代码行数 ÷ 有效代码行数 × 100% | 内置实现(行级滑动窗口 + md5 哈希,口径与 scc 一致) |
| 总文件重复率 | 内容完全一致的文件对数 ÷ 文件总数 × 100% | md5 哈希 |
5.2 指标口径说明
以下口径与 openlibing-code-metrics-action 插件的指标说明保持一致:
- 有效代码行:非空非注释代码行。C/C++ 预处理指令(
#include/#define等)计入代码行。 - 函数体有效代码行:不包含函数名和函数参数在内的有效代码行。
- 重复代码:非空非注释源代码行连续 10 行相同的代码片段即为重复代码(import 行也计入)。
- 代码重复率:重复代码行占有效代码行的百分比。
- 文件级代码重复率:将该文件的代码与代码仓中所有文件的代码进行比对,重复代码行数占该文件有效代码行数的百分比。
- 总文件重复率:内容完全一致的文件数 ÷ 仓库总文件数 × 100%。若文件 A 与文件 B 内容完全一致,则 A 和 B 均计入分子(共 2 个文件),源文件本身也计入比率。
5.2.1 重复代码计数规则
- 多文件相同重复块:若 3 个文件都包含相同的 10 行重复代码,则总代码重复行数 +30(每个文件的 10 行均计入)。
- 同文件多段重复取并集:若 A 文件第 1-10 行与 B 文件重复,第 2-11 行与 C 文件重复,则 A 文件的重复代码行数取两段重复区间的并集,即第 1-11 行共 11 行;此时总代码重复行数为 31(A 的 11 行 + B 的 10 行 + C 的 10 行)。
5.2.2 重复代码检测说明
代码重复率检测为插件内置实现,分两阶段完成:
第一阶段:行级滑动窗口 + MD5 哈希(初筛)
- 将源代码按行切分,过滤空行与注释行,得到有效代码行序列
- 以固定窗口(连续 10 行)在行序列上滑动,对每个窗口内容计算 md5 哈希
- 哈希相同的窗口即为候选重复代码片段
第二阶段:两两代表文件最大连续相同行匹配 + union-find 分组(精准分组)
- 对每个哈希值下的候选文件,选取代表文件,进行两两最大连续相同行(maximal-run)匹配
- 以
(文件, 索引区间)为节点建立 union-find,将相同/重叠的重复区间合并为独立组 - 每组即为一个独立的重复代码块组,包含该组内各文件的行号区间
- 分组结果互不干扰:同一文件的不同重复区间可分别属于多个组,每组独立输出
重复率计算
- 汇总所有重复的有效代码行(分子)÷ 有效代码行(分母,来自
scc) = 重复率 - 同文件多段重复取并集后再计入分子(避免同文件内重复区间重叠时重复计数)
文件重复率独立于代码重复率,采用文件内容归一化后的 md5 哈希两两比对,找出内容完全一致的文件。
5.3 各指标解读
| 指标 | 健康范围 | 偏高风险 | 处理建议 |
|---|---|---|---|
| 平均函数代码行 | 5 ~ 30 行 | > 50 行 | 拆分为职责单一的小函数 |
| 平均圈复杂度 | 1 ~ 10 | > 15 | 简化分支逻辑,使用早返回或策略模式 |
| 总代码重复率 | 0% ~ 5% | > 10% | 抽取公共方法/工具类,消除复制粘贴代码 |
| 总文件重复率 | 0% | > 0% | 检查是否存在备份/拷贝文件 |
⚠️ 代码规模无固定健康范围,与项目阶段、模块数量相关;建议关注趋势变化而非绝对值。
6. 插件输出文件示例
扫描完成后生成的 metrics.json 完整结构:
{
"codeScale": 12345,
"avgFunctionLoc": 15.3,
"avgCyclomaticComplexity": 3.2,
"totalCodeDuplicationRate": 6.05,
"totalFileDuplicationRate": 2.1,
"fileDetails": [
{
"filePath": "src/main/App.java",
"language": "Java",
"loc": 120,
"functionCount": 5,
"avgFunctionLoc": 12.4,
"avgCyclomaticComplexity": 2.8,
"functionDetails": [
{
"functionName": "MyClass.myMethod",
"functionLoc": 15,
"startLine": 42,
"endLine": 60
}
],
"duplicationRate": 15.5,
"duplicationLineCount": 18,
"snapshotData": "eyJ0b3RhbExpbmVzIjoxMjAs..."
}
],
"identicalFileDetails": [
{
"duplicatedFiles": [
"src/main/App.java",
"src/copy/App.java",
"src/backup/App.java"
]
}
],
"duplicationOccurrences": [
{
"groupId": "a1b2c3d4...",
"contentHash": "a1b2c3d4...",
"occurrenceIndex": 0,
"filePath": "src/main/App.java",
"startLine": 42,
"endLine": 51,
"contentB64": "aW50IG1haW4oKXs..."
}
],
"detectionInfo": {
"startTime": "2026-06-30 10:00:00",
"endTime": "2026-06-30 10:05:00",
"durationMs": 300000,
"sources": ["/path/to/source"],
"tools": { "sloc": true, "lizard": true, "duplication": true }
},
"uploadInfo": {
"success": true,
"recordId": 42
}
}
7. 常见问题(FAQ)
7.1 插件的扫描原理是什么?
插件通过以下流程完成代码度量检测:
- 代码规模统计:使用内置
scc二进制扫描源代码目录,统计有效代码行数(排除空行、注释行) - 函数级分析:通过
lizard解析源码 AST,提取每个函数的代码行数和圈复杂度 - 重复代码检测:插件内置实现,行级滑动窗口 + md5 哈希识别跨文件及同文件内的重复代码块(口径与
scc一致) - 文件级重复检测:对文件内容归一化后做 md5 哈希比对,找出内容完全一致的文件
- 汇总上报:全量指标及文件级明细先上传到 OBS,再通过 APIG 单接口上报元数据与 OBS 下载链接到 openLiBing
7.2 scc / lizard 是业界通用的吗?
是的,二者均为业界广泛使用的代码度量工具:
- scc:Go 实现的代码行数统计工具,支持 200+ 语言,性能优于 cloc,被众多 CI 系统采用
- lizard:Python 实现的函数复杂度分析工具,支持 20+ 语言,社区活跃
- 重复代码检测:为插件内置实现,无需外部依赖,口径与
scc完全一致
7.3 指标值异常应该怎么处理?
Step 1:定位问题文件
在仓库分支管理页面,点击异常指标值下钻到文件级详情,按对应字段倒序找出 top N 异常文件。
Step 2:区分问题类型
| 指标 | 典型原因 | 处理方式 |
|---|---|---|
| 代码规模偏大 | 未排除生成目录(target/dist/node_modules) | 在 exclude-dirs 中补充排除目录 |
| 平均函数代码行偏高 | 函数职责过多、缺少拆分 | 拆分为职责单一的小函数 |
| 平均圈复杂度偏高 | 多层嵌套 if-else、复杂条件分支 |
使用早返回、策略模式、表驱动简化分支 |
| 代码重复率偏高 | 复制粘贴代码、缺少抽象 | 抽取公共方法/工具类 |
| 文件重复率偏高 | 存在备份文件(如 App.java.bak、App_old.java) |
清理冗余备份文件 |
Step 3:修改代码后重新扫描
修改代码后通过 workflow_dispatch 手动触发流水线,或等待 push 触发自动扫描,验证指标改善效果。
7.4 哪些文件会被扫描?
文件按"三段式"规则决定是否参与扫描,优先级从高到低:
- 文件名特例(始终放行):
CMakeLists.txt、Makefile、GNUMakefile、Dockerfile、Jenkinsfile、Rakefile、Gemfile、build、build.bazel、WORKSPACE等按文件名识别的构建/入口文件。 allowed-extensions显式指定(覆盖式):指定扩展名后,扫描范围 = 指定扩展名 + 文件名特例,不再叠加缺省白名单。例如指定.h,.cpp,则只扫描这两类扩展名的文件(外加 CMakeLists.txt 等特例文件)。- 缺省代码扩展名白名单(未配置时生效):内置 50+ 常见代码扩展名,覆盖主流语言:
| 语言 | 扩展名 |
|---|---|
| Java | .java |
| Python | .py |
| JavaScript / TypeScript | .js .jsx .ts .tsx |
| Vue | .vue |
| Go | .go |
| C / C++ | .c .cpp .h .hpp |
| Rust / Swift / Kotlin | .rs .swift .kt |
| Shell / 脚本 | .sh .bash .ps1 |
| 数据 / 配置 | .json .xml .yml .sql .proto |
| 构建体系 | .cmake .mk .gn .gni .prelds |
💡 留空即有合理缺省:不配置
allowed-extensions时按缺省白名单 + 文件名特例扫描,二进制、文档等非代码文件天然排除;只需关注特定语言时再显式指定(覆盖式)。
7.5 多分支如何扫描?
每个分支独立配置工作流(或在同一工作流中以分支名区分),插件扫描时会上报当前分支名(由 GitCode Runner 自动注入),平台按分支维度展示扫描结果。
同一分支多次扫描时,仅展示最新一次结果,历史扫描数据可通过GitCode流水线回溯。
8. 依赖说明
| 依赖 | 类型 | 安装方式 | 用途 |
|---|---|---|---|
| scc | 内置二进制(dist/bin/scc) |
无需安装 | 代码行数统计 |
| lizard | Python 包 | pip install lizard |
函数复杂度分析 |
| 重复检测 | 插件内置实现 | 无需安装 | 代码重复检测 |