diff --git "a/release-assistant/doc/design/update\345\217\221\345\270\203\350\207\252\345\212\250\345\214\226\345\267\245\345\205\267\350\256\276\350\256\241\346\226\207\346\241\243.md" "b/release-assistant/doc/design/update\345\217\221\345\270\203\350\207\252\345\212\250\345\214\226\345\267\245\345\205\267\350\256\276\350\256\241\346\226\207\346\241\243.md" index 267f8b6d03f9e0fdada431fc99397465a64b8d6d..3a7c122207adcafd97f2f0e00b7b80026b7999da 100644 --- "a/release-assistant/doc/design/update\345\217\221\345\270\203\350\207\252\345\212\250\345\214\226\345\267\245\345\205\267\350\256\276\350\256\241\346\226\207\346\241\243.md" +++ "b/release-assistant/doc/design/update\345\217\221\345\270\203\350\207\252\345\212\250\345\214\226\345\267\245\345\205\267\350\256\276\350\256\241\346\226\207\346\241\243.md" @@ -1,611 +1,611 @@ -# update发布自动化工具设计文档 - -## 1. 背景 -openEuler社区对于LTS版本中的软件提供全量的漏洞修复,通过定期发布软件包提供安全漏洞的修复补丁。漏洞修复SLO为,9.0~10.0分漏洞,7天内修复;7.0~8.9 分漏洞,14内修复;0.1~6.9分漏洞,30天内修复。为了保证SLO达成率目标90%,有必要将发布周期缩短为7天,因此发布流程自动化成为openEuler 漏洞处理能力提升的关键路径之一。 - -![avatar](./img/update发布流程简图.png) - -update发布自动化工具通过码云issue串联起整个update发布流程,将分散的子流程,包括确定发布范围、测试、发布安全公告整合成完整的发布流程,提升发布效率和实现发布过程的可视化。 - -## 2. 需求分析 -### 2.1 需求描述 -1. 提供一键式启动发布流程功能,版本经理在release-management仓库中创建版本发布issue,在评论中评论/start-update启动发布流程; -2. 提供更新发布清单功能,通过在版本发布issue中评论/add $type $issueID 或者/delete $type $issueID,增加或删除发布包; -3. 提供开发自验功能,通过在版本发布issue中评论/check-requires,启动依赖范围检查、自编译和安装验证; -4. 提供检查并更新update版本的问题单状态,在版本发布issue的描述中罗列当前发布版本的issue,评论/check-status可获取issue最新状态。 - -### 2.2 依赖组件 -|组件|组件描述| -|:--|:-------| -| cve-manager | 提供需发布cve清单和安全公告 | -| jenkins | 提供开发自验证环境、发布包接口 | -| CI-bot | 通过issue评论触发对应操作 | -| gitee | 提供issue管理接口 | -| openEuler.org | 发布安全公告口接口 | - -### 2.3 USE-CASE视图 -![avatar](./img/update用例图.png) - -涉及外部人员为版本经理、开发人员、测试人员和安全委员会成员。在整个发布流程中,版本经理启动发布流程,创建空白issue并且评论/start-update启动发布流程。开发人员负责流程的驱动,通过评论触发依赖验证和发布包。安全委员会成员侧重关注CVE相关功能。在工具自动生成发布清单之后,安全委员会成员进行cve范围审核和更新,开发人员进行其他发布包(bugfix、特性、升级)范围的审核和更新。测试人员负责测试,及对问题的回归验收。 - -### 2.4 issue评论说明 -#### 2.4.1 issue评论逻辑图 - -![avatar](./img/issue评论流程图.png) -版本经理创建空白issue并评论/start-update启动发布流程。开发人员及安全委员会人员审核完发布包范围分别评论/pkg-ok和/cve-ok,满足这两个评论触发依赖验证。由工具自动启动依赖验证、上传测试目录、安装和自编译检查后,开发自验证通过后转测,转测回归通过后由测试人员评论/test-ok,自动触发发布评审。测试发现问题则由测试人员通过评论/add test $issueID添加问题单到版本issue中。审核人员审核完评论/check-ok,自动启动发布包。安全委员会人员审核完归档的cvrf文件,评论/cvrf-ok,满足/check-ok和/cvrf-ok,启动发布安全公告。 - -#### 2.4.2 评论说明 -|编号|评论|标签|操作人员|说明| -|:--|:---|:--|:---|:-----| -|①|/start-update|update-check-pkg|版本经理|启动发布流程,工具获取cve和bugfix发布范围,回写到issue描述中,添加标签update-check-pkg| -|②|/add cve I3NQ1A|NA|安全委员会|添加cve,工具自动修改版本issue描述中的cve清单| -|②|/delete cve #I3NQ1A|NA|安全委员会|删除cve,工具自动修改版本issue描述中的cve清单| -|②|/add bugfix #I3AZ9M|NA|开发人员|添加bugfix,工具自动修改版本issue描述中的bugfix清单| -|②|/delete bugfix #I3AZ9M|NA|开发人员|删除bugfix,工具自动修改版本issue描述中的bugfix清单| -|③|/cve-ok|update-check-requires|安全委员会成员|审核完cve清单且没有问题,满足/cve-ok和/bugfix-ok触发检查依赖操作,添加标签update-check-requires| -|③|/bugfix-ok|update-check-requires|开发人员|审核完bugfix清单且没有问题,满足/cve-ok和/bugfix-ok触发检查依赖操作,添加标签update-check-requires| -|④|/check-status|NA|开发人员|检查issue清单中的最新issue状态,并更新到版本发布issue的描述中| -|⑤|/get-requires|NA|开发人员|触发检查依赖操作| -|⑥|/test-ok|update-release-check|测试人员|测试回归通过,触发发布评审,添加标签update-release-check| -|⑦|/check-ok|update-release|tc、release、qa、安全委员会|发布评审通过,触发发布包及发布列表归档,添加标签update-release| -|⑧|/cvrf-ok|NA|安全委员会|安全公告检查完成且没有问题,满足/check-ok和/cvrf-ok触发发布安全公告| -|⑨|/no-release #I3NQ1A|NA|版本经理|问题未解决,评审决策本次版本不发布,遗留到下一版本| - -#### 2.4.3 issue模板参考举例 -![avatar](./img/issue模板参考举例.png) - -### 2.5 模块设计 -![avatar](./img/update自动化工具模块设计.png) - -命令解析模块:功能模块,解析版本发布issue中的评论触发对应操作 -用户权限校验模块: 校验请求命令的用户是否有权限 -版本issue管理模块:功能模块,管理版本issue内容,根据传入参数创建(issue内容、issue类型、里程碑)issue -issue描述解析模块:功能模块,获取到版本发布issue中的相关内容 -开发自验证模块:功能模块,验证依赖、安装、自编译 -验证结果解析模块:功能模块,解析验证结果,触发下一步操作 -问题单状态检查模块:功能模块,检查issue是否已经完成 -发布模块:功能模块,发布包和安全公告 - -### 2.6 接口设计 -![avatar](./img/update发布.png) - -#### 2.6.1 命令行接口 -1. 启动发布流程 -- oe-art start #I3NQ1A - -2. 更新发布范围 -- oe-art modify -releaseid #I3NQ1A -addcve #I3AZ9M -giteeid ruebb -- oe-art modify -releaseid #I3NQ1A -delcve #I3AZ9M -giteeid ruebb -- oe-art modify -releaseid #I3NQ1A -addbug #I3AZ9M -giteeid ruebb -- oe-art modify -releaseid #I3NQ1A -delbug #I3AZ9M -giteeid ruebb -- oe-art modify -releaseid #I3NQ1A -addtest #I3AZ9M -giteeid ruebb -- oe-art modify -releaseid #I3NQ1A -deltest #I3AZ9M -giteeid ruebb - -- 参数说明: - - |参数|说明| - |:--|:-------| - |releaseid|版本发布issue的ID| - |addcve|添加的CVE issue的ID|、 - |delcve|删除的CVE issue的ID| - |addbug|添加的bugfix issue的ID| - |delbug|删除的bugfix issue的ID| - |addtest|添加的测试issue的ID| - |deltest|删除的测试issue的ID| - -3. 确认发布范围 -- oe-art check -releaseid #I3NQ1A -type cve ok -giteeid ruebb -- oe-art check -releaseid #I3NQ1A -type cve nok -giteeid ruebb -- oe-art check -releaseid #I3NQ1A -type bug ok -giteeid ruebb -- oe-art check -releaseid #I3NQ1A -type bug nok -giteeid ruebb - -- 参数说明: - - |参数|说明| - |:--|:-------| - |releaseid|版本发布issue的ID| - |type|cve/bug,后面的参数为ok/nok| - |giteeid|触发操作的人员的码云ID| - -4. 检查最新issue状态 -- oe-art check -releaseid #I3NQ1A -type status -- 参数说明: - - |参数|说明| - |:--|:-------| - |releaseid|版本发布issue的ID| - -5. 获取需增加依赖包的列表 -- oe-art check -releaseid #I3NQ1A -type requires -giteeid ruebb -- 参数说明: - - |参数|类型|说明| - |:--|:--|:--| - |releaseid|版本发布issue的ID| - |giteeid|触发操作的人员的码云ID| - -6. 确认测试结果 -- oe-art check -releaseid #I3NQ1A -type test ok -giteeid ruebb -- oe-art check -releaseid #I3NQ1A -type test nok -giteeid ruebb - -- 参数说明: - - |参数|说明| - |:--|:-------| - |releaseid|版本发布issue的ID| - |giteeid|触发操作的人员的码云ID| - -7. 确认发布 -- oe-art release -releaseid #I3NQ1A -type checkok -giteeid ruebb -- 参数说明: - - |参数|说明| - |:--|:-------| - |releaseid|版本发布issue的ID| - |giteeid|触发操作的人员的码云ID| - -8. 确认安全公告 -- oe-art release -releaseid #I3NQ1A -type cvrfok -giteeid ruebb -- 参数说明: - - |参数|说明| - |:--|:-------| - |releaseid|版本发布issue的ID| - |giteeid|触发操作的人员的码云ID| - -9. 问题遗留 -- oe-art modify -releaseid #I3NQ1A -norelease #I3AZ9M -giteeid ruebb -- oe-art modify -releaseid #I3NQ1A -release #I3AZ9M -giteeid ruebb - - |参数|说明| - |:--|:-------| - |releaseid|版本发布issue的ID| - |giteeid|触发操作的人员的码云ID| - - -#### 2.6.3 内部接口 -##### 2.6.3.1 用户权限校验模块 -1. users_check -- 接口描述: - - 判断请求操作的用户是否有相关权限 - -- 请求参数: - - |参数|类型|说明| - |:--|:--|:--| - |user|str|请求命令的用户名| - |comment|str|请求命令的评论| - -- 返回参数 - | 参数 | 类型 | 说明 | - | :---- | :--- | :------------- | - | True | bool | 权限校验成功 | - | False | bool | 权限校验失败 | - -##### 2.6.3.2 命令解析模块 -1. commands -- 接口描述: - - 解析请求命令的评论 - -- 请求参数: - - |参数|类型|说明| - |:--|:--|:--| - |comment|str|请求命令的评论| - -- 返回参数 - - |参数|类型|说明| - |:--|:--|:--| - |command|str|执行的命令| - -- 评论对应的命令 - |评论|命令|说明| - |:--|:--|:--| - |start-update|auto-release start|启动发布流程| - |add/delete cve/bugfix/requires/test $modifyIssueID|auto-release add/delete $releaseIssueID [-cve/bugfix/requires/test $modifyIssueID]|修改发布范围| - |get-requires|auto-release get-requires $releaseIssueID|启动开发自验证流程| - |check-status|auto-release check-issues-status $releaseIssueID|更新issue状态| - |test-ok|auto-release check-release $releaseIssueID|启动发布审核| - |check-ok|auto-release release-update-pkg $releaseIssueID $timestamp auto-release|启动包发布| - |check-ok|upload-update-list $releaseIssueID|归档发布列表| - |check-ok and cvrf-ok|auto-release release-update-sa $releaseIssueID $timestamp|启动安全公告发布| - -##### 2.6.3.3 版本issue管理模块 - -1. get_CVE_list - - - 接口描述: - 获取cve-manager提供的CVE相关的信息,将该信息添加到"issue描述"属性中,回写网页端的issue描述 - - - - - CVE 相关信息表示: - ``` - [ - { - "CVE_id": "xxx1", - "score": 7.5, - "repository" : "xxx" - "issue_id": "xxx1", - "status" : "closed" - "version" : "1.1.1" - "abi_changed" : "TRUE" - }, - { - "CVE_id": "xxx2", - "score": 8.2, - "repository" : "xxx" - "issue_id": "xxx2", - "status" : "closed" - "version" : "2.0" - "abi_changed" : "FALSE" - } - ] - ``` - - - 请求参数 - - None - - - 返回参数 - - | 参数 | 类型 | 说明 | - | :---- | :--- | :---------- | - | True | bool | 操作成功 | - | False | bool | 操作失败 | - - - -2. get_bugfix_list - - - 接口描述: - 获取bugfix issue的相关的信息,将该信息添加到"issue描述"属性中,回写网页端的issue描述 - - - 请求参数: - - None - - - bugfix 相关信息表示 - - ``` - [ - { - "issue_id": "xxx1", - "repository": "xxx", - "status" : "closed" - }, - { - "issue_id": "xxx2", - "repository": "xxx", - "status" : "xxx" - } - ] - ``` - - 返回参数 - - | 参数 | 类型 | 说明 | - | :---- | :--- | :---------- | - | True | bool | 操作成功 | - | False | bool | 操作失败 | - -3. update_issue_description - 根据issue的各项描述信息(如CVE、bugfix等),并回写网页issue的描述 - - - 请求参数 - - None - - - 返回参数 - - | 参数 | 类型 | 说明 | - | :---- | :--- | :------------- | - | True | bool | 回写成功 | - | False | bool | 回写失败 | - - - 网页端回写样式 - - https://gitee.com/angela7/release-management/issues/I3NTCN - -4. parse_issue_commands - - 接口描述 - - 解析update issue的命令 - - - 请求参数 - - | 参数 | 类型 | 说明 | - | :------------ | :--- | :-------- | - | issue_command | str | issue命令| - - - 返回参数 - - | 参数 | 类型 | 说明 | - | :------------ | :--- | :-------- | - | operation | str | 操作类型例如delete、add| - | type | str | 例如CVE、bugfix| - -5. update_CVE_list - - 接口描述 - - 根据操作类型对CVE进行增删,将CVE信息更新到"issue描述"属性中,回写网页端的issue描述 - - - 请求参数 - - | 参数 | 类型 | 说明 | - | :------------ | :--- | :-------- | - | operation | str | 操作类型,例如delete、add| - | CVE_id | str | cve的id| - - - 返回参数 - | 参数 | 类型 | 说明 | - | :------------ | :--- | :-------- | - | True | bool | 操作成功| - | False | bool | 操作失败| - -6. update_bugfix_list - - 接口描述 - - 根据操作类型对bugfix进行增删,将bugfix信息更新到"issue描述"属性中,回写网页端的issue描述 - - - 请求参数 - - | 参数 | 类型 | 说明 | - | :------------ | :--- | :-------- | - | operation | str | 操作类型,例如delete、add| - | issue_id | str | issue的id| - - - 返回参数 - | 参数 | 类型 | 说明 | - | :------------ | :--- | :-------- | - | True | bool | 操作成功| - | False | bool | 操作失败| - -##### 2.6.3.4 issue描述解析模块 -1. get_update_list - - - 接口描述 - - 获取版本发布issue中的发布列表 - - - 请求参数 - - None - - - 返回参数 (列表类型) - - | 参数 | 类型 | 说明 | - | :------- | :--- | :----------- | - | pkglist | list | CVE、bugfix、requires涉及所有的包 | - -2. get_repo - - - 接口描述 - - 获取测试的repo源 - - - 请求参数 - - None - - - 返回参数 (字典类型) - - | 参数 | 类型 | 说明 | - | :------- | :--- | :----------- | - | aarch64 | str | aarch64的repo源 | - | x86 | str | x86的repo源 | - - - 返回示例 - ``` - { - "aarch64": "http://xxxx", - "x86": "http://xxxx" - } - ``` -3. get_branch - - 接口描述: - - 获取发布版本 - - - 请求参数 - - None - - - 返回参数: - - | 参数 | 类型 | 说明 | - | :------- | :--- | :----------- | - | branch | str | 发布版本 | - - -##### 2.6.3.5 开发自验证模块 -1. get_requires - - 接口描述: - - 检查软件包依赖,根据缺少的依赖列表,添加requires信息到"issue描述"属性中,回写网页端的issue描述 - - - 请求参数 - - | 参数 | 类型 | 说明 | - | :------- | :--- | :----------- | - | branch | str | 发布版本 | - | pkglist | list | CVE、bugfix、requires涉及所有的包 | - - - 返回参数 - | 参数 | 类型 | 说明 | - | :------------ | :--- | :-------- | - | True | bool | 操作成功| - | False | bool | 操作失败| - -2. upload_pkg_to_server - - - 接口描述 - - 获取CVE、bugfix、requires中包含的软件包名,将这些软件包名上传到测试服务器,更新release issue中的repo源 - - - 请求参数 - - | 参数 | 类型 | 说明 | - | :------- | :--- | :----------- | - | branch | str | 发布版本 | - | pkglist | list | CVE、bugfix、requires涉及所有的包 | - - - 返回参数 - | 参数 | 类型 | 说明 | - | :------------ | :--- | :-------- | - | True | bool | 操作成功| - | False | bool | 操作失败| - -3. check_install_build - - 接口描述 - - 进行安装以及自编译的验证 - - - 请求参数 - - | 参数 | 类型 | 说明 | - | :------------ | :--- | :--------------------- | - | branch | str | 发布版本 | - | aarch64 | str | aarch64的repo源 | - - - 返回参数 (字典类型) - - | 参数 | 类型 | 说明 | - | :------------ | :--- | :--------------------- | - | True | bool | 验证安装自编译成功 | - | False | bool | 验证安装自编译失败 | - -##### 2.6.3.6 验证结果解析模块 -1. create_install_build_issue - - 接口描述 - - 根据check_install_build的结果,若安装自编译失败,则提issue(如果issue是已经存在的,且将issue改为待办状态),并将该issue添加到安装、自编译的“issue描述”属性中,回写网页端的issue描述 - - - 请求参数 - - | 参数 | 类型 | 说明 | - | :--------- | :--- | :------------------ | - | repository | str | 待提issue的仓库名称 | - - - 返回参数 (字典类型) - - 安装、自编译的issue清单 - - | 参数 | 类型 | 说明 | - | :------------ | :--- | :--------------------- | - | True | str | 创建issue并回写成功 | - | False | str | 创建issue并回写失败 | - - -##### 2.6.3.7 问题单状态检查模块 -1、check_issue_state - -- 接口描述 - - 更新install_build 的issue及转测的 issue状态 - -- 请求参数 - - None - -- 返回参数 - - - 安装自编译的issue状态清单以及转测的issue清单 - - | 参数 | 类型 | 说明 | - | :---- | :--- | :----------------- | - | install_build_issue | str | 安装、自编译的issue | - | overall_status | str | issue整体状态,只要有一个issue状态为待办的,则表示不通过| - - - install_build_issue 参数: - - | 参数 | 类型 | 说明 | - | :---- | :--- | :----------------- | - | issue_id | str | 安装、自编译的issue id | - | status | str | issue 状态 | - -- 返回示例 - ``` - { - "install_build_issue": [ - { - "issue_id1": "closed" - }, - { - "issue_id2": "status2" - } - ], - "overall_status": "closed" -} - ``` - -##### 2.6.3.8 发布模块 -1. release_check - - 接口描述: - - 在版本发布issue描述中@相关人员进行发布评审 - - - 请求参数: - - None - - - 返回参数 - | 参数 | 类型 | 说明 | - | :------------ | :--- | :-------- | - | True | bool | 操作成功| - | False | bool | 操作失败| - -2. upload_release_pkg - - - 接口描述: - - 归档发布列表至release-management仓库里 - - - 请求参数: - - | 参数 | 类型 | 说明 | - | :---- | :---- | :---- | - | releasetime | str | 发布时间,作为归档目录名 | - | branch | str | 发布版本 | - - - 返回参数 - | 参数 | 类型 | 说明 | - | :------------ | :--- | :-------- | - | True | bool | 操作成功| - | False | bool | 操作失败| - -3. release_pkg - - 接口描述: - - 将发布的包从测试服务器拷贝到正式发布服务器 - - - 请求参数: - - | 参数 | 类型 | 说明 | - | :---- | :---- | :---- | - | timestamp | str | repo的时间戳 | - - - 返回参数 - | 参数 | 类型 | 说明 | - | :------------ | :--- | :-------- | - | True | bool | 操作成功| - | False | bool | 操作失败| - -4. release_SA - - 接口描述: - - 发布安全公告、cvrf和updateinfo - - - 请求参数: - - | 参数 | 类型 | 说明 | - | :---- | :---- | :---- | - | timestamp | str | repo的时间戳 | - - - 返回参数 - | 参数 | 类型 | 说明 | - | :------------ | :--- | :-------- | - | True | bool | 操作成功| - | False | bool | 操作失败| +# update发布自动化工具设计文档 + +## 1. 背景 +openEuler社区对于LTS版本中的软件提供全量的漏洞修复,通过定期发布软件包提供安全漏洞的修复补丁。漏洞修复SLO为,9.0~10.0分漏洞,7天内修复;7.0~8.9 分漏洞,14内修复;0.1~6.9分漏洞,30天内修复。为了保证SLO达成率目标90%,有必要将发布周期缩短为7天,因此发布流程自动化成为openEuler 漏洞处理能力提升的关键路径之一。 + +![avatar](./img/update发布流程简图.png) + +update发布自动化工具通过码云issue串联起整个update发布流程,将分散的子流程,包括确定发布范围、测试、发布安全公告整合成完整的发布流程,提升发布效率和实现发布过程的可视化。 + +## 2. 需求分析 +### 2.1 需求描述 +1. 提供一键式启动发布流程功能,版本经理在release-management仓库中创建版本发布issue,在评论中评论/start-update启动发布流程; +2. 提供更新发布清单功能,通过在版本发布issue中评论/add $type $issueID 或者/delete $type $issueID,增加或删除发布包; +3. 提供开发自验功能,通过在版本发布issue中评论/check-requires,启动依赖范围检查、自编译和安装验证; +4. 提供检查并更新update版本的问题单状态,在版本发布issue的描述中罗列当前发布版本的issue,评论/check-status可获取issue最新状态。 + +### 2.2 依赖组件 +|组件|组件描述| +|:--|:-------| +| cve-manager | 提供需发布cve清单和安全公告 | +| jenkins | 提供开发自验证环境、发布包接口 | +| CI-bot | 通过issue评论触发对应操作 | +| gitee | 提供issue管理接口 | +| openEuler.org | 发布安全公告口接口 | + +### 2.3 USE-CASE视图 +![avatar](./img/update用例图.png) + +涉及外部人员为版本经理、开发人员、测试人员和安全委员会成员。在整个发布流程中,版本经理启动发布流程,创建空白issue并且评论/start-update启动发布流程。开发人员负责流程的驱动,通过评论触发依赖验证和发布包。安全委员会成员侧重关注CVE相关功能。在工具自动生成发布清单之后,安全委员会成员进行cve范围审核和更新,开发人员进行其他发布包(bugfix、特性、升级)范围的审核和更新。测试人员负责测试,及对问题的回归验收。 + +### 2.4 issue评论说明 +#### 2.4.1 issue评论逻辑图 + +![avatar](./img/issue评论流程图.png) +版本经理创建空白issue并评论/start-update启动发布流程。开发人员及安全委员会人员审核完发布包范围分别评论/pkg-ok和/cve-ok,满足这两个评论触发依赖验证。由工具自动启动依赖验证、上传测试目录、安装和自编译检查后,开发自验证通过后转测,转测回归通过后由测试人员评论/test-ok,自动触发发布评审。测试发现问题则由测试人员通过评论/add test $issueID添加问题单到版本issue中。审核人员审核完评论/check-ok,自动启动发布包。安全委员会人员审核完归档的cvrf文件,评论/cvrf-ok,满足/check-ok和/cvrf-ok,启动发布安全公告。 + +#### 2.4.2 评论说明 +|编号|评论|标签|操作人员|说明| +|:--|:---|:--|:---|:-----| +|①|/start-update|update-check-pkg|版本经理|启动发布流程,工具获取cve和bugfix发布范围,回写到issue描述中,添加标签check-pkg| +|②|/add cve I3NQ1A|NA|安全委员会|添加cve,工具自动修改版本issue描述中的cve清单| +|②|/delete cve #I3NQ1A|NA|安全委员会|删除cve,工具自动修改版本issue描述中的cve清单| +|②|/add bugfix #I3AZ9M|NA|开发人员|添加bugfix,工具自动修改版本issue描述中的bugfix清单| +|②|/delete bugfix #I3AZ9M|NA|开发人员|删除bugfix,工具自动修改版本issue描述中的bugfix清单| +|③|/cve-ok|update-check-requires|安全委员会成员|审核完cve清单且没有问题,满足/cve-ok和/bugfix-ok触发检查依赖操作,添加标签check-requires| +|③|/bugfix-ok|update-check-requires|开发人员|审核完bugfix清单且没有问题,满足/cve-ok和/bugfix-ok触发检查依赖操作,添加标签check-requires| +|④|/check-status|NA|开发人员|检查issue清单中的最新issue状态,并更新到版本发布issue的描述中| +|⑤|/get-requires|NA|开发人员|触发检查依赖操作| +|⑥|/test-ok|update-release-check|测试人员|测试回归通过,触发发布评审,添加标签release-check| +|⑦|/check-ok|update-release|tc、release、qa、安全委员会|发布评审通过,触发发布包及发布列表归档,添加标签update-release| +|⑧|/cvrf-ok|NA|安全委员会|安全公告检查完成且没有问题,满足/check-ok和/cvrf-ok触发发布安全公告| +|⑨|/no-release #I3NQ1A|NA|版本经理|问题未解决,评审决策本次版本不发布,遗留到下一版本| + +#### 2.4.3 issue模板参考举例 +![avatar](./img/issue模板参考举例.png) + +### 2.5 模块设计 +![avatar](./img/update自动化工具模块设计.png) + +命令解析模块:功能模块,解析版本发布issue中的评论触发对应操作 +用户权限校验模块: 校验请求命令的用户是否有权限 +版本issue管理模块:功能模块,管理版本issue内容,根据传入参数创建(issue内容、issue类型、里程碑)issue +issue描述解析模块:功能模块,获取到版本发布issue中的相关内容 +开发自验证模块:功能模块,验证依赖、安装、自编译 +验证结果解析模块:功能模块,解析验证结果,触发下一步操作 +问题单状态检查模块:功能模块,检查issue是否已经完成 +发布模块:功能模块,发布包和安全公告 + +### 2.6 接口设计 +![avatar](./img/update发布.png) + +#### 2.6.1 命令行接口 +1. 启动发布流程 +- oe-art start #I3NQ1A + +2. 更新发布范围 +- oe-art modify -releaseid #I3NQ1A -addcve #I3AZ9M -giteeid ruebb +- oe-art modify -releaseid #I3NQ1A -delcve #I3AZ9M -giteeid ruebb +- oe-art modify -releaseid #I3NQ1A -addbug #I3AZ9M -giteeid ruebb +- oe-art modify -releaseid #I3NQ1A -delbug #I3AZ9M -giteeid ruebb +- oe-art modify -releaseid #I3NQ1A -addtest #I3AZ9M -giteeid ruebb +- oe-art modify -releaseid #I3NQ1A -deltest #I3AZ9M -giteeid ruebb + +- 参数说明: + + |参数|说明| + |:--|:-------| + |releaseid|版本发布issue的ID| + |addcve|添加的CVE issue的ID|、 + |delcve|删除的CVE issue的ID| + |addbug|添加的bugfix issue的ID| + |delbug|删除的bugfix issue的ID| + |addtest|添加的测试issue的ID| + |deltest|删除的测试issue的ID| + +3. 确认发布范围 +- oe-art check -releaseid #I3NQ1A -type cve ok -giteeid ruebb +- oe-art check -releaseid #I3NQ1A -type cve nok -giteeid ruebb +- oe-art check -releaseid #I3NQ1A -type bug ok -giteeid ruebb +- oe-art check -releaseid #I3NQ1A -type bug nok -giteeid ruebb + +- 参数说明: + + |参数|说明| + |:--|:-------| + |releaseid|版本发布issue的ID| + |type|cve/bug,后面的参数为ok/nok| + |giteeid|触发操作的人员的码云ID| + +4. 检查最新issue状态 +- oe-art check -releaseid #I3NQ1A -type status +- 参数说明: + + |参数|说明| + |:--|:-------| + |releaseid|版本发布issue的ID| + +5. 获取需增加依赖包的列表 +- oe-art check -releaseid #I3NQ1A -type requires -giteeid ruebb +- 参数说明: + + |参数|类型|说明| + |:--|:--|:--| + |releaseid|版本发布issue的ID| + |giteeid|触发操作的人员的码云ID| + +6. 确认测试结果 +- oe-art check -releaseid #I3NQ1A -type test ok -giteeid ruebb +- oe-art check -releaseid #I3NQ1A -type test nok -giteeid ruebb + +- 参数说明: + + |参数|说明| + |:--|:-------| + |releaseid|版本发布issue的ID| + |giteeid|触发操作的人员的码云ID| + +7. 确认发布 +- oe-art release -releaseid #I3NQ1A -type checkok -giteeid ruebb +- 参数说明: + + |参数|说明| + |:--|:-------| + |releaseid|版本发布issue的ID| + |giteeid|触发操作的人员的码云ID| + +8. 确认安全公告 +- oe-art release -releaseid #I3NQ1A -type cvrfok -giteeid ruebb +- 参数说明: + + |参数|说明| + |:--|:-------| + |releaseid|版本发布issue的ID| + |giteeid|触发操作的人员的码云ID| + +9. 问题遗留 +- oe-art modify -releaseid #I3NQ1A -norelease #I3AZ9M -giteeid ruebb +- oe-art modify -releaseid #I3NQ1A -release #I3AZ9M -giteeid ruebb + + |参数|说明| + |:--|:-------| + |releaseid|版本发布issue的ID| + |giteeid|触发操作的人员的码云ID| + + +#### 2.6.3 内部接口 +##### 2.6.3.1 用户权限校验模块 +1. users_check +- 接口描述: + + 判断请求操作的用户是否有相关权限 + +- 请求参数: + + |参数|类型|说明| + |:--|:--|:--| + |user|str|请求命令的用户名| + |comment|str|请求命令的评论| + +- 返回参数 + | 参数 | 类型 | 说明 | + | :---- | :--- | :------------- | + | True | bool | 权限校验成功 | + | False | bool | 权限校验失败 | + +##### 2.6.3.2 命令解析模块 +1. commands +- 接口描述: + + 解析请求命令的评论 + +- 请求参数: + + |参数|类型|说明| + |:--|:--|:--| + |comment|str|请求命令的评论| + +- 返回参数 + + |参数|类型|说明| + |:--|:--|:--| + |command|str|执行的命令| + +- 评论对应的命令 + |评论|命令|说明| + |:--|:--|:--| + |start-update|auto-release start|启动发布流程| + |add/delete cve/bugfix/requires/test $modifyIssueID|auto-release add/delete $releaseIssueID [-cve/bugfix/requires/test $modifyIssueID]|修改发布范围| + |get-requires|auto-release get-requires $releaseIssueID|启动开发自验证流程| + |check-status|auto-release check-issues-status $releaseIssueID|更新issue状态| + |test-ok|auto-release check-release $releaseIssueID|启动发布审核| + |check-ok|auto-release release-update-pkg $releaseIssueID $timestamp auto-release|启动包发布| + |check-ok|upload-update-list $releaseIssueID|归档发布列表| + |check-ok and cvrf-ok|auto-release release-update-sa $releaseIssueID $timestamp|启动安全公告发布| + +##### 2.6.3.3 版本issue管理模块 + +1. get_CVE_list + + - 接口描述: + 获取cve-manager提供的CVE相关的信息,将该信息添加到"issue描述"属性中,回写网页端的issue描述 + + - + + CVE 相关信息表示: + ``` + [ + { + "CVE_id": "xxx1", + "score": 7.5, + "repository" : "xxx" + "issue_id": "xxx1", + "status" : "closed" + "version" : "1.1.1" + "abi_changed" : "TRUE" + }, + { + "CVE_id": "xxx2", + "score": 8.2, + "repository" : "xxx" + "issue_id": "xxx2", + "status" : "closed" + "version" : "2.0" + "abi_changed" : "FALSE" + } + ] + ``` + + - 请求参数 + + None + + - 返回参数 + + | 参数 | 类型 | 说明 | + | :---- | :--- | :---------- | + | True | bool | 操作成功 | + | False | bool | 操作失败 | + + + +2. get_bugfix_list + + - 接口描述: + 获取bugfix issue的相关的信息,将该信息添加到"issue描述"属性中,回写网页端的issue描述 + + - 请求参数: + + None + + - bugfix 相关信息表示 + + ``` + [ + { + "issue_id": "xxx1", + "repository": "xxx", + "status" : "closed" + }, + { + "issue_id": "xxx2", + "repository": "xxx", + "status" : "xxx" + } + ] + ``` + - 返回参数 + + | 参数 | 类型 | 说明 | + | :---- | :--- | :---------- | + | True | bool | 操作成功 | + | False | bool | 操作失败 | + +3. update_issue_description + 根据issue的各项描述信息(如CVE、bugfix等),并回写网页issue的描述 + + - 请求参数 + + None + + - 返回参数 + + | 参数 | 类型 | 说明 | + | :---- | :--- | :------------- | + | True | bool | 回写成功 | + | False | bool | 回写失败 | + + - 网页端回写样式 + + https://gitee.com/angela7/release-management/issues/I3NTCN + +4. parse_issue_commands + - 接口描述 + + 解析update issue的命令 + + - 请求参数 + + | 参数 | 类型 | 说明 | + | :------------ | :--- | :-------- | + | issue_command | str | issue命令| + + - 返回参数 + + | 参数 | 类型 | 说明 | + | :------------ | :--- | :-------- | + | operation | str | 操作类型例如delete、add| + | type | str | 例如CVE、bugfix| + +5. update_CVE_list + - 接口描述 + + 根据操作类型对CVE进行增删,将CVE信息更新到"issue描述"属性中,回写网页端的issue描述 + + - 请求参数 + + | 参数 | 类型 | 说明 | + | :------------ | :--- | :-------- | + | operation | str | 操作类型,例如delete、add| + | CVE_id | str | cve的id| + + - 返回参数 + | 参数 | 类型 | 说明 | + | :------------ | :--- | :-------- | + | True | bool | 操作成功| + | False | bool | 操作失败| + +6. update_bugfix_list + - 接口描述 + + 根据操作类型对bugfix进行增删,将bugfix信息更新到"issue描述"属性中,回写网页端的issue描述 + + - 请求参数 + + | 参数 | 类型 | 说明 | + | :------------ | :--- | :-------- | + | operation | str | 操作类型,例如delete、add| + | issue_id | str | issue的id| + + - 返回参数 + | 参数 | 类型 | 说明 | + | :------------ | :--- | :-------- | + | True | bool | 操作成功| + | False | bool | 操作失败| + +##### 2.6.3.4 issue描述解析模块 +1. get_update_list + + - 接口描述 + + 获取版本发布issue中的发布列表 + + - 请求参数 + + None + + - 返回参数 (列表类型) + + | 参数 | 类型 | 说明 | + | :------- | :--- | :----------- | + | pkglist | list | CVE、bugfix、requires涉及所有的包 | + +2. get_repo + + - 接口描述 + + 获取测试的repo源 + + - 请求参数 + + None + + - 返回参数 (字典类型) + + | 参数 | 类型 | 说明 | + | :------- | :--- | :----------- | + | aarch64 | str | aarch64的repo源 | + | x86 | str | x86的repo源 | + + - 返回示例 + ``` + { + "aarch64": "http://xxxx", + "x86": "http://xxxx" + } + ``` +3. get_branch + - 接口描述: + + 获取发布版本 + + - 请求参数 + + None + + - 返回参数: + + | 参数 | 类型 | 说明 | + | :------- | :--- | :----------- | + | branch | str | 发布版本 | + + +##### 2.6.3.5 开发自验证模块 +1. get_requires + - 接口描述: + + 检查软件包依赖,根据缺少的依赖列表,添加requires信息到"issue描述"属性中,回写网页端的issue描述 + + - 请求参数 + + | 参数 | 类型 | 说明 | + | :------- | :--- | :----------- | + | branch | str | 发布版本 | + | pkglist | list | CVE、bugfix、requires涉及所有的包 | + + - 返回参数 + | 参数 | 类型 | 说明 | + | :------------ | :--- | :-------- | + | True | bool | 操作成功| + | False | bool | 操作失败| + +2. upload_pkg_to_server + + - 接口描述 + + 获取CVE、bugfix、requires中包含的软件包名,将这些软件包名上传到测试服务器,更新release issue中的repo源 + + - 请求参数 + + | 参数 | 类型 | 说明 | + | :------- | :--- | :----------- | + | branch | str | 发布版本 | + | pkglist | list | CVE、bugfix、requires涉及所有的包 | + + - 返回参数 + | 参数 | 类型 | 说明 | + | :------------ | :--- | :-------- | + | True | bool | 操作成功| + | False | bool | 操作失败| + +3. check_install_build + - 接口描述 + + 进行安装以及自编译的验证 + + - 请求参数 + + | 参数 | 类型 | 说明 | + | :------------ | :--- | :--------------------- | + | branch | str | 发布版本 | + | aarch64 | str | aarch64的repo源 | + + - 返回参数 (字典类型) + + | 参数 | 类型 | 说明 | + | :------------ | :--- | :--------------------- | + | True | bool | 验证安装自编译成功 | + | False | bool | 验证安装自编译失败 | + +##### 2.6.3.6 验证结果解析模块 +1. create_install_build_issue + - 接口描述 + + 根据check_install_build的结果,若安装自编译失败,则提issue(如果issue是已经存在的,且将issue改为待办状态),并将该issue添加到安装、自编译的“issue描述”属性中,回写网页端的issue描述 + + - 请求参数 + + | 参数 | 类型 | 说明 | + | :--------- | :--- | :------------------ | + | repository | str | 待提issue的仓库名称 | + + - 返回参数 (字典类型) + + 安装、自编译的issue清单 + + | 参数 | 类型 | 说明 | + | :------------ | :--- | :--------------------- | + | True | str | 创建issue并回写成功 | + | False | str | 创建issue并回写失败 | + + +##### 2.6.3.7 问题单状态检查模块 +1、check_issue_state + +- 接口描述 + + 更新install_build 的issue及转测的 issue状态 + +- 请求参数 + + None + +- 返回参数 + + - 安装自编译的issue状态清单以及转测的issue清单 + + | 参数 | 类型 | 说明 | + | :---- | :--- | :----------------- | + | install_build_issue | str | 安装、自编译的issue | + | overall_status | str | issue整体状态,只要有一个issue状态为待办的,则表示不通过| + + - install_build_issue 参数: + + | 参数 | 类型 | 说明 | + | :---- | :--- | :----------------- | + | issue_id | str | 安装、自编译的issue id | + | status | str | issue 状态 | + +- 返回示例 + ``` + { + "install_build_issue": [ + { + "issue_id1": "closed" + }, + { + "issue_id2": "status2" + } + ], + "overall_status": "closed" +} + ``` + +##### 2.6.3.8 发布模块 +1. release_check + - 接口描述: + + 在版本发布issue描述中@相关人员进行发布评审 + + - 请求参数: + + None + + - 返回参数 + | 参数 | 类型 | 说明 | + | :------------ | :--- | :-------- | + | True | bool | 操作成功| + | False | bool | 操作失败| + +2. upload_release_pkg + + - 接口描述: + + 归档发布列表至release-management仓库里 + + - 请求参数: + + | 参数 | 类型 | 说明 | + | :---- | :---- | :---- | + | releasetime | str | 发布时间,作为归档目录名 | + | branch | str | 发布版本 | + + - 返回参数 + | 参数 | 类型 | 说明 | + | :------------ | :--- | :-------- | + | True | bool | 操作成功| + | False | bool | 操作失败| + +3. release_pkg + - 接口描述: + + 将发布的包从测试服务器拷贝到正式发布服务器 + + - 请求参数: + + | 参数 | 类型 | 说明 | + | :---- | :---- | :---- | + | timestamp | str | repo的时间戳 | + + - 返回参数 + | 参数 | 类型 | 说明 | + | :------------ | :--- | :-------- | + | True | bool | 操作成功| + | False | bool | 操作失败| + +4. release_SA + - 接口描述: + + 发布安全公告、cvrf和updateinfo + + - 请求参数: + + | 参数 | 类型 | 说明 | + | :---- | :---- | :---- | + | timestamp | str | repo的时间戳 | + + - 返回参数 + | 参数 | 类型 | 说明 | + | :------------ | :--- | :-------- | + | True | bool | 操作成功| + | False | bool | 操作失败| diff --git a/release-assistant/javcra/api/gitee_api.py b/release-assistant/javcra/api/gitee_api.py index fc8d1d776e986b08d2f1c1041c5770076e3fc31a..62fffad7176d904fa0428ff9a53d0226ff7815d7 100644 --- a/release-assistant/javcra/api/gitee_api.py +++ b/release-assistant/javcra/api/gitee_api.py @@ -90,6 +90,16 @@ class Issue: return resp def __get_gitee_api_url(self, url_name, **kwargs): + """ + get specific gitee api url + + Args: + url_name(string) : url name + kwargs(dict): params to get url + + Returns: + gitee api url + """ url_prefix = "https://gitee.com/api/v5/" @@ -107,11 +117,17 @@ class Issue: "create_comment_url": url_prefix + "repos/{owner}/{repo}/issues/{number}/comments".format( owner=kwargs.get("owner"), repo=self.repo, number=self.issue_num), + "get_issue_comments": url_prefix + "repos/{owner}/{repo}/issues/{number}/comments?access_token={token}" "&page=1&per_page=100&order=asc".format( owner=kwargs.get("owner"), repo=kwargs.get("repo"), number=kwargs.get("issue_id"), token=self.token), + "update_issue_status": url_prefix + "repos/{owner}/issues/{number}".format(owner=kwargs.get("owner"), - number=kwargs.get("issue_id")) + number=kwargs.get("issue_id")), + + "creat_issue_label_url": url_prefix + "repos/{owner}/{repo}/issues/{number}/labels?access_token={token}" + "".format(owner=kwargs.get("owner"), repo=kwargs.get("repo"), + number=kwargs.get("issue_id"), token=self.token) } return url_dict.get(url_name) @@ -463,3 +479,71 @@ class Issue: elif shell_cmd(basis_cmd, repo_file_condition, epol_repo_condition): pkglist_epol.append(pkg) return pkglist_standard, pkglist_epol + + def get_issue_comments(self, params): + """ + get comments according to params + + Args: + params: comment params + Returns: + comment_list: comment list in release issue + """ + issue_comments_url = self.__get_gitee_api_url( + "get_issue_comments", **params + ) + issue_comments = self.gitee_api_request("get", url=issue_comments_url) + if not issue_comments: + logger.error("failed to get the comment of the issue, the route is %s" % issue_comments_url) + return [] + try: + comment_list = [] + issue_comments_resp = json.loads(issue_comments.text) + for comment_dict in issue_comments_resp: + if comment_dict.get("body"): + comment_list.append(comment_dict.get("body").strip()) + return comment_list + + except json.JSONDecodeError as error: + logger.error("failed to parse json data %s" % error) + return [] + + def judge_specific_comment_exists(self, comment_list, issue_params): + """ + judge whether the specific comment exists + + Args: + comment_list: comment list in release issue + issue_params: params for issue + + Returns: + True or False + """ + issue_comments = self.get_issue_comments(issue_params) + if not issue_comments: + return False + + lower_comment_list = [comment.lower() for comment in issue_comments] + for comment in comment_list: + if comment not in lower_comment_list: + logger.info("%s not in issue comments." % comment) + return False + + return True + + def create_issue_label(self, label_list, params): + """ + create label for release issue + + Args: + label_list: labels to create + params: params for issue + + Returns: + True or False + """ + create_label_url = self.__get_gitee_api_url("creat_issue_label_url", **params) + json_data = json.dumps(label_list) + resp = self.gitee_api_request("post", url=create_label_url, data=json_data) + return True if resp else False + diff --git a/release-assistant/javcra/application/checkpart/checkentrance.py b/release-assistant/javcra/application/checkpart/checkentrance.py index 92313a9f6b0d2293fd0afdf2f0c95e206a5e0bf2..f8e4b72838690ac96acfc9cb7044bbcc8d8cf521 100644 --- a/release-assistant/javcra/application/checkpart/checkentrance.py +++ b/release-assistant/javcra/application/checkpart/checkentrance.py @@ -103,7 +103,7 @@ class CheckEntrance(IssueOperation): else: update_data["epol_update_url"] = repo_info.get("url") - url_for_test = "http://" + TEST_IP_PORT + "/api/tce/testsuite/run" + url_for_test = "http://" + TEST_IP_PORT + "/api/v1/openeuler/task/update" headers = {"Content-Type": "application/json; charset=utf8"} resp = requests.post(url_for_test, data=json.dumps(update_data), headers=headers, timeout=3) @@ -111,7 +111,7 @@ class CheckEntrance(IssueOperation): logger.error("failed to send repo info.") return False - if json.loads(resp.text).get("error_code") != 200: + if json.loads(resp.text).get("error_code") != '2000': logger.error("failed to send repo info. Response: %s." % json.loads(resp.text)) return False diff --git a/release-assistant/javcra/application/modifypart/modifyentrance.py b/release-assistant/javcra/application/modifypart/modifyentrance.py index 8842ab721cbcf4a7a23b3657f02cb8701a0d25ee..a5acb3aa947a69f303390cf946dcb1426ddd4eea 100644 --- a/release-assistant/javcra/application/modifypart/modifyentrance.py +++ b/release-assistant/javcra/application/modifypart/modifyentrance.py @@ -19,7 +19,7 @@ import re import requests from retrying import retry from javcra.api.gitee_api import Issue -from javcra.common.constant import REPO_BASE_URL, RELEASE_URL +from javcra.common.constant import REPO_BASE_URL, RELEASE_URL, MULTI_VERSION_BRANCHS from javcra.libs.log import logger from javcra.libs.read_excel import download_file @@ -810,6 +810,7 @@ class IssueOperation(Operation): elif failed_type == "install": command = "yum install" + remind_str = "说明:此issue为机器自动创建,若验证后为误报,请在评论区评论‘误报’两字,并说明具体原因" params["body"] = """Branch: {brh} Component: {pkg} Instructions to reappear the problem : {command} @@ -818,8 +819,9 @@ class IssueOperation(Operation): Partial failure log:

{log_data} + {remind_str} """.format(brh=branch, pkg=pkg_name, command=command, - _type=failed_type, log_data=log_data) + _type=failed_type, log_data=log_data, remind_str=remind_str) issue_id = self.create_issue(params) return issue_id @@ -900,7 +902,7 @@ class IssueOperation(Operation): if epol_list: repo_dict = dict() repo_dict["repo_type"] = "epol" - if "sp2" in branch or "SP2" in branch: + if any(mul_branch in branch for mul_branch in MULTI_VERSION_BRANCHS): repo_dict["url"] = base_url + "/EPOL/update_" + release_date + "/main/" else: repo_dict["url"] = base_url + "/EPOL/update_" + release_date + "/" diff --git a/release-assistant/javcra/application/serialize/serialize.py b/release-assistant/javcra/application/serialize/serialize.py index 51c1c737bff922b36e46c64b59e7d42dc0b45558..15e19cdb67df165e32523ec682c53e9c3aaa64d1 100644 --- a/release-assistant/javcra/application/serialize/serialize.py +++ b/release-assistant/javcra/application/serialize/serialize.py @@ -17,6 +17,8 @@ from marshmallow import Schema from marshmallow import fields from marshmallow import validate +from javcra.common.constant import CHECK_PART_LIST + class BaseSchema(Schema): """ @@ -42,7 +44,7 @@ class CheckSchema(BaseSchema): check command parameter verification """ type = fields.String( - required=True, validate=validate.OneOf(["status", "requires", "test"]) + required=True, validate=validate.OneOf(CHECK_PART_LIST) ) ak = fields.String(required=False, validate=validate.Length(min=1)) sk = fields.String(required=False, validate=validate.Length(min=1)) diff --git a/release-assistant/javcra/cli/commands/checkpart.py b/release-assistant/javcra/cli/commands/checkpart.py index 5ae22157944b99f81128880e03bf10a818fabd08..24745fa833a39575e2355667dfe73c9f76ad17e3 100644 --- a/release-assistant/javcra/cli/commands/checkpart.py +++ b/release-assistant/javcra/cli/commands/checkpart.py @@ -30,7 +30,12 @@ from javcra.common.constant import ( MAX_PARAL_NUM, REALSE_TOOLS_BUCKET_NAME, REALSE_TOOLS_SERVER, - X86_FRAME, CHECK_COMMENT_DICT, EPOL_DICT) + X86_FRAME, + CHECK_COMMENT_DICT, + EPOL_DICT, + COMMENT_DICT, + LABEL_DICT, + CHECK_PART_LIST) from javcra.api.obscloud import ObsCloud from javcra.libs.log import logger @@ -54,7 +59,7 @@ class CheckCommand(BaseCommand): action="store", nargs=None, required=True, - choices=["status", "requires", "test"], + choices=CHECK_PART_LIST, ) self.sub_parse.add_argument( "--ak", @@ -146,6 +151,51 @@ class CheckCommand(BaseCommand): check_issue = CheckEntrance(GITEE_REPO, params.token, params.releaseIssueID) return check_issue + def judge_cve_bugfix_comment(self, issue, params): + """ + Description: judge whether /bugfix-ok and /cve-ok in comment area + Args: + issue: issue object + params: Command line parameters + + Returns: + True or False + """ + comment_params = {"owner": "openEuler", "repo": GITEE_REPO, "issue_id": params.releaseIssueID} + comment_list = [COMMENT_DICT.get("cve"), COMMENT_DICT.get("bugfix")] + judge_res = issue.judge_specific_comment_exists(comment_list, comment_params) + if not judge_res: + print("[ERROR] cve list or bugfix list not OK, please check.") + return False + return True + + def create_specific_label(self, issue_object, params, label_part): + """ + Description: create specific issue label + Args: + issue_object: issue object + params: Command line parameters + label_part: create label like release label or requires label + """ + label_params = {"owner": "openEuler", "repo": GITEE_REPO, "issue_id": params.releaseIssueID} + create_res = issue_object.create_issue_label([LABEL_DICT.get("requires")], label_params) + if not create_res: + raise ValueError("failed to create %s label for release issue." % label_part) + + def cve_bugfix_operation(self, params): + """ + Description: the operation for cve and bugfix list check + Args: + params: Command line parameters + """ + issue = self.issue(params) + judege_res = self.judge_cve_bugfix_comment(issue, params) + if not judege_res: + return + + self.create_specific_label(issue, params, "requires") + print("[INFO] successfuly operate cve and bugfix in check part.") + def test_operation(self, params): """ Description: the operation for test @@ -159,6 +209,9 @@ class CheckCommand(BaseCommand): if not review_res: print("[ERROR] failed to operate test in check part.") return False + + issue = self.issue(params) + self.create_specific_label(issue, params, "release") print("[INFO] successfully operate test in check part.") return True @@ -332,6 +385,10 @@ class CheckCommand(BaseCommand): print("failed to write back to install_build issue %s in release issue" % issue_id) issue = self.issue(params) + judege_res = self.judge_cve_bugfix_comment(issue, params) + if not judege_res: + return + check_issue = self.check_issue(params) branch_name, update_pkgs, release_date = self.get_release_info(issue) @@ -412,6 +469,7 @@ class CheckCommand(BaseCommand): """ comment = CHECK_COMMENT_DICT.get(params.type) + if not comment: print("[ERROR] not allowed operation, please check.") return diff --git a/release-assistant/javcra/cli/commands/releasepart.py b/release-assistant/javcra/cli/commands/releasepart.py index e87c14658e6cdfbfcac9a5ea8ba21dfc71825a19..a14cd3e94fbba77b3c9d59fb9bf010afb8467f92 100644 --- a/release-assistant/javcra/cli/commands/releasepart.py +++ b/release-assistant/javcra/cli/commands/releasepart.py @@ -21,7 +21,7 @@ from javcra.application.serialize.serialize import ReleaseSchema from javcra.cli.base import BaseCommand from javcra.cli.commands import parameter_permission_validate from javcra.common import constant -from javcra.common.constant import MAX_PARAL_NUM, GITEE_REPO, EPOL_DICT +from javcra.common.constant import MAX_PARAL_NUM, GITEE_REPO, EPOL_DICT, COMMENT_DICT class ReleaseCommand(BaseCommand): @@ -72,6 +72,24 @@ class ReleaseCommand(BaseCommand): required=True, ) + def judge_test_comment(self, issue, params): + """ + Description: judge whether /test-ok in comment area + Args: + issue: issue object + params: Command line parameters + + Returns: + True or False + """ + comment_params = {"owner": "openEuler", "repo": GITEE_REPO, "issue_id": params.releaseIssueID} + comment_list = [COMMENT_DICT.get("test")] + judge_res = issue.judge_specific_comment_exists(comment_list, comment_params) + if not judge_res: + print("[ERROR] test not OK, please check.") + return False + return True + def checkok_operation(self, params): """ Description: operation for check ok @@ -119,6 +137,11 @@ class ReleaseCommand(BaseCommand): self.create_comment("{action} epol rpm jenkins res".format(action=action), epol_transfer_res, issue) issue = IssueOperation(GITEE_REPO, params.token, params.releaseIssueID) + + judege_res = self.judge_test_comment(issue, params) + if not judege_res: + return + branch_name, update_pkgs, release_date = self.get_release_info(issue) # get parallel jenkins job num according to length of pkg_list and max parallel num @@ -153,6 +176,10 @@ class ReleaseCommand(BaseCommand): Returns: """ + issue = IssueOperation(GITEE_REPO, params.token, params.releaseIssueID) + judege_res = self.judge_test_comment(issue, params) + if not judege_res: + return release_resp = IssueOperation.release_announcement(params.publishuser, params.publishkey) if not release_resp: diff --git a/release-assistant/javcra/cli/commands/startpart.py b/release-assistant/javcra/cli/commands/startpart.py index f0519d827a69245caf4d15e4aecdc5e18f2153c8..9854bb3427e5437871a19f3e467aac9a22b6fe6d 100644 --- a/release-assistant/javcra/cli/commands/startpart.py +++ b/release-assistant/javcra/cli/commands/startpart.py @@ -19,7 +19,7 @@ from javcra.application.modifypart.modifyentrance import IssueOperation from javcra.application.serialize.serialize import StartSchema from javcra.cli.base import BaseCommand from javcra.cli.commands import parameter_permission_validate -from javcra.common.constant import GITEE_REPO +from javcra.common.constant import GITEE_REPO, LABEL_DICT class StartCommand(BaseCommand): @@ -79,3 +79,9 @@ class StartCommand(BaseCommand): print("[INFO] start update successfully.") else: print("[ERROR] failed to start update.") + return + + label_params = {"owner": "openEuler", "repo": GITEE_REPO, "issue_id": params.releaseIssueID} + create_res = issue.create_issue_label([LABEL_DICT.get("start")], label_params) + if not create_res: + print("failed to create start label for release issue.") diff --git a/release-assistant/javcra/common/constant.py b/release-assistant/javcra/common/constant.py index ecef20f2b2abc23c48037fce7ceaf22d5f20d543..db0544e7feb74e67e3227e02078cf000fe7493b2 100644 --- a/release-assistant/javcra/common/constant.py +++ b/release-assistant/javcra/common/constant.py @@ -39,6 +39,7 @@ PERMISSION_INFO = { "/check-ok", "/cvrf-ok", "/start-update" + "/check-cve-bugfix" ], "developer": [ "/add-bugfix", @@ -46,6 +47,7 @@ PERMISSION_INFO = { "/bugfix-ok", "/check-status", "/get-requires", + "/check-cve-bugfix" ], "tester": ["/test-ok"], "tc": ["/check-ok"], @@ -71,7 +73,7 @@ AARCH_FRAME = "aarch64" X86_FRAME = "x86_64" # branch list for standard epol list -BRANCH_LIST = ["openEuler-20.03-LTS-SP1", "openEuler-20.03-LTS-SP2", "openEuler-20.03-LTS"] +BRANCH_LIST = ["openEuler-20.03-LTS-SP1", "openEuler-20.03-LTS-SP2", "openEuler-20.03-LTS-SP3", "openEuler-20.03-LTS"] # lts branch LTS_BRANCH = "openEuler-20.03-LTS" @@ -100,6 +102,7 @@ TRIGGER_TM_JOB = "function-item/release-manager/update_template_jobs/trigger" ACTUATOR_DICT = { "openEuler-20.03-LTS-SP1": "openeuler-20.03-lts-sp1", "openEuler-20.03-LTS-SP2": "openeuler-20.03-lts-sp2", + "openEuler-20.03-LTS-SP3": "openeuler-20.03-lts-sp3", "openEuler-20.03-LTS": "openeuler-20.03-lts" } @@ -136,6 +139,7 @@ CHECK_COMMENT_DICT = { "status": "/check-status", "requires": "/get-requires", "test": "/test-ok", + "cve_bugfix": "/check-cve-bugfix" } # release url @@ -144,5 +148,16 @@ RELEASE_URL = "https://www.openeuler.org/api-cve/cve-security-notice-server/sync EPOL_DICT = { "openEuler-20.03-LTS": "EPOL", "openEuler-20.03-LTS-SP1": "EPOL", - "openEuler-20.03-LTS-SP2": "EPOL-main" + "openEuler-20.03-LTS-SP2": "EPOL-main", + "openEuler-20.03-LTS-SP3": "EPOL-main" } + +# comment dict +COMMENT_DICT = {"cve": "/cve-ok", "bugfix": "/bugfix-ok", "test": "/test-ok"} + +# label dict +LABEL_DICT = {"start": "check-pkg", "requires": "check-requires", "release": "release-check"} + +MULTI_VERSION_BRANCHS = ["sp2", "sp3", "SP2", "SP3"] + +CHECK_PART_LIST = ["status", "requires", "test", "cve_bugfix"] diff --git a/release-assistant/javcra/libs/config/checkpart/check_requires/support_branch.yaml b/release-assistant/javcra/libs/config/checkpart/check_requires/support_branch.yaml index c53786ab179532f29bc56e3adf0ac4ca6ae1a34d..e18a14892289f54ebadabfb6f23006fd9c9bbd83 100644 --- a/release-assistant/javcra/libs/config/checkpart/check_requires/support_branch.yaml +++ b/release-assistant/javcra/libs/config/checkpart/check_requires/support_branch.yaml @@ -4,4 +4,6 @@ openEuler : openEuler-20.03-LTS-SP1: obs_alias: "openEuler:20.03:LTS:SP1" openEuler-20.03-LTS-SP2: - obs_alias: "openEuler:20.03:LTS:SP2" \ No newline at end of file + obs_alias: "openEuler:20.03:LTS:SP2" + openEuler-20.03-LTS-SP3: + obs_alias: "openEuler:20.03:LTS:SP3" \ No newline at end of file diff --git a/release-assistant/javcra/libs/config/checkpart/check_requires/yum.repo/openEuler-20.03-LTS-SP3.repo b/release-assistant/javcra/libs/config/checkpart/check_requires/yum.repo/openEuler-20.03-LTS-SP3.repo new file mode 100644 index 0000000000000000000000000000000000000000..41cc1499bc72f3d9fe45f72d4daba7cdeb3afd42 --- /dev/null +++ b/release-assistant/javcra/libs/config/checkpart/check_requires/yum.repo/openEuler-20.03-LTS-SP3.repo @@ -0,0 +1,86 @@ +#generic-repos is licensed under the Mulan PSL v2. +#You can use this software according to the terms and conditions of the Mulan PSL v2. +#You may obtain a copy of Mulan PSL v2 at: +# http://license.coscl.org.cn/MulanPSL2 +#THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR +#IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR +#PURPOSE. +#See the Mulan PSL v2 for more details. + +#binary repo + +[published-everything] +name=published-everything +baseurl=https://repo.openeuler.org/openEuler-20.03-LTS-SP3/everything/$basearch/ +enabled=1 +gpgcheck=0 +priority=2 +gpgkey=https://repo.openeuler.org/openEuler-20.03-LTS-SP3/everything/$basearch/RPM-GPG-KEY-openEuler + +[published-update] +name=published-update +baseurl=https://repo.openeuler.org/openEuler-20.03-LTS-SP3/update/$basearch/ +enabled=1 +gpgcheck=0 +priority=1 +gpgkey=https://repo.openeuler.org/openEuler-20.03-LTS-SP3/update/$basearch/RPM-GPG-KEY-openEuler + +[published-debuginfo] +name=published-debuginfo +baseurl=https://repo.openeuler.org/openEuler-20.03-LTS-SP3/debuginfo/$basearch/ +enabled=0 +gpgcheck=0 +gpgkey=https://repo.openeuler.org/openEuler-20.03-LTS-SP3/debuginfo/$basearch/RPM-GPG-KEY-openEuler + +[devel-obs] +name=devel-obs +baseurl=http://119.3.219.20:82/openEuler:/20.03:/LTS:/SP3/standard_$basearch/ +enabled=1 +gpgcheck=0 +priority=3 + +#source repo + +[published-everything-src] +name=published-everything-src +baseurl=https://repo.openeuler.org/openEuler-20.03-LTS-SP3/source +enabled=0 +gpgcheck=0 +gpgkey=https://repo.openeuler.org/openEuler-20.03-LTS-SP3/source/$basearch/RPM-GPG-KEY-openEuler + +[published-update-src] +name=published-update-src +baseurl=https://repo.openeuler.org/openEuler-20.03-LTS-SP3/update/source +enabled=0 +gpgcheck=0 +gpgkey=https://repo.openeuler.org/openEuler-20.03-LTS-SP3/update/source/RPM-GPG-KEY-openEuler + +#EPOL binary repo + +[published-EPOL] +name=published-EPOL +baseurl=https://repo.openeuler.org/openEuler-20.03-LTS-SP3/EPOL/$basearch/ +enabled=0 +gpgcheck=0 +gpgkey=https://repo.openeuler.org/openEuler-20.03-LTS-SP3/OS/$basearch/RPM-GPG-KEY-openEuler + +[published-Epol-src] +name=published-Epol-src +baseurl=https://repo.openeuler.org/openEuler-20.03-LTS-SP3/EPOL/main/source/ +enabled=0 +gpgcheck=0 +gpgkey=https://repo.openeuler.org/openEuler-20.03-LTS-SP3/EPOL/main/source/RPM-GPG-KEY-openEuler + +[published-Epol-update-src] +name=published-Epol-update-src +baseurl=https://repo.openeuler.org/openEuler-20.03-LTS-SP3/EPOL/update/main/source +enabled=0 +gpgcheck=0 +gpgkey=https://repo.openeuler.org/openEuler-20.03-LTS-SP3/EPOL/update/main/source/RPM-GPG-KEY-openEuler + +[devel-obs] +name=devel-obs +baseurl=http://119.3.219.20:82/openEuler:/20.03:/LTS:/SP3/standard_$basearch/ +enabled=1 +gpgcheck=0 +priority=3 diff --git a/release-assistant/test/test_check/mock_data/mock_cve_bugfix_comment.txt b/release-assistant/test/test_check/mock_data/mock_cve_bugfix_comment.txt new file mode 100644 index 0000000000000000000000000000000000000000..e5f46a3ed29473433ec4073193c6d8bcfb8adadc --- /dev/null +++ b/release-assistant/test/test_check/mock_data/mock_cve_bugfix_comment.txt @@ -0,0 +1,134 @@ +[ + { + "id": 5785714, + "body": "/bugfix-ok.\nThat means the developers can comment below every pull request or issue to trigger Bot Commands.\nPlease follow instructions at \u003Chttps://gitee.com/openeuler/community/blob/master/en/sig-infrastructure/command.md\u003E to find the details.", + "user": { + "id": 5329419, + "login": "openeuler-ci-bot", + "name": "openeuler-ci-bot", + "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/1776/5329419_openeuler-ci-bot_1578984659.png", + "url": "https://gitee.com/api/v5/users/openeuler-ci-bot", + "html_url": "https://gitee.com/openeuler-ci-bot", + "followers_url": "https://gitee.com/api/v5/users/openeuler-ci-bot/followers", + "following_url": "https://gitee.com/api/v5/users/openeuler-ci-bot/following_url{/other_user}", + "gists_url": "https://gitee.com/api/v5/users/openeuler-ci-bot/gists{/gist_id}", + "starred_url": "https://gitee.com/api/v5/users/openeuler-ci-bot/starred{/owner}{/repo}", + "subscriptions_url": "https://gitee.com/api/v5/users/openeuler-ci-bot/subscriptions", + "organizations_url": "https://gitee.com/api/v5/users/openeuler-ci-bot/orgs", + "repos_url": "https://gitee.com/api/v5/users/openeuler-ci-bot/repos", + "events_url": "https://gitee.com/api/v5/users/openeuler-ci-bot/events{/privacy}", + "received_events_url": "https://gitee.com/api/v5/users/openeuler-ci-bot/received_events", + "type": "User" + }, + "source": null, + "target": { + "issue": { + "id": 6720618, + "number": "I401NU", + "title": "test" + }, + "pull_request": null + }, + "created_at": "2021-07-10T15:41:18+08:00", + "updated_at": "2021-07-10T15:41:18+08:00" + }, + { + "id": 6004607, + "body": "@Mary Please review this issue", + "user": { + "id": 7558519, + "login": "jiangpengjuj", + "name": "jiangpengjuj", + "avatar_url": "https://gitee.com/assets/no_portrait.png", + "url": "https://gitee.com/api/v5/users/jiangpengjuj", + "html_url": "https://gitee.com/jiangpengjuj", + "followers_url": "https://gitee.com/api/v5/users/jiangpengjuj/followers", + "following_url": "https://gitee.com/api/v5/users/jiangpengjuj/following_url{/other_user}", + "gists_url": "https://gitee.com/api/v5/users/jiangpengjuj/gists{/gist_id}", + "starred_url": "https://gitee.com/api/v5/users/jiangpengjuj/starred{/owner}{/repo}", + "subscriptions_url": "https://gitee.com/api/v5/users/jiangpengjuj/subscriptions", + "organizations_url": "https://gitee.com/api/v5/users/jiangpengjuj/orgs", + "repos_url": "https://gitee.com/api/v5/users/jiangpengjuj/repos", + "events_url": "https://gitee.com/api/v5/users/jiangpengjuj/events{/privacy}", + "received_events_url": "https://gitee.com/api/v5/users/jiangpengjuj/received_events", + "type": "User" + }, + "source": null, + "target": { + "issue": { + "id": 6720618, + "number": "I401NU", + "title": "test" + }, + "pull_request": null + }, + "created_at": "2021-07-27T18:57:57+08:00", + "updated_at": "2021-07-27T18:57:57+08:00" + }, + { + "id": 6176316, + "body": "/cve-ok", + "user": { + "id": 2234080, + "login": "Mary", + "name": "Mary", + "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/744/2234080_Mary_1578969378.png", + "url": "https://gitee.com/api/v5/users/Mary", + "html_url": "https://gitee.com/Mary", + "followers_url": "https://gitee.com/api/v5/users/Mary/followers", + "following_url": "https://gitee.com/api/v5/users/Mary/following_url{/other_user}", + "gists_url": "https://gitee.com/api/v5/users/Mary/gists{/gist_id}", + "starred_url": "https://gitee.com/api/v5/users/Mary/starred{/owner}{/repo}", + "subscriptions_url": "https://gitee.com/api/v5/users/Mary/subscriptions", + "organizations_url": "https://gitee.com/api/v5/users/Mary/orgs", + "repos_url": "https://gitee.com/api/v5/users/Mary/repos", + "events_url": "https://gitee.com/api/v5/users/Mary/events{/privacy}", + "received_events_url": "https://gitee.com/api/v5/users/Mary/received_events", + "type": "User" + }, + "source": null, + "target": { + "issue": { + "id": 6720618, + "number": "I401NU", + "title": "test" + }, + "pull_request": null + }, + "created_at": "2021-08-10T10:37:52+08:00", + "updated_at": "2021-08-10T10:37:52+08:00" + }, + { + "id": 6176331, + "body": "/bugfix-ok", + "user": { + "id": 2234080, + "login": "Mary", + "name": "Mary", + "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/744/2234080_Mary_1578969378.png", + "url": "https://gitee.com/api/v5/users/Mary", + "html_url": "https://gitee.com/Mary", + "followers_url": "https://gitee.com/api/v5/users/Mary/followers", + "following_url": "https://gitee.com/api/v5/users/Mary/following_url{/other_user}", + "gists_url": "https://gitee.com/api/v5/users/Mary/gists{/gist_id}", + "starred_url": "https://gitee.com/api/v5/users/Mary/starred{/owner}{/repo}", + "subscriptions_url": "https://gitee.com/api/v5/users/Mary/subscriptions", + "organizations_url": "https://gitee.com/api/v5/users/Mary/orgs", + "repos_url": "https://gitee.com/api/v5/users/Mary/repos", + "events_url": "https://gitee.com/api/v5/users/Mary/events{/privacy}", + "received_events_url": "https://gitee.com/api/v5/users/Mary/received_events", + "type": "User" + }, + "source": null, + "target": { + "issue": { + "id": 6720618, + "number": "I401NU", + "title": "test" + }, + "pull_request": null + }, + "created_at": "2021-08-10T10:38:23+08:00", + "updated_at": "2021-08-10T10:38:23+08:00" + } +] \ No newline at end of file diff --git a/release-assistant/test/test_check/mock_data/mock_post_data.txt b/release-assistant/test/test_check/mock_data/mock_post_data.txt index 802e4e4cb40ef13d84c1013367479e4788c2c843..9696331a18982a29cb5feb31e1c40ed2facdf028 100644 --- a/release-assistant/test/test_check/mock_data/mock_post_data.txt +++ b/release-assistant/test/test_check/mock_data/mock_post_data.txt @@ -1,4 +1,4 @@ { - "error_code": 200, + "error_code": "2000", "error_mesg": "All test suites have been executed." } \ No newline at end of file diff --git a/release-assistant/test/test_check/test_check_cli.py b/release-assistant/test/test_check/test_check_cli.py index 0a2163f73e1e9add2305cdadd97357c958a8e4d3..677d3b0b11652c54541519954b56f5fbdd361999 100644 --- a/release-assistant/test/test_check/test_check_cli.py +++ b/release-assistant/test/test_check/test_check_cli.py @@ -226,13 +226,14 @@ during the operation status, a failure occurred, and the cause of the error was test people review success """ self.expect_str = """ -[INFO] successfully operate test in check part. +[INFO] successfully operate test in check part. """ self.command_params = ["--giteeid=Mary", "--token=example", "--type=test", "--jenkinsuser=mary", "--jenkinskey=marykey", "--ak=forexample", "--sk=forexample", "I40769"] resp = self.make_expect_data(200, 'checkpart.txt') mock_comment_r = self.make_need_content('mock_issue_comment.txt', MOCK_DATA_FILE) - self.mock_request(side_effect=[resp, resp, resp, mock_comment_r]) + mock_cve_bigfix_comment = self.make_need_content('mock_cve_bugfix_comment.txt', MOCK_DATA_FILE) + self.mock_request(side_effect=[resp, resp, resp, mock_comment_r, mock_cve_bigfix_comment]) self.assert_result() def test_create_issue_comment_failed(self): @@ -312,8 +313,9 @@ Parameter validation failed mock_create_install_issue = self.make_need_content('create_install_issue_success.txt', MOCK_DATA_FILE) mock_checkpart_add_build = self.make_need_content('checkpart_add_build_success.txt', MOCK_DATA_FILE) mock_checkpart_add_install = self.make_need_content('checkpart_add_install_success.txt', MOCK_DATA_FILE) + mock_cve_bigfix_comment = self.make_need_content('mock_cve_bugfix_comment.txt', MOCK_DATA_FILE) self.mock_request( - side_effect=[resp, resp, resp, resp, resp, resp, resp, resp, resp, resp, mock_repo_list_data, + side_effect=[resp, resp, mock_cve_bigfix_comment, resp, resp, resp, resp, resp, resp, resp, resp, mock_repo_list_data, mock_repo_list_data, mock_create_jenkins_comment, mock_create_jenkins_comment, resp, resp, resp, resp, mock_add_repo_r, mock_create_build_jenkins_comment, mock_create_install_jenkins_comment, mock_create_install_jenkins_comment, resp, resp, @@ -333,7 +335,8 @@ Parameter validation failed self.prepare_jenkins_data() self.prepare_obs_data(delete_status_code=400) resp = self.make_expect_data(200, 'checkpart.txt') - self.mock_request(side_effect=[resp, resp, resp, resp, resp, resp]) + mock_cve_bigfix_comment = self.make_need_content('mock_cve_bugfix_comment.txt', MOCK_DATA_FILE) + self.mock_request(side_effect=[resp, resp, mock_cve_bigfix_comment, resp, resp, resp, resp]) self.assert_result() def test_get_repo_in_table_failed(self): @@ -349,8 +352,9 @@ Parameter validation failed self.mock_subprocess_check_output(return_value=b"published-everything-src") mock_create_jenkins_comment = self.make_need_content('create_jenkins_comments_success.txt', MOCK_DATA_FILE) resp = self.make_expect_data(200, 'checkpart.txt') + mock_cve_bigfix_comment = self.make_need_content('mock_cve_bugfix_comment.txt', MOCK_DATA_FILE) self.mock_request( - side_effect=[resp, resp, resp, resp, resp, resp, resp, resp, resp, resp, mock_repo_list_data, + side_effect=[resp, resp, mock_cve_bigfix_comment, resp, resp, resp, resp, resp, resp, resp, resp, mock_repo_list_data, mock_create_jenkins_comment, resp, resp, resp, resp, RequestException]) self.assert_result() @@ -372,8 +376,9 @@ Parameter validation failed mock_create_install_issue = self.make_need_content('create_install_issue_success.txt', MOCK_DATA_FILE) mock_checkpart_add_build = self.make_need_content('checkpart_add_build_success.txt', MOCK_DATA_FILE) mock_checkpart_add_install = self.make_need_content('checkpart_add_install_success.txt', MOCK_DATA_FILE) + mock_cve_bigfix_comment = self.make_need_content('mock_cve_bugfix_comment.txt', MOCK_DATA_FILE) self.mock_request( - side_effect=[resp, resp, resp, resp, resp, resp, resp, resp, resp, resp, mock_repo_list_data, + side_effect=[resp, resp, mock_cve_bigfix_comment, resp, resp, resp, resp, resp, resp, resp, resp, mock_repo_list_data, RequestException, resp, resp, resp, resp, mock_add_repo_r, RequestException, RequestException, resp, resp, mock_exist_issues, mock_create_build_issue, resp, resp, mock_create_build_issue, mock_checkpart_add_build, resp, resp, mock_exist_issues, mock_create_install_issue, resp, resp, @@ -394,8 +399,9 @@ Parameter validation failed mock_repo_list_data = self.make_need_content('repo_list_data.txt', MOCK_DATA_FILE) self.mock_subprocess_check_output(return_value=b'published-Epol-src') mock_create_jenkins_comment = self.make_need_content('create_jenkins_comments_success.txt', MOCK_DATA_FILE) + mock_cve_bigfix_comment = self.make_need_content('mock_cve_bugfix_comment.txt', MOCK_DATA_FILE) self.mock_request( - side_effect=[resp, resp, resp, resp, resp, resp, resp, resp, resp, resp, mock_repo_list_data, + side_effect=[resp, resp, mock_cve_bigfix_comment, resp, resp, resp, resp, resp, resp, resp, resp, mock_repo_list_data, mock_create_jenkins_comment, resp, resp, resp, resp, RequestException]) self.assert_result() @@ -410,7 +416,8 @@ during the operation requires, a failure occurred, and the cause of the error wa "--jenkinskey=marykey", "--ak=forexample", "--sk=forexample", "I40769"] resp = self.make_expect_data(200, 'checkpart.txt') branch_abnormal_r = self.make_need_content('check_branch_abnormal.txt', MOCK_DATA_FILE) - self.mock_request(side_effect=[resp, resp, branch_abnormal_r, branch_abnormal_r, RequestException]) + mock_cve_bigfix_comment = self.make_need_content('mock_cve_bugfix_comment.txt', MOCK_DATA_FILE) + self.mock_request(side_effect=[resp, resp, mock_cve_bigfix_comment, branch_abnormal_r, branch_abnormal_r, RequestException]) self.assert_result() def test_branch_name_is_none_and_get_update_list_failed(self): @@ -424,8 +431,9 @@ during the operation requires, a failure occurred, and the cause of the error wa "--jenkinskey=marykey", "--ak=forexample", "--sk=forexample", "I40769"] resp = self.make_expect_data(200, 'checkpart.txt') branch_abnormal_r = self.make_need_content('branch_name_is_none.txt', MOCK_DATA_FILE) + mock_cve_bigfix_comment = self.make_need_content('mock_cve_bugfix_comment.txt', MOCK_DATA_FILE) self.mock_request( - side_effect=[resp, resp, branch_abnormal_r, branch_abnormal_r, RequestException]) + side_effect=[resp, resp, mock_cve_bigfix_comment, branch_abnormal_r, branch_abnormal_r, RequestException]) self.assert_result() def test_create_jenkins_comment_failed(self): @@ -443,7 +451,11 @@ during the operation requires, a failure occurred, and the cause of the error wa self.prepare_jenkins_data() self.prepare_obs_data() self.mock_jenkins_build_job(return_value=0) - self.mock_request(return_value=resp) + mock_repo_list_data = self.make_need_content('repo_list_data.txt', MOCK_DATA_FILE) + mock_cve_bigfix_comment = self.make_need_content('mock_cve_bugfix_comment.txt', MOCK_DATA_FILE) + self.mock_request( + side_effect=[resp, resp, mock_cve_bigfix_comment, resp, resp, resp, resp, resp, resp, resp, resp, + mock_repo_list_data]) self.assert_result() def test_download_pkg_log_write_back_create_install_build_issue_failed(self): @@ -467,8 +479,9 @@ during the operation requires, a failure occurred, and the cause of the error wa MOCK_DATA_FILE) mock_issue_comment = self.make_need_content('mock_issue_comment.txt', MOCK_DATA_FILE) mock_create_build_issue = self.make_need_content('create_build_issue_success.txt', MOCK_DATA_FILE) + mock_cve_bigfix_comment = self.make_need_content('mock_cve_bugfix_comment.txt', MOCK_DATA_FILE) self.mock_request( - side_effect=[resp, resp, resp, resp, resp, resp, resp, resp, resp, resp, mock_repo_list_data, + side_effect=[resp, resp, mock_cve_bigfix_comment, resp, resp, resp, resp, resp, resp, resp, resp, mock_repo_list_data, mock_create_jenkins_comment, resp, resp, resp, resp, mock_add_repo_r, mock_create_build_jenkins_comment, mock_create_install_jenkins_comment, resp, resp, mock_exist_issues, mock_issue_comment, mock_create_build_issue, RequestException, @@ -494,8 +507,9 @@ during the operation requires, a failure occurred, and the cause of the error wa MOCK_DATA_FILE) mock_create_build_jenkins_comment = self.make_need_content('create_build_jenkins_comments_success.txt', MOCK_DATA_FILE) + mock_cve_bigfix_comment = self.make_need_content('mock_cve_bugfix_comment.txt', MOCK_DATA_FILE) self.mock_request( - side_effect=[resp, resp, resp, resp, resp, resp, resp, resp, resp, resp, mock_repo_list_data, + side_effect=[resp, resp, mock_cve_bigfix_comment, resp, resp, resp, resp, resp, resp, resp, resp, mock_repo_list_data, mock_create_jenkins_comment, resp, resp, resp, resp, mock_add_repo_r, mock_create_build_jenkins_comment, mock_create_install_jenkins_comment, resp, resp, mock_exist_issues, RequestException, RequestException]) @@ -522,8 +536,9 @@ during the operation requires, a failure occurred, and the cause of the error wa MOCK_DATA_FILE) mock_create_build_issue = self.make_need_content('create_build_issue_success.txt', MOCK_DATA_FILE) mock_create_install_issue = self.make_need_content('create_install_issue_success.txt', MOCK_DATA_FILE) + mock_cve_bigfix_comment = self.make_need_content('mock_cve_bugfix_comment.txt', MOCK_DATA_FILE) self.mock_request( - side_effect=[resp, resp, resp, resp, resp, resp, resp, resp, resp, resp, mock_repo_list_data, + side_effect=[resp, resp, mock_cve_bigfix_comment, resp, resp, resp, resp, resp, resp, resp, resp, mock_repo_list_data, mock_create_jenkins_comment, resp, resp, resp, resp, mock_add_repo_r, mock_create_build_jenkins_comment, mock_create_install_jenkins_comment, resp, resp, mock_exist_issues, mock_create_build_issue, resp, resp, mock_create_build_issue, diff --git a/release-assistant/test/test_release/mock_data/mock_test_comments.txt b/release-assistant/test/test_release/mock_data/mock_test_comments.txt new file mode 100644 index 0000000000000000000000000000000000000000..e6e7636f0fc1c6509e5252166a43e51371d2fa65 --- /dev/null +++ b/release-assistant/test/test_release/mock_data/mock_test_comments.txt @@ -0,0 +1,34 @@ +[{ + "id": 6274335, + "body": "/test-ok", + "user": { + "id": 2234080, + "login": "MementoMoriCheng", + "name": "魔箭胖胖", + "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/744/2234080_MementoMoriCheng_1578969378.png", + "url": "https://gitee.com/api/v5/users/MementoMoriCheng", + "html_url": "https://gitee.com/MementoMoriCheng", + "followers_url": "https://gitee.com/api/v5/users/MementoMoriCheng/followers", + "following_url": "https://gitee.com/api/v5/users/MementoMoriCheng/following_url{/other_user}", + "gists_url": "https://gitee.com/api/v5/users/MementoMoriCheng/gists{/gist_id}", + "starred_url": "https://gitee.com/api/v5/users/MementoMoriCheng/starred{/owner}{/repo}", + "subscriptions_url": "https://gitee.com/api/v5/users/MementoMoriCheng/subscriptions", + "organizations_url": "https://gitee.com/api/v5/users/MementoMoriCheng/orgs", + "repos_url": "https://gitee.com/api/v5/users/MementoMoriCheng/repos", + "events_url": "https://gitee.com/api/v5/users/MementoMoriCheng/events{/privacy}", + "received_events_url": "https://gitee.com/api/v5/users/MementoMoriCheng/received_events", + "type": "User" + }, + "source": null, + "target": { + "issue": { + "id": 6674816, + "number": "I3Z2BK", + "title": "你好" + }, + "pull_request": null + }, + "created_at": "2021-08-17T15:40:25+08:00", + "updated_at": "2021-08-17T15:40:25+08:00" + } +] \ No newline at end of file diff --git a/release-assistant/test/test_release/test_release_cli.py b/release-assistant/test/test_release/test_release_cli.py index 1cba1e3ee1710729f7b04d6a62e45f9e9f98ee45..508fef4ee6f4d31c4ab5d0faf026a1f47f739f03 100644 --- a/release-assistant/test/test_release/test_release_cli.py +++ b/release-assistant/test/test_release/test_release_cli.py @@ -50,8 +50,9 @@ remain issues exists, need to delete rpms for repo. MOCK_DATA_FILE) mock_publish_standard_comment = self.make_need_content('publish_standard_comments_success.txt', MOCK_DATA_FILE) mock_publish_epol_comment = self.make_need_content('publish_epol_comments_success.txt', MOCK_DATA_FILE) + test_comment_data = self.make_need_content('mock_test_comments.txt', MOCK_DATA_FILE) self.mock_request( - side_effect=[resp, resp, resp, resp, resp, resp, mock_remain_issue_data, + side_effect=[resp, resp, test_comment_data, resp, resp, resp, resp, mock_remain_issue_data, mock_delete_remain_standard_comment, mock_delete_remain_epol_comment, mock_publish_standard_comment, mock_publish_epol_comment]) self.assert_result() @@ -70,7 +71,8 @@ during the operation checkok, a failure occurred, and the cause of the error was self.mock_jenkins_build_job(return_value=0) self.mock_subprocess_check_output(return_value=b'published-Epol-src') mock_remain_issue_data = self.make_need_content('mock_remain_issue.txt', MOCK_DATA_FILE) - self.mock_request(side_effect=[resp, resp, resp, resp, resp, resp, mock_remain_issue_data]) + test_comment_data = self.make_need_content('mock_test_comments.txt', MOCK_DATA_FILE) + self.mock_request(side_effect=[resp, resp, test_comment_data, resp, resp, resp, resp, mock_remain_issue_data]) self.assert_result() def test_checkok_date_info_not_in_issue_body(self): @@ -87,7 +89,8 @@ during the operation checkok, a failure occurred, and the cause of the error was self.prepare_jenkins_data() self.mock_jenkins_build_job(return_value=0) self.mock_subprocess_check_output(return_value=b'published-Epol-src') - self.mock_request(side_effect=[resp, resp, resp, resp, not_exist_date_info_data]) + test_comment_data = self.make_need_content('mock_test_comments.txt', MOCK_DATA_FILE) + self.mock_request(side_effect=[resp, resp, test_comment_data, resp, resp, not_exist_date_info_data]) self.assert_result() def test_checkok_abnormal_date_info(self): @@ -104,7 +107,8 @@ during the operation checkok, a failure occurred, and the cause of the error was self.prepare_jenkins_data() self.mock_jenkins_build_job(return_value=0) self.mock_subprocess_check_output(return_value=b'published-Epol-src') - self.mock_request(side_effect=[resp, resp, resp, resp, abnormal_date_info_data]) + test_comment_data = self.make_need_content('mock_test_comments.txt', MOCK_DATA_FILE) + self.mock_request(side_effect=[resp, resp, test_comment_data, resp, resp, abnormal_date_info_data]) self.assert_result() def test_checkok_date_info_index_error(self): @@ -121,7 +125,8 @@ during the operation checkok, a failure occurred, and the cause of the error was self.prepare_jenkins_data() self.mock_jenkins_build_job(return_value=0) self.mock_subprocess_check_output(return_value=b'published-Epol-src') - self.mock_request(side_effect=[resp, resp, resp, resp, abnormal_date_info_data]) + test_comment_data = self.make_need_content('mock_test_comments.txt', MOCK_DATA_FILE) + self.mock_request(side_effect=[resp, resp, test_comment_data, resp, resp, abnormal_date_info_data]) self.assert_result() def test_cvrfok_success(self): @@ -135,8 +140,10 @@ successful announcement "--jenkinskey=marykey", "--publishuser=tom", "--publishkey=tomkey", "I40769"] resp = self.make_expect_data(200, 'releasepart.txt') mock_post_data = self.make_need_content('mock_post_data.txt', MOCK_DATA_FILE) + test_comment_data = self.make_need_content('mock_test_comments.txt', MOCK_DATA_FILE) self.mock_request(return_value=resp) self.mock_requests_post(return_value=mock_post_data) + self.mock_request(side_effect=[resp, resp, test_comment_data]) self.assert_result() def test_cvrfok_successfully_not_in_text(self): @@ -150,7 +157,8 @@ during the operation cvrfok, a failure occurred, and the cause of the error was "--jenkinskey=marykey", "--publishuser=tom", "--publishkey=tomkey", "I40769"] resp = self.make_expect_data(200, 'releasepart.txt') mock_post_data = self.make_need_content('mock_post_failed_data.txt', MOCK_DATA_FILE) - self.mock_request(return_value=resp) + test_comment_data = self.make_need_content('mock_test_comments.txt', MOCK_DATA_FILE) + self.mock_request(side_effect=[resp, resp, test_comment_data]) self.mock_requests_post(return_value=mock_post_data) self.assert_result() @@ -166,6 +174,10 @@ during the operation cvrfok, a failure occurred, and the cause of the error was resp = self.make_expect_data(200, 'releasepart.txt') mock_post_data = self.make_object_data(404, "404 not found") self.mock_request(return_value=resp) + + test_comment_data = self.make_need_content('mock_test_comments.txt', MOCK_DATA_FILE) + self.mock_request(side_effect=[resp, resp, test_comment_data]) + self.mock_requests_post(return_value=mock_post_data) self.assert_result() @@ -180,7 +192,8 @@ during the operation cvrfok, a failure occurred, and the cause of the error was "--jenkinskey=marykey", "--publishuser=tom", "--publishkey=tomkey", "I40769"] resp = self.make_expect_data(200, 'releasepart.txt') mock_post_data = self.make_object_data(200, "") - self.mock_request(return_value=resp) + test_comment_data = self.make_need_content('mock_test_comments.txt', MOCK_DATA_FILE) + self.mock_request(side_effect=[resp, resp, test_comment_data]) self.mock_requests_post(return_value=mock_post_data) self.assert_result() diff --git a/release-assistant/test/test_start/mock_data/mock_update_label.txt b/release-assistant/test/test_start/mock_data/mock_update_label.txt new file mode 100644 index 0000000000000000000000000000000000000000..b4656e7692ba9cf4178de71d0fa011cdb4cbedf8 --- /dev/null +++ b/release-assistant/test/test_start/mock_data/mock_update_label.txt @@ -0,0 +1 @@ +[{"id":140051492,"name":"feat","color":"ededed","repository_id":16929727,"url":"https://gitee.com/api/v5/repos/tushenmei/release-tools/labels/feat","created_at":"2022-01-06T11:19:37+08:00","updated_at":"2022-01-06T11:19:37+08:00"},{"id":116771435,"name":"bug","color":"DB2828","repository_id":16929727,"url":"https://gitee.com/api/v5/repos/tushenmei/release-tools/labels/bug","created_at":"2021-07-13T11:19:56+08:00","updated_at":"2022-01-06T11:19:37+08:00"},{"id":140107476,"name":"feat1","color":"ededed","repository_id":16929727,"url":"https://gitee.com/api/v5/repos/tushenmei/release-tools/labels/feat1","created_at":"2022-01-06T14:39:54+08:00","updated_at":"2022-01-06T14:39:54+08:00"},{"id":140107477,"name":"bug1","color":"ededed","repository_id":16929727,"url":"https://gitee.com/api/v5/repos/tushenmei/release-tools/labels/bug1","created_at":"2022-01-06T14:39:54+08:00","updated_at":"2022-01-06T14:39:54+08:00"},{"id":140114323,"name":"cccc","color":"ededed","repository_id":16929727,"url":"https://gitee.com/api/v5/repos/tushenmei/release-tools/labels/cccc","created_at":"2022-01-06T15:13:42+08:00","updated_at":"2022-01-06T15:13:42+08:00"},{"id":140114324,"name":"dddd","color":"ededed","repository_id":16929727,"url":"https://gitee.com/api/v5/repos/tushenmei/release-tools/labels/dddd","created_at":"2022-01-06T15:13:42+08:00","updated_at":"2022-01-06T15:13:42+08:00"}] \ No newline at end of file diff --git a/release-assistant/test/test_start/test_start_cli.py b/release-assistant/test/test_start/test_start_cli.py index 5e59b466aa495ca796e6ccaee9f7143117bda930..22e385b22d40f25d7fa85efe30493f2aecddae5f 100644 --- a/release-assistant/test/test_start/test_start_cli.py +++ b/release-assistant/test/test_start/test_start_cli.py @@ -52,7 +52,8 @@ class TestStart(TestMixin): self.mock_obs_cloud_get_objects(return_value=mock_r) read_excel = pd.read_excel(Path(MOCK_DATA_FILE, "mock_cve_data.xlsx"), sheet_name="cve_list") self.mock_pandas_read_excel(return_value=read_excel) - self.mock_request(side_effect=[resp, resp, resp, mock_init_r]) + mock_label = self.make_need_content('mock_update_label.txt', MOCK_DATA_FILE) + self.mock_request(side_effect=[resp, resp, resp, mock_init_r, mock_label]) mock_get_r = self.make_object_data(200, "The number of requests is too frequent, " "please try again later, there is currently a task being processed") self.mock_requests_get(side_effect=[mock_get_r])