拼多多店群自动化报活动上架影刀RPA Git仓库管理代码分支清理与统计自动化作者林焱什么情况用这个开发团队遇到这个问题的应该不少项目跑了半年Git仓库里堆了上百个feature分支有些早就合并了有些已经废弃了但没人去清理。每次git branch -a滚好几屏找个分支眼睛都看花。更烦的是项目经理每周要统计各成员的代码提交量以前都是人工去GitLab/GitHub后台翻费时费力还不一定准。还有代码审查Code Review的提醒——有人提了MR没人审一挂就是好几天。这些重复性Git管理操作用影刀RPA做自动化再合适不过。不涉及代码逻辑就是规律的查询——统计——通知操作。怎么做第一步定位Git仓库先在影刀中用【读取Excel】读取一个仓库清单包含每个项目的本地路径和远程地址importosimportsubprocess# 仓库配置示例REPOS[{name:后端服务,local_path:D:/Projects/backend-service,remote:origin,default_branch:main},{name:前端项目,local_path:D:/Projects/frontend-app,remote:origin,default_branch:master}]第二步扫描过期分支核心逻辑是找到那些已经合并到主分支但没删除的本地/远程分支importsubprocessimportdatetimedefscan_stale_branches(local_path,remote,default_branch,months_threshold2): 扫描过期分支 - 已合并到默认分支的 - 最后提交超过阈值的 os.chdir(local_path)# 先拉取最新远程信息subprocess.run([git,fetch,--prune],capture_outputTrue,textTrue)# 获取已合并到主分支的分支列表merged_resultsubprocess.run([git,branch,-r,--merged,f{remote}/{default_branch}],capture_outputTrue,textTrue)merged_branches[b.strip()forbinmerged_result.stdout.split(\n)ifb.strip()andf{remote}/inbanddefault_branchnotinb]# 获取每个分支的最后提交时间stale_list[]nowdatetime.datetime.now()forbranchinmerged_branches:branch_namebranch.replace(f{remote}/,)# 获取最后提交日期log_resultsubprocess.run([git,log,-1,--format%ai,branch],capture_outputTrue,textTrue)try:last_commit_datedatetime.datetime.strptime(log_result.stdout.strip()[:10],%Y-%m-%d)months_ago(now-last_commit_date).days/30ifmonths_agomonths_threshold:stale_list.append({分支名:branch_name,最后提交:log_result.stdout.strip()[:10],距今(月):round(months_ago,1),来源:branch})except:continuereturnstale_list第三步批量清理过期分支扫描出过期分支后影刀流程中生成确认列表经人工确认后自动删除defcleanup_branches(local_path,branches_to_delete):批量删除过期分支os.chdir(local_path)results[]forbranch_infoinbranches_to_delete:branch_namebranch_info[分支名]# 删除远程分支push_resultsubprocess.run([git,push,origin,--delete,branch_name],capture_outputTrue,textTrue)# 删除本地跟踪分支subprocess.run([git,branch,-rd,forigin/{branch_name}],capture_outputTrue,textTrue)results.append({分支名:branch_name,远程删除:成功ifdeletedinpush_result.stderr.lower()else失败,详情:push_result.stderr.strip()})returnresults影刀流程操作【Python节点】→ 执行scan_stale_branches()扫描【IF】→ 如果过期分支列表不为空【写入Excel】→ 将过期分支列表写入Excel供人工确认【发送邮件】→ 将Excel发给技术负责人审批【等待】→ 收到确认后【Python节点】→ 执行cleanup_branches()批量删除第四步代码提交统计统计每个成员的提交次数、代码行数、活跃度defcode_contribution_stats(local_path,since_days7):统计团队成员代码贡献os.chdir(local_path)since_date(datetime.datetime.now()-datetime.timedelta(dayssince_days)).strftime(%Y-%m-%d)# 按作者统计提交次数commit_count_resultsubprocess.run([git,shortlog,-sn,--since,since_date,--no-merges],capture_outputTrue,textTrue)# 按作者统计新增/删除行数line_stats_resultsubprocess.run([git,log,--since,since_date,--no-merges,--formatauthor: %an,--numstat],capture_outputTrue,textTrue)# 解析统计结果author_stats{}forlineincommit_count_result.stdout.strip().split(\n):ifnotline.strip():continuepartsline.strip().split(\t)iflen(parts)2:commitsint(parts[0].strip())authorparts[1].strip()author_stats[author]{commits:commits,insertions:0,deletions:0}# 解析行数变更current_authorNoneforlineinline_stats_result.stdout.strip().split(\n):ifline.startswith(author: ):current_authorline.replace(author: ,).strip()elif\tinlineandcurrent_authorandcurrent_authorinauthor_stats:partsline.split(\t)iflen(parts)3andparts[0]!-:try:author_stats[current_author][insertions]int(parts[0])author_stats[current_author][deletions]int(parts[1])except:pass# 排序输出sorted_statssorted(author_stats.items(),keylambdax:x[1][commits],reverseTrue)return[{作者:author,**stats}forauthor,statsinsorted_stats]第五步MR/PR超时提醒检测GitLab上超过24小时未处理的Merge Requestimportrequestsdefcheck_stale_mrs(gitlab_url,project_id,private_token,stale_hours24):检查超时未处理的MRheaders{PRIVATE-TOKEN:private_token}# 获取所有打开的MRurlf{gitlab_url}/api/v4/projects/{project_id}/merge_requestsparams{state:opened,per_page:100}resprequests.get(url,headersheaders,paramsparams)mrsresp.json()stale_mrs[]nowdatetime.datetime.now(datetime.timezone.utc)formrinmrs:created_atdatetime.datetime.fromisoformat(mr[created_at].replace(Z,00:00))hours_since(now-created_at).total_seconds()/3600ifhours_sincestale_hours:stale_mrs.append({MR标题:mr[title],作者:mr[author][name],创建时间:mr[created_at],超时(小时):round(hours_since,1),链接:mr[web_url],指派人:mr.get(assignee,{}).get(name,未分配)})returnstale_mrs第六步生成周报并推送影刀流程串联所有统计结果生成周报1. 【Python节点】→ scan_stale_branches() 2. 【Python节点】→ code_contribution_stats() 3. 【Python节点】→ check_stale_mrs() 4. 【Python节点】→ 组装Markdown报告 5. 【发送企业微信消息】→ Markdown卡片推送有什么坑坑一Git未配置环境变量现象Python节点执行git命令报git不是内部或外部命令。原因影刀的Python环境不一定能继承系统的PATH。解决在Python节点中使用Git的绝对路径或者在代码中先设置环境变量importos# 方法一使用完整路径GIT_PATHrC:\Program Files\Git\bin\git.exedefgit_command(local_path,*args):returnsubprocess.run([GIT_PATH]list(args),cwdlocal_path,capture_outputTrue,textTrue)# 方法二代码中添加PATHos.environ[PATH]rC:\Program Files\Git\bin;os.environ.get(PATH,)坑二仓库未fetch导致数据过期现象统计的数据是好几天前的远程分支状态不准。原因直接读本地仓库信息没有先拉取远程数据。解决所有统计操作之前务必执行git fetch --prune。这个--prune很重要它能清理本地那些远程已经不存在的分支引用# 任何时候操作前先同步subprocess.run([GIT_PATH,fetch,--prune,--all],cwdlocal_path,capture_outputTrue,[video(video-GL1fGnGz-1783575922216)(type-csdn)(url-https://live.csdn.net/v/embed/526817)(image-https://v-blog.csdnimg.cn/asset/1d3c3709da119dd8c13ab01e9b282520/cover/Cover0.jpg)(title-TEMU店群矩阵自动化运营核价报活动)]textTrue,timeout60# 大仓库fetch可能很慢设长超时)坑三误删仍在使用的分支现象分支显示已合并但实际还有未完成的feature在上面。原因有人往主分支合并了一部分代码但feature分支还有其他功能的提交未合并。解决删除前做双重校验——不仅要检查是否已合并还要检查分支与默认分支是否有差异defis_branch_really_merged(local_path,branch_name,default_branch):检查分支是否真正完全合并os.chdir(local_path)# 检查该分支是否有未合并到默认分支的提交resultsubprocess.run([GIT_PATH,log,f{default_branch}..origin/{branch_name},--oneline],capture_outputTrue,textTrue)unmerged_commits[lforlinresult.stdout.strip().split(\n)ifl]returnlen(unmerged_commits)0,unmerged_commits坑四多项目操作时的目录混乱现象处理完第一个仓库后处理第二个仓库时命令在错误的目录执行。原因os.chdir()改变了工作目录后续没切回来。解决每次操作仓库前显式设置cwd参数不依赖全局工作目录# 正确做法每次指定cwdsubprocess.run([GIT_PATH,status],cwdrepo_path,# 显式指定capture_outputTrue,textTrue)坑五GitLab API分页问题现象MR统计只拉到了20条明明有50多个打开的MR。原因GitLab API默认每页只返回20条你没处理分页。解决检查响应头中的分页信息循环拉取所有页defget_all_mrs(gitlab_url,project_id,private_token):获取所有MR处理分页headers{PRIVATE-TOKEN:private_token}all_mrs[]page1whileTrue:urlf{gitlab_url}/api/v4/projects/{project_id}/merge_requestsparams{state:opened,per_page:100,page:page}resprequests.get(url,headersheaders,paramsparams)mrsresp.json()ifnotmrs:breakall_mrs.extend(mrs)# 检查还有没有下一页ifresp.headers.get(X-Next-Page,):breakpage1returnall_mrs总结Git仓库管理自动化是一个不做也行做了很爽的典型场景。每次清理能删掉几十个废弃分支代码统计从手工半小时变成一键跑完。关键是这些操作都是标准化的不会有特殊情况RPA跑起来很稳定。唯一要小心的是删除操作——一定要加确认步骤别让机器人把正在用的分支给删了。