Linux发行版维护的日常:从“人工盯仓库“到“一键自动同步上游代码更新“

📅 2026/8/11 16:02:21
Linux发行版维护的日常:从“人工盯仓库“到“一键自动同步上游代码更新“
Errata 自动化更新工具跨发行版包同步流水线作者杨显钊版本1.0日期2024年8月11日标签Errata、发行版同步、Anolis、RPM、Git、自动化适合人群Linux 发行版维护者、软件包管理工程师、DevOps摘要本文介绍了一套errata勘误自动化更新工具用于解决上游 Linux 发行版Anolis OS与下游发行版之间的软件包同步问题。该工具通过 Gitee API 自动检测上游仓库更新对比下游版本号识别下游是否有定制修改智能分类为自动同步“手动同步”无需操作三类最终生成 Excel 报告并通过邮件通知团队。本文详细拆解了工具的整体架构、核心流程、关键技术实现并附代码片段为发行版维护者提供一套可复用的自动化同步方案。一、背景与痛点在操作系统发行版维护中上游upstream更新同步到下游downstream是一个常见但繁琐的任务。某 Linux 发行版基于 Anolis OS龙蜥操作系统构建。Anolis OS 在 Gitee 上维护着数千个源码仓库src-anolis-os/*当上游修复了 CVE 漏洞、发布了新特性下游需要及时跟进。然而下游对某些包有自己的定制修改不能盲目覆盖需要人工判断——这就需要一个自动化的 errata 同步工具。本文介绍的工具正是为了解决这个痛点而生的自动检测 Anolis OS 的包更新对比下游版本智能分类处理方式生成 Excel 报告并邮件通知团队。二、技术栈类别技术用途语言Python 3核心调度、Excel 生成、邮件发送脚本BashGit 操作、版本比较、RPM 构建命令版本控制Git、Gitee API v5、Gerrit克隆仓库、检测更新、推送同步构建系统Koji (RPM)重建源码包至目标标签RPM 工具rpm2cpio、cpio提取源码包Excelxlwt、xlrd、xlutils生成带格式的统计报表邮件smtplib (SMTP_SSL)发送通知邮件HTTPcurl、jsonGitee API 接口调用三、整体架构entrance.py CLI入口errata_automation.py 核心引擎Gitee API 获取更新列表get_uos_src.sh 获取下游源码git.sh 版本比较与同步决策Gerrit 克隆Koji 回退下载Excel 报告 first.xls邮件通知入口entrance.py解析命令行参数-r release_version根据参数路由到不同处理函数。-r anolis进入 Anolis 同步流程-r monitor_euler/merge_euler为预留扩展接口。核心引擎errata_automation.py628 行是整个工具的调度中枢按顺序执行创建工作目录 → 拉取更新列表 → 遍历每个包 → 获取下游源码 → 比较版本 → 分类决策 → 生成 Excel → 发送邮件。四、核心流程详解4.1 获取上游更新列表start()函数通过 Gitee API v5 分页获取src-anolis-os组织下所有仓库共 39 页每页 100 个提取full_name和updated_at字段与上一次记录的oldlog做 diff得到本次更新的包列表。deftest():os.system(rm -rf log)pkg_updateopen(log,modea,encodingutf-8)withopen(anolis)asf:datajson.load(f)foriinrange(len(data)):print(data[i][full_name]:data[i][updated_at],filepkg_update)pkg_update.close()os.system(sed -i s/src-anolis-os\\///g log)curl-XGET--headerContent-Type: application/json;charsetUTF-8\https://gitee.com/api/v5/orgs/src-anolis-os/repos?access_tokenTOKENtypeallpage${x}per_page1004.2 分支映射Anolis OS 有多个分支a7、a8、a8.2、a8.4、stream-*每个分支对应下游的不同发行版代号。通过git lg --all查看仓库的提交分支信息解析出分支名并映射defget_update_branchname(pkgname):os.chdir(errata/anolis-gitee/pkgname)os.system(git pull --all --force)xos.popen(git lg --all | sed -n 1p)sx.readline().rstrip(\n)tmpre.compile(r[(](.*?)[)],re.S)xzre.findall(tmp,s)branchstr(xz)# 分支映射逻辑ifa7,inbranch:anolis_brancha7uos_branch1000cuos_tagc7elifa8.2,inbranchora8.4,inbranchora8,inbranch:anolis_brancha8.xuos_branch105xuos_tagc8elifstreaminbranch:anolis_branchuos_branchstream# 同名映射return[anolis_branch,uos_branch,uos_tag]4.3 获取下游源码get_uos_src.sh优先从Gerrit服务器克隆下游源码仓库。如果 Gerrit 上没有该包或分支不存在则回退到Koji查询最新构建找到对应的.src.rpm并用rpm2cpio cpio解压# 尝试从 Gerrit 克隆gitclonessh://usergerrit-server:29418/uos/${update_pkgname}# 失败则回退到 KojipkgNVR$(koji list-tagged $tag ${update_pkgname}--latest|sed-n3p|awk{print($1)})kojiPkgDir$(find/mnt/koji/packages/${update_pkgname}|grep${pkgNVR}*.src.rpm$)rpm2cpio$kojiPkgDir|cpio-di获取来源gerrit或koji会写入getUosSrc文件供下游的git.sh决策同步方式。4.4 跳过 Rebrand 提交Anolis OS 的某些提交只是改个名rebrand不涉及实质功能变更。git.sh中的anolis_skip_rebrand()会检查最近 5 个提交遇到 rebrand 提交就自动跳过functionanolis_skip_rebrand(){foriin{1..5};docd$anolis_pkgdircommit$(gitlog-1)result1$(echo$commit|greprebrand)result2$(echo$commit|grepRebrand)if[[$result1]][[$result2]];thenbreakelsegitreset--hardHEAD~1fidone}4.5 版本提取从.spec文件的%changelog中提取版本号。由于各包 Changelog 格式不统一脚本用了 11 种 awk 模式来覆盖不同格式——格式包括 - version、 version、- version等邮件域名覆盖.com、.org、.net、.cz、.cn、.name、.fi、.deanolis_tmp1$(cat*.spec|grep-A1%changelog|grep.com|awk-F - {print $2})anolis_tmp2$(cat*.spec|grep-A1%changelog|grep.org|awk-F - {print $2})# ...if[[${anolis_tmp1}!]];thenvanolis${anolis_tmp1}elif[[${anolis_tmp2}!]];thenvanolis${anolis_tmp2}# ... 依次回退提取出上游版本vanolis和下游版本vuos后使用sort -Vversion sort比较版本高低。4.6 分类决策git.sh将每个包分为三类核心决策树如下否是否是全部为 rebuild有功能修改版本比较vanolis vuos?无需操作下游有本地修改?自动同步修改类型?触发 Koji 重建标记为手动同步关键判断函数skipUosRebuild()解析下游.spec文件的%changelog统计包含特定标识的条目中有多少是rebuilddefskipUosRebuild(pkgname):withopen(uos_dir%s.spec%pkgname)asf:af.read()ba.split(%changelog)[1]# 解析 changelog按 * 分割条目uos0uos_rebuild0forxinl:if(rebuildinx.lower()):# 判断是否为本地下游修改uosuos1ifrebuildinx.lower():uos_rebuilduos_rebuild1ifuosuos_rebuild0:rebuildno# 无下游本地修改elifuosuos_rebuild!0:rebuildall# 全部是 rebuildelse:rebuildsome# 有功能性修改returnrebuild4.7 Excel 报告生成的first.xls包含 5 个 SheetSheet 名称内容用途Summary分类统计汇总快速了解本次更新概况Manual需人工干预的包 changelog开发人员逐包审核Auto已自动同步的包 changelog追溯确认Rebuild需要重建的包 changelog运维触发Errors版本提取失败的包修复异常Excel 的生成使用xlwt创建工作簿xlutils.copy追加 Sheet支持自定义样式字体、对齐、边框。4.8 邮件通知最后通过 SMTP_SSL 将 Excel 报告发送给团队成员mail_hostsmtp.exmail.qq.commail_useruserexample.commessageMIMEMultipart()xlsMIMEApplication(open(pwdd/first.xls,rb).read())xls.add_header(Content-Disposition,attachment,filenameerrata.xlsx)message.attach(xls)message[Subject]Header(errata自动化更新,utf-8)smtpObjsmtplib.SMTP_SSL(mail_host,465)smtpObj.login(mail_user,mail_pass)smtpObj.sendmail(sender,receivers,message.as_string())五、项目文件结构errata/ ├── entrance.py # CLI 入口参数分发 ├── errata_automation.py # 核心引擎628 行 ├── utils.py # 工具函数argparse、pandas ├── git.sh # 版本比较与同步逻辑355 行 ├── get_uos_src.sh # 获取下游源码Gerrit/Koji ├── download_anolis_src # 下载上游源码包 ├── README # 使用说明 └── errata.png # 流程图六、使用方式# 安装依赖pip3installxlwt xlrd xlutils pandas numpy# 执行 Anolis 同步python3 entrance.py-ranolis-bbranch脚本会自动完成创建工作目录结构从 Gitee API 获取所有仓库的最新更新时间对比上次记录找出更新的包对每个更新包克隆源码 → 获取下游源码 → 比较版本 → 分类处理生成 Excel 报告 → 发送邮件七、总结本文实现了一套完整的跨发行版包自动同步方案核心价值在于自动分类——通过解析 Changelog 智能识别下游定制修改避免盲目覆盖双重源获取——Gerrit 优先、Koji 回退适配不同基础设施可视化报告——Excel 多 Sheet 分类 邮件通知团队一目了然可扩展架构——预留了 EulerOS 的接口可快速适配其他上游这套方案的思路不仅适用于 Anolis OS 到下游的同步任何上游-下游架构的发行版维护场景都可以参考复用。声明本文为 AI 辅助创作。文中代码示例已做脱敏处理实际部署时请替换为真实的 token、服务器地址和邮箱配置。