AI代码覆盖率统计方案

1、开发环境:MacOS v14.2、VSCode v1.98.1
2、开发工具:Flutter v3.7.6、Dart v2.19.3
3、统计项目:Dream App v1.5.0

一、定义AI生成代码的特定标记(注释)

1. 代码块标记:// @AI_START// @AI_END
// @AI_START
  @override
  void dispose() {
    ScanCodeUtils().clear();
    super.dispose();
  }
// @AI_END

说明:被 // @AI_START// @AI_END 包裹的代码块,将被统计为AI生成代码。

2、单行代码标记:// @AI_GENERATE
  @override
  void dispose() {
    ScanCodeUtils().clear(); // @AI_GENERATE
    super.dispose();
  }

说明:被 // @AI_GENERATE 标记的代码行,将被统计为AI生成代码。

3、如何快速使用特定标记?

主流的开发工具都有代码片段(Code Snippets),开发者可以通过设定自定义的Snippets来快速完成相关代码的填充工作,以下介绍下VSCode的相关设置:
1、打开指令面板,搜索Snippets
2、选择Snippets: Insert Snippet
3、编写配置文件:

{
    "@AI_START": {
        "prefix": "@AI_START",
        "body": [
            "// @AI_START",
            "",
            "// @AI_END"
        ],
        "description": "快速生成AI代码块首尾标记(代码块)"
    },
    "@AI_GENERATE": {
        "prefix": "@AI_GENERATE",
        "body": [
            " // @AI_GENERATE",
        ],
        "description": "快速生成AI代码标记(单行)"
    }
}

4、使用效果:


二、统计代码覆盖率

此处主要统计新增代码的AI覆盖率,所以,采取的方案是:获取指定日期区间内,git提交记录中,AI代码总行数占提交总行数的比重

以下是完整扫描脚本(python):

import subprocess # 进程管理模块
import re # 正则表达式模块
import csv # CSV 文件处理模块
import os # 操作系统接口模块 
from datetime import datetime # 日期和时间处理模块

# 配置
start_date = "2025-03-12" # 开始日期(包含)
end_date = "2025-03-14" # 结束日期(不包含)
csv_path = "./ai_coverage.csv" # 输出文件

# 正则表达式
ai_start_pattern = re.compile(r'^\s*//\s*@AI_START')
ai_end_pattern = re.compile(r'^\s*//\s*@AI_END')
ai_generated_pattern = re.compile(r'//\s*@AI_GENERATED')

# 数据存储
total_ai = 0 # AI代码总行数
total_code = 0 # 总行数

# 以写入模式打开一个 CSV 文件,并确保文件以 UTF-8 编码保存,同时避免出现额外的空行。
with open(csv_path, 'w', newline='', encoding='utf-8-sig') as csvfile:
    # 创建一个 CSV 写入器对象。这个对象可以将数据以 CSV 格式写入到文件中。
    writer = csv.writer(csvfile)
    # 写入中文表头(新增提交者列)
    writer.writerow(["提交时间", "提交哈希", "提交者", "AI代码行", "总代码行", "覆盖率"])

    # 执行外部命令,并捕获命令的标准输出(stdout)
    commits = subprocess.check_output(
        ['git', 'log', '--since', start_date, '--until', end_date, '--pretty=format:%H|%cd|%an'],
        stderr=subprocess.STDOUT
    ).decode().split('\n')

    # git log:查看 Git 提交记录。
    # --since start_date:指定开始时间,获取从该时间之后的提交。
    # --until end_date:指定结束时间,获取到该时间之前的提交。
    # --pretty=format:%H|%cd|%an:自定义输出格式:
    # %H:提交的完整哈希值。
    # %cd:提交日期。
    # %an:作者名称。
    # 使用 | 作为字段分隔符。


    for commit_line in commits:
        # 跳过空行或仅包含空白字符的行
        if not commit_line.strip():
            continue

        # commit_line 格式如下:
        # f30bea9c145dffd7c041b31bb2d43b06312fd9f0|Thu Mar 13 09:33:13 2025 +0800|liuyongsheng

        # 解析新增的提交者字段
        commit_hash, raw_date, author = commit_line.split('|', 2)
        commit_time = datetime.strptime(raw_date.strip(), '%a %b %d %H:%M:%S %Y %z')
        formatted_time = commit_time.strftime('%Y-%m-%d %H:%M:%S')

        # 获取指定提交的详细信息,并将输出解析为 Python 列表。
        diff = subprocess.check_output(['git', 'show', commit_hash]).decode().split('\n')
        # 代码行格式如下
        # diff 是一个数组,包含了该时间区间内,提交记录详细信息

        ai_lines = 0
        code_lines = 0
        in_ai_block = False

        for line in diff:
            # line 格式如下:
            #     " class ConstValue {", 
            # "   /// 环境标识位(1:测试 2:预发 3:正式)", 
            # "-  static int environment = 2;", 
            # "+  static int environment = 3;", 
            # " ", 

            # "--- a/lib/common/values/values.dart", 
            # "+++ b/lib/common/values/values.dart", 
            if line.startswith('+') and not line.startswith('+++'):
                code_lines += 1
                content = line[1:]

                if ai_start_pattern.search(content):
                    ai_lines += 1
                    in_ai_block = True
                elif ai_end_pattern.search(content):
                    ai_lines += 1
                    in_ai_block = False
                elif ai_generated_pattern.search(content):
                    ai_lines += 1
                elif in_ai_block:
                    ai_lines += 1

        # 写入数据
        coverage = (ai_lines / code_lines * 100) if code_lines > 0 else 0
        writer.writerow([
            formatted_time,
            commit_hash[:7],
            author.strip(),  # 新增的提交者列
            ai_lines,
            code_lines,
            f"{coverage:.2f}%"
        ])
        
        # 累计统计
        total_ai += ai_lines
        total_code += code_lines

    # 添加统计行(提交者列留空)
    total_coverage = (total_ai / total_code * 100) if total_code > 0 else 0
    writer.writerow([
        f"统计时段: {start_date} 至 {end_date}",
        "汇总数据",
        "",  # 提交者列留空
        total_ai,
        total_code,
        f"{total_coverage:.2f}%"
    ])

# 自动打开Finder
csv_abs_path = os.path.abspath(csv_path)
if os.path.exists(csv_abs_path):
    subprocess.call(['open', '-R', csv_abs_path])

输出的文件如下:


三、校验提交内容AI标记不成对的问题

步骤 1:创建钩子文件

1、进入 Git 项目的 .git/hooks 目录:

cd your_project/.git/hooks

2、创建 pre-commit 文件(无后缀):

touch pre-commit
步骤 2:编辑钩子内容
#!/bin/bash

# 定义需要检查的标记
START_MARKER='// @AI_START'
END_MARKER='// @AI_END'
ERROR=0
SEPARATOR="-------------------------"

# 遍历暂存区中的每个文件
while read -r file; do
    # 跳过二进制文件
    if grep -q "^Binary file" <<< "$(git diff --cached -- "$file")"; then
        continue
    fi

    # 统计新增行中的标记数量(自动去除前导空格)
    start_count=$(git diff --cached -- "$file" | grep -E '^\+' | grep -F -o "$START_MARKER" | wc -l | tr -d ' ')
    end_count=$(git diff --cached -- "$file" | grep -E '^\+' | grep -F -o "$END_MARKER" | wc -l | tr -d ' ')

    # 检查标记是否成对
    if [ "$start_count" -ne "$end_count" ]; then
        echo "$SEPARATOR"
        echo -e "错误: 文件 '$file' 存在未闭合的标记!"
        echo -e "新增的【$START_MARKER】数量: $start_count"
        echo -e "新增的【$END_MARKER】数量: $end_count"
        ERROR=1
    fi
done < <(git diff --cached --name-only)

# 阻止提交若存在错误
if [ $ERROR -ne 0 ]; then
    echo "$SEPARATOR"
    echo -e "\n提交被阻止,请修复标记不匹配的问题。"
    exit 1
fi

exit 0
步骤 3:赋予执行权限
chmod +x pre-commit
步骤 4:测试提交效果


存在的问题:

1、单次提交不要嵌套使用 // @AI_START// @AI_START,以下是错误示例:
// @AI_START
class TestClass1 {
  void test() {
    print('test');
  }
  // @AI_START
  void test() {
    print('test');
  }
  // @AI_END
}
// @AI_END
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。
禁止转载,如需转载请通过简信或评论联系作者。

推荐阅读更多精彩内容