三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

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

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.py(628 行)是整个工具的调度中枢,按顺序执行:创建工作目录 → 拉取更新列表 → 遍历每个包 → 获取下游源码 → 比较版本 → 分类决策 → 生成 Excel → 发送邮件。


四、核心流程详解

4.1 获取上游更新列表

start()函数通过 Gitee API v5 分页获取src-anolis-os组织下所有仓库(共 39 页,每页 100 个),提取full_nameupdated_at字段,与上一次记录的oldlog做 diff,得到本次更新的包列表。

deftest():os.system('rm -rf log')pkg_update=open('log',mode='a',encoding='utf-8')withopen('anolis')asf:data=json.load(f)foriinrange(len(data)):print(data[i]["full_name"]+":"+data[i]["updated_at"],file=pkg_update)pkg_update.close()os.system("sed -i 's/src-anolis-os\\///g' log")
curl-XGET--header"Content-Type: application/json;charset=UTF-8"\"https://gitee.com/api/v5/orgs/src-anolis-os/repos?access_token=<TOKEN>&type=all&page=${x}&per_page=100"

4.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')x=os.popen('git lg --all | sed -n 1p')s=x.readline().rstrip("\n")tmp=re.compile(r'[(](.*?)[)]',re.S)xz=re.findall(tmp,s)branch=str(xz)# 分支映射逻辑if'a7,'inbranch:anolis_branch='a7'uos_branch='1000c'uos_tag='c7'elif'a8.2,'inbranchor'a8.4,'inbranchor'a8,'inbranch:anolis_branch='a8.x'uos_branch='105x'uos_tag='c8'elif'stream'inbranch:anolis_branch=uos_branch=stream# 同名映射return[anolis_branch,uos_branch,uos_tag]

4.3 获取下游源码

get_uos_src.sh优先从Gerrit服务器克隆下游源码仓库。如果 Gerrit 上没有该包或分支不存在,则回退到Koji查询最新构建,找到对应的.src.rpm并用rpm2cpio + cpio解压:

# 尝试从 Gerrit 克隆gitclone"ssh://<user>@<gerrit-server>:29418/uos/${update_pkgname}"# 失败则回退到 KojipkgNVR=$(koji list-tagged $tag ${update_pkgname}--latest|sed-n'3p'|awk'{print($1)}')kojiPkgDir=$(find/mnt/koji/packages/${update_pkgname}|grep"${pkgNVR}*.src.rpm$")rpm2cpio$kojiPkgDir|cpio-di

获取来源(gerritkoji)会写入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"|grep"rebrand")result2=$(echo"$commit"|grep"Rebrand")if[[$result1==""]]&&[[$result2==""]];thenbreakelsegitreset--hardHEAD~1fidone}

4.5 版本提取

.spec文件的%changelog中提取版本号。由于各包 Changelog 格式不统一,脚本用了 11 种 awk 模式来覆盖不同格式——格式包括> - <version>> <version>- <version>等,邮件域名覆盖.com.org.net.cz.cn.name.fi.de

anolis_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 -V(version sort)比较版本高低。

4.6 分类决策

git.sh将每个包分为三类,核心决策树如下:

全部为 rebuild

有功能修改

版本比较

vanolis > vuos?

无需操作

下游有本地修改?

自动同步

修改类型?

触发 Koji 重建

标记为手动同步

关键判断函数skipUosRebuild()解析下游.spec文件的%changelog,统计包含特定标识的条目中有多少是rebuild

defskipUosRebuild(pkgname):withopen(uos_dir+'%s.spec'%pkgname)asf:a=f.read()b=a.split('%changelog')[1]# 解析 changelog,按 * 分割条目uos=0uos_rebuild=0forxinl:if("rebuild"inx.lower()):# 判断是否为本地下游修改uos=uos+1if"rebuild"inx.lower():uos_rebuild=uos_rebuild+1ifuos==uos_rebuild==0:rebuild="no"# 无下游本地修改elifuos==uos_rebuild!=0:rebuild="all"# 全部是 rebuildelse:rebuild="some"# 有功能性修改returnrebuild

4.7 Excel 报告

生成的first.xls包含 5 个 Sheet:

Sheet 名称内容用途
Summary分类统计汇总快速了解本次更新概况
Manual需人工干预的包 + changelog开发人员逐包审核
Auto已自动同步的包 + changelog追溯确认
Rebuild需要重建的包 + changelog运维触发
Errors版本提取失败的包修复异常

Excel 的生成使用xlwt创建工作簿,xlutils.copy追加 Sheet,支持自定义样式(字体、对齐、边框)。

4.8 邮件通知

最后通过 SMTP_SSL 将 Excel 报告发送给团队成员:

mail_host="smtp.exmail.qq.com"mail_user="user@example.com"message=MIMEMultipart()xls=MIMEApplication(open(pwdd+'/first.xls','rb').read())xls.add_header('Content-Disposition','attachment',filename='errata.xlsx')message.attach(xls)message['Subject']=Header('errata自动化更新','utf-8')smtpObj=smtplib.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-b<branch>

脚本会自动完成:

  1. 创建工作目录结构
  2. 从 Gitee API 获取所有仓库的最新更新时间
  3. 对比上次记录,找出更新的包
  4. 对每个更新包:克隆源码 → 获取下游源码 → 比较版本 → 分类处理
  5. 生成 Excel 报告 → 发送邮件

七、总结

本文实现了一套完整的跨发行版包自动同步方案,核心价值在于:

  1. 自动分类——通过解析 Changelog 智能识别下游定制修改,避免盲目覆盖
  2. 双重源获取——Gerrit 优先、Koji 回退,适配不同基础设施
  3. 可视化报告——Excel 多 Sheet 分类 + 邮件通知,团队一目了然
  4. 可扩展架构——预留了 EulerOS 的接口,可快速适配其他上游

这套方案的思路不仅适用于 Anolis OS 到下游的同步,任何上游-下游架构的发行版维护场景都可以参考复用。


声明:本文为 AI 辅助创作。文中代码示例已做脱敏处理,实际部署时请替换为真实的 token、服务器地址和邮箱配置。

← 返回列表