用AI来进行GitLab代码合并情况统计

Python3.8

Python3.8

Conda
Python

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

一、背景

公司之前有统计下发人员在Gitlab上的代码合并情况,但不知什么原因已经停止好几个月了;然而团队内的代码管理不能停,于是想着让AI帮忙搞个工具来统计,自己把控下统计的业务、范围和时机。

二、实现过程

  1. 向AI问询要实现这种统计的方法
  2. 得知可以使用gitlab提供的Rest API进行操作
  3. 实现的开发语言选择,Java虽然也可以但显然python干这种事情更方便、灵活。
  4. 整理统计的业务和步骤,让AI进行代码实现。
  5. 逐步提问、验证,最终形成最终的python脚本。
  6. 执行脚本并导出到Excel查看。

最终实现:指定group在某段时间范围内合并代码到develop分支的情况,并输出到Excel。

三、关键设计

  • 常量参数定义:有些参数是动态的,需要进行常量的定义,以方便每次执行的调整。包含顶级的group名称(数组)、查询的开始时间、结束时间、合并的目标分支、导出的文件名称以及gitlab服务地址和认证信息。
  • Rest API的认证:有两种方式,建议token方式
    • Access Token:登录个人的gitlab账号,点击个人图像-Edit profile进行个人设置界面,然后点击Access Tokens,添加一个新的token,注意Scopes要选择api,确认好过期时间并随即保存生成的token;并在请求api时直接在headers指定"PRIVATE-TOKEN: 生成的token"。

    • Cookie:若只是临时的使用,在登录gitlab后直接f12在cookie中获取一个叫“_gitlab_session”的值,并在请求api时在headers指定"cookie: _gitlab_session=f6f4959a5296e06535b3166d0ef91bfd"。

  • Gitlab的group处理:在gitlab中一个顶级group是可以进行多级嵌套的,需要进行子group的递归处理,避免group和其下项目统计的遗漏。

  • Rest API调用逻辑:

    • 1、获取顶级group的id:/api/v4/groups/demo_group

    • 2、查询其下子group:/api/v4/groups/:groupId/subgroups

    • 3、查询group的项目列表:/api/v4/groups/:groupId/projects

    • 4、查询项目的合并情况:/api/v4/projects/:project/merge_requests?state=merged&target_branch=develop&author_username=zhangsan&created_after=2025-03-01T00:00:00Z&created_before=2025-03-31T00:00:00Z&per_page=100

  • Excel输出的信息:要求包含合并请求ID、项目ID、合并请求标题、合并请求说明、合并时间、合并作者、代码Review人、合并请求地址等等信息

四、代码示例

import urllib3
import requests
import pandas as pd
from datetime import datetime

# 关闭 https 安全告警(如果你用 verify=False)
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

# === 配置项 ===
PRIVATE_TOKEN = "xxxx"  # 替换为你的 GitLab Access Token
GITLAB_URL = "https://xxx.gitlab.com/"  # 替换为你的 GitLab 实际地址
TOP_GROUPS = ["demo_group"]  # 替换为目标 group 的名称列表
START_DATE = "2025-03-01T00:00:00Z" # 指定合并请求的开始时间
END_DATE = "2025-03-31T23:59:59Z" # 指定合并请求的结束时间
TARGET_BRANCH = "develop" # 默认为开发分支
EXPORT_FILENAME = "3月份代码合并情况-demo_group.xlsx" # 默认导出的文件名称

HEADERS = {
    "PRIVATE-TOKEN": PRIVATE_TOKEN
}


# === 获取 group ID ===
def get_group_id(group_path):
    try:
        url = f"{GITLAB_URL}/api/v4/groups/{group_path}"
        resp = requests.get(url, headers=HEADERS, timeout=30, verify=False)
        resp.raise_for_status()
        return resp.json().get("id")
    except requests.exceptions.RequestException as e:
        print(f"Error getting group ID for {group_path}: {str(e)}")
        return None

# === 获取 group 下所有项目 ===
def get_projects_by_group_id(group_id):
    projects = []
    page = 1
    while True:
        url = f"{GITLAB_URL}/api/v4/groups/{group_id}/projects?per_page=100&page={page}"
        resp = requests.get(url, headers=HEADERS, verify=False)
        if not resp.ok or not resp.json():
            break
        projects.extend(resp.json())
        page += 1
    return projects

# === 获取子 group 列表 ===
def get_subgroups(group_id):
    subgroups = []
    page = 1
    while True:
        url = f"{GITLAB_URL}/api/v4/groups/{group_id}/subgroups?per_page=100&page={page}"
        resp = requests.get(url, headers=HEADERS, verify=False)
        if not resp.ok or not resp.json():
            break
        subgroups.extend(resp.json())
        page += 1
    return [{"id": g["id"], "full_path": g["full_path"]} for g in subgroups]

# === 递归获取所有项目 ===
def get_all_projects_recursively(group_id):
    all_projects = get_projects_by_group_id(group_id)
    subgroups = get_subgroups(group_id)
    for subgroup in subgroups:
        all_projects.extend(get_all_projects_recursively(subgroup["id"]))
        print(f"  ▶ 获取到子group: {subgroup['full_path']}")
    return all_projects

# === 从多个顶级 group 名称中获取所有项目 ===
def get_all_projects_from_group_names(group_names):
    all_projects = []
    for group_name in group_names:
        print(f"  ▶ 正在处理 group: {group_name}")
        group_id = get_group_id(group_name)
        if group_id:
            all_projects.extend(get_all_projects_recursively(group_id))
        else:
            print(f"⚠️ 无法获取 group ID:{group_name}")
    return all_projects

# === 获取指定项目的合并请求信息 ===
def get_merge_requests(project_id):
    merge_requests = []
    page = 1
    while True:
        url = (
            f"{GITLAB_URL}/api/v4/projects/{project_id}/merge_requests?state=merged"
            f"&target_branch={TARGET_BRANCH}&updated_after={START_DATE}&updated_before={END_DATE}"
            f"&per_page=100&page={page}"
        )
        resp = requests.get(url, headers=HEADERS, verify=False)
        if not resp.ok or not resp.json():
            break
        for mr in resp.json():
            merge_requests.append({
                "id": mr.get("id"),
                "project_id": mr.get("project_id"),
                "title": mr.get("title"),
                "description": mr.get("description"),
                "created_at": mr.get("created_at"),
                "updated_at": mr.get("updated_at"),
                "author_username": mr.get("author", {}).get("username"),
                "author_name": mr.get("author", {}).get("name"),
                "reviewer": (mr.get("reviewers")[0]["name"] if mr.get("reviewers") else None),
                "web_url": mr.get("web_url"),
            })
        page += 1
    return merge_requests

# === 执行统计流程 ===
def main():
    print("[1/3] 正在获取 group 下的所有项目...")
    projects = get_all_projects_from_group_names(TOP_GROUPS)

    print(f"[2/3] 共获取到 {len(projects)} 个项目,开始抓取合并请求...")
    all_merge_requests = []
    for idx, project in enumerate(projects):
        print(f"  -> ({idx+1}/{len(projects)}) 处理项目: {project['name']}")
        all_merge_requests.extend(get_merge_requests(project["id"]))

    print(f"[3/3] 共获取到符合条件的合并请求: {len(all_merge_requests)} 条,正在导出 Excel...")
    df = pd.DataFrame(all_merge_requests)
    df.to_excel(EXPORT_FILENAME, index=False)
    print(f"✅ 导出完成,文件路径:{EXPORT_FILENAME}")

if __name__ == "__main__":
    main()

五、问题

1、很多企业内部 GitLab 服务器使用了非公开 CA 签发的 SSL 证书,这就会导致 SSL 验证失败。目前在请求中禁用证书验证,引入了urllib3进行设置和verify=False的配置。

2、当前AI生成的python代码其实是存在一些问题的,比如:进行group和project列表收集时使用的是数组的提前定义,在信息量大的时间占用的内存会比较大,甚至会出现中断。有兴趣的小伙伴可以尝试着优化下。

3、通用AI的能力没有想象那么全能,在过程中也经历过了多次错误,人工检查核对不能丢,目前当工具来使用挺好,我们做好把控和审核挺好。

六、后续扩展

1、后续可以同时统计个人每次合并请求新增、删除的代码行数,以此统计人员的代码行数产量。

比如可以使用GET /projects/:id/merge_requests/:merge_request_iid/changes接口

2、/api/v4/groups/demo_group的接口不必查询,直接到group详情页面获取ID?

3、附上官方Rest API文档地址:Merge requests API | GitLab Docs

您可能感兴趣的与本文相关的镜像

Python3.8

Python3.8

Conda
Python

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值