From 79e32b231bc93acf4183c3775d51baa908e05f21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=94=A1=E9=93=96?= Date: Tue, 21 Jan 2025 11:51:45 +0800 Subject: [PATCH 1/8] ci/tenon: PR title detection supports driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 蔡铖 --- jobs/tenon/module.env | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jobs/tenon/module.env b/jobs/tenon/module.env index 409d5e0..42313e7 100644 --- a/jobs/tenon/module.env +++ b/jobs/tenon/module.env @@ -1 +1 @@ -export REPO_MODULE="(lib|plat|arch|build|ci|doc)" \ No newline at end of file +export REPO_MODULE="(lib|plat|arch|build|ci|doc|driver)" \ No newline at end of file -- Gitee From 703235fb644711a3c65440a272d4d25282fa7232 Mon Sep 17 00:00:00 2001 From: wangguokun Date: Mon, 10 Feb 2025 18:07:24 +0800 Subject: [PATCH 2/8] ci/lib: provide ci for lib Signed-off-by: wangguokun --- jobs/common/commit_check/checkpatch.uk | 4 +- jobs/lib/.jenkinsfile | 337 ++++++++++++++++++ ...d-mem-config_ukmmap_schedcoop_syscall-shim | 1 + ...em-config_ukmmap_schedprio_nolibc_tickless | 1 + ...d-mem-config_ukvmem_schedprio_syscall-shim | 1 + jobs/lib/config/repos/lib_config_Tenon.json | 46 +++ jobs/lib/module.env | 2 +- jobs/lib/warning_check/known_warnings | 4 + 8 files changed, 393 insertions(+), 3 deletions(-) create mode 100644 jobs/lib/.jenkinsfile create mode 100644 jobs/lib/config/compile/lib-reserved-mem-config_ukmmap_schedcoop_syscall-shim create mode 100644 jobs/lib/config/compile/lib-reserved-mem-config_ukmmap_schedprio_nolibc_tickless create mode 100644 jobs/lib/config/compile/lib-reserved-mem-config_ukvmem_schedprio_syscall-shim create mode 100644 jobs/lib/config/repos/lib_config_Tenon.json create mode 100644 jobs/lib/warning_check/known_warnings diff --git a/jobs/common/commit_check/checkpatch.uk b/jobs/common/commit_check/checkpatch.uk index 3ae4d08..f29e060 100755 --- a/jobs/common/commit_check/checkpatch.uk +++ b/jobs/common/commit_check/checkpatch.uk @@ -26,8 +26,8 @@ if [ ! -x "${_CHECKPATCHPL}" ]; then fi _CHECKPATCHCONF= -if [ -f "${_SELF_BASE}/../../.checkpatch.conf" ]; then - _CHECKPATCHCONF=$( cat "${_SELF_BASE}/../../.checkpatch.conf" ) +if [ -f "${_SELF_BASE}/.checkpatch.conf" ]; then + _CHECKPATCHCONF=$( cat "${_SELF_BASE}/.checkpatch.conf" ) fi exec "${_CHECKPATCHPL}" ${_CHECKPATCHCONF} "$@" diff --git a/jobs/lib/.jenkinsfile b/jobs/lib/.jenkinsfile new file mode 100644 index 0000000..bd05aa8 --- /dev/null +++ b/jobs/lib/.jenkinsfile @@ -0,0 +1,337 @@ +import groovy.transform.Field + +@Field int nBuildThreads = 1 +@Field def constant +@Field def module +@Field def tolNum = 4 +@Field def exeNum = 0 +@Field def skipNum = 0 +@Field def sucNum = 0 +@Field def failNum = 0 +@Field def error_handler = null +@Field def stageStatus = [:] +@Field def repo_config = [] +@Field def prComment = "" +@Field def userParam = [] + +def pr_handler +def checker +def parallel_handler +def compile_handler +def uttest_handler +def repo_handler + +pipeline { + agent any + + environment { + CI_LOG_URL = "[日志信息](${env.BUILD_URL}console)" + SKIP_COMMAND = "/skip" + } + + options { + ansiColor('xterm') + } + + stages { + stage("初始化变量") { + steps { + script { + constant = load("${env.WORKSPACE}/utils/constant/path.groovy") + pr_handler = load(constant.PR_DEPENDED_HANDLER_PATH) + checker = load(constant.TITLE_CHECKER_DIR_PATH + "checkout.groovy") + parallel_handler = load(constant.PARALLEL_HANDLER_PATH) + compile_handler = load(constant.COMPILE_CHECKER_PATH) + uttest_handler = load(constant.UTTEST_CHECKER_PATH) + error_handler = load(constant.FAILURES_CHECKER_PATH) + repo_handler = load(constant.REPOS_CHECKER_PATH) + } + + script { + module = sh ( + script: """ + #!/bin/sh + set -e +x + . ${env.WORKSPACE}/jobs/lib/module.env + echo \$REPO_MODULE + """, + returnStdout: true + ).trim() + } + + } + } + + stage('初始化任务') { + steps { + script { + def r = "开始CI测试,任务编号:${env.BUILD_NUMBER} \n" + + "任务链接:${env.BUILD_URL}pipeline-console/" + + addGiteeMRComment comment: r + + // 获取 CPU 核心数 + cpuCores = sh(script: 'nproc', returnStdout: true).trim() + echo "Detected CPU Cores: ${cpuCores}" + nBuildThreads = Integer.parseInt(cpuCores) / 2 + } + } + } + + stage("仓库初始化") { + steps { + script { + if (!env.giteeTargetNamespace || !env.giteeTargetRepoName || !env.giteePullRequestIid) { + addGiteeMRComment comment: "${env.STAGE_NAME} 失败: 关键环境变量缺失" + error """关键环境变量为空: + - giteeTargetNamespace=${env.giteeTargetNamespace} + - giteeTargetRepoName=${env.giteeTargetRepoName} + - giteePullRequestIid=${env.giteePullRequestIid} + """ + } + + def repo_config_name = "lib_config_Tenon.json" + def repo_config_path = "${env.WORKSPACE}/jobs/lib/config/" + + "/repos/" + repo_config_name + def compile_path = "${env.WORKSPACE}/jobs/lib/config/compile" + def compile_dir = new File(compile_path) + def dirExists = sh( + script: "[ -d ${compile_path} ] && echo exists || echo missing", + returnStdout: true + ).trim() + def hasFiles = sh( + script: "[ -n \"\$(ls -A ${compile_path} 2>/dev/null)\" ] && echo files || echo empty", + returnStdout: true + ).trim() + + if (dirExists == "exists" && hasFiles == "files") { + echo """ + 配置文件存在: ${repo_config_path} + 编译文件存在: ${compile_path} + """ + } else { + def errorMessage = """ + 未配置 ${env.giteeTargetRepoName} 微库相应的 CI 配置或编译文件! + 请联系管理员确认该lib微库已配置CI! + """ + + addGiteeMRComment comment: errorMessage.trim() + error errorMessage.trim() + } + + def repoBranchMap = [:] + sh ( + label: "init libs", + script: """ + #!/bin/sh + set -e +x + mkdir -p ${env.WORKSPACE}/workdir + cd ${env.WORKSPACE}/workdir + git clone git@gitee.com:tenonos/board-support-package.git + cd board-support-package + bash build.sh --init-libs=${repo_config_path} + """, + returnStatus: true + ) + def pr_data = [ + [ + repo_owner: "${env.giteeTargetNamespace}", + repo_name: "${env.giteeTargetRepoName}", + pr_number: "${env.giteePullRequestIid}" + ] + ] + + repo_config = repo_handler.Parse(repo_config_path) + def targetLib = repo_config.libs.find { it.name == env.giteeTargetRepoName } + + if (targetLib?.version) { + repoBranchMap[env.giteeTargetRepoName] = targetLib.version + } else { + addGiteeMRComment comment: "${env.STAGE_NAME}失败" + error "配置文件格式错误: ${repo_config_name}" + } + + // 针对相关PR需要合入Tenon + if (repo_config.tenon?.branch) { + repoBranchMap["tenon"] = repo_config.tenon.branch + } else { + addGiteeMRComment comment: "${env.STAGE_NAME}失败" + error "配置文件格式错误: ${repo_config_name}" + } + + pr_handler.GetDependedPRInfo(pr_data) + pr_handler.MergePR(pr_data, repoBranchMap) + } + } + } + + stage("评论解析") { + when { + expression { + env.giteeActionType == "NOTE" && + env.giteeTriggerPhrase && + env.giteeTriggerPhrase.startsWith(env.SKIP_COMMAND) + } + } + steps { + script { + prComment = env.giteeTriggerPhrase.trim() + def userParamString = "" + if (prComment.startsWith(env.SKIP_COMMAND)) { + userParamString = prComment.minus(env.SKIP_COMMAND).trim() + } + userParam = userParamString.split(/\s*,\s*/) + } + } + } + + stage("PR标题合法性检查") { + when { + expression { + def skip_flag = userParam.contains("title") + if (skip_flag) { + skipNum += 1 + } + return !skip_flag + } + } + steps { + script { + exeNum += 1 + def res = checker.checkTitle(env.giteePullRequestTitle, module) + + if (!res) { + stageStatus[env.STAGE_NAME] = [status: 1, remark: ["标题要求可见:[PR标题合法性检查](https://gitee.com/tenonos/ci)"]] + error "check title failed ${module}" + } + + stageStatus[env.STAGE_NAME] = [status: 0, remark: ""] + + } + } + } + + stage("Commit静态检查") { + when { + expression { + def skip_flag = userParam.contains("checkpatch") + if (skip_flag) { + skipNum += 1 + } + return !skip_flag + } + } + steps { + script { + exeNum += 1 + def env_jsonbody = readJSON text: env.jsonBody + def commit_num = env_jsonbody.pull_request.commits + def res = sh ( + label: "checkpatch", + script: """ + #!/bin/bash + set -e +x + cd workdir/libs/${env.giteeTargetRepoName} + . ${env.WORKSPACE}/jobs/lib/module.env + git format-patch HEAD~${commit_num}..HEAD + ${env.WORKSPACE}/jobs/common/commit_check/checkpatch.uk ./00* + """, + returnStatus: true + ) + + if (res) { + stageStatus[env.STAGE_NAME] = [status: 1, remark: "${CI_LOG_URL}"] + error "Commit静态检测失败" + } + stageStatus[env.STAGE_NAME] = [status: 0, remark: ["检查DCO、Commit标题格式、代码规范性等"]] + } + } + } + + stage("编译") { + when { + expression { + def skip_flag = userParam.contains("compile") + if (skip_flag) { + skipNum += 1 + } + return !skip_flag + } + } + steps { + script { + exeNum += 1 + def final_config_path = "${env.WORKSPACE}/workdir/tmp_config/${env.giteeTargetRepoName}/compile" + def lib_config_path = "${env.WORKSPACE}/jobs/lib/config/compile" + def tenon_config_path = "${env.WORKSPACE}/jobs/tenon/config/compile" + sh ( + script: """ + #!/bin/bash + set -e +x + mkdir -p ${final_config_path} + + for file in "${lib_config_path}"/*; do + [ -f "\$file" ] || continue + + filename=\$(basename "\$file") + matched_name=\${filename#"${env.giteeTargetRepoName}-"} + if [ -f "${tenon_config_path}/\$matched_name" ]; then + echo "合并配置: \$filename ->\$matched_name" + cat "\$file" "${tenon_config_path}/\$matched_name" > "${final_config_path}/\$filename" + else + echo "警告: ${tenon_config_path}/\$matched_name 不存在,仅复制 \$file" + cp "\$file" "${final_config_path}/\$filename" + fi + done + """, + returnStatus: true + ) + def dir = new File(final_config_path) + parallel_handler.parallel_tool(dir, { compile_handler.compile_check(it, stageStatus) }, stageStatus) + stageStatus[env.STAGE_NAME] = [status: 0, remark: ["所有配置文件编译成功"]] + } + } + } + + stage("UT测试") { + when { + expression { + def skip_flag = userParam.contains("ut") + if (skip_flag) { + skipNum += 1 + } + return !skip_flag + } + } + steps { + script { + exeNum += 1 + def dir = new File("${env.WORKSPACE}/jobs/lib/config/compile") + parallel_handler.parallel_tool(dir, { uttest_handler.ut_test_check(it, stageStatus) }, stageStatus) + stageStatus[env.STAGE_NAME] = [status: 0, remark: ["所有配置文件UT测试通过"]] + } + } + } + } + + post { + always { + script { + def ret = "|任务名|测试状态|备注|\n|-|-|-|\n" + stageStatus.each { stageName, stageInfo -> + if (stageInfo.status) { + failNum += 1 + } else { + sucNum += 1 + } + def statusText = stageInfo.status == 0 ? "成功" : "**失败**" + def remarkText = stageInfo.remark.join("
") + ret += "|${stageName}|${statusText}|${remarkText}|\n" + } + addGiteeMRComment comment: "结束CI测试,计划测试" + "**$tolNum**" + "项-->已经测试" + "**$exeNum**" + "项:成功" + "**$sucNum**" + "项,失败" + "**$failNum**" + "项,跳过" + "**$skipNum**" + "项"+ + "\n" + ret + + "\n\n任务链接: ${env.BUILD_URL}pipeline-console/ " + } + } + } +} diff --git a/jobs/lib/config/compile/lib-reserved-mem-config_ukmmap_schedcoop_syscall-shim b/jobs/lib/config/compile/lib-reserved-mem-config_ukmmap_schedcoop_syscall-shim new file mode 100644 index 0000000..682c00f --- /dev/null +++ b/jobs/lib/config/compile/lib-reserved-mem-config_ukmmap_schedcoop_syscall-shim @@ -0,0 +1 @@ +CONFIG_LIBRESERVEDMEM=y diff --git a/jobs/lib/config/compile/lib-reserved-mem-config_ukmmap_schedprio_nolibc_tickless b/jobs/lib/config/compile/lib-reserved-mem-config_ukmmap_schedprio_nolibc_tickless new file mode 100644 index 0000000..682c00f --- /dev/null +++ b/jobs/lib/config/compile/lib-reserved-mem-config_ukmmap_schedprio_nolibc_tickless @@ -0,0 +1 @@ +CONFIG_LIBRESERVEDMEM=y diff --git a/jobs/lib/config/compile/lib-reserved-mem-config_ukvmem_schedprio_syscall-shim b/jobs/lib/config/compile/lib-reserved-mem-config_ukvmem_schedprio_syscall-shim new file mode 100644 index 0000000..682c00f --- /dev/null +++ b/jobs/lib/config/compile/lib-reserved-mem-config_ukvmem_schedprio_syscall-shim @@ -0,0 +1 @@ +CONFIG_LIBRESERVEDMEM=y diff --git a/jobs/lib/config/repos/lib_config_Tenon.json b/jobs/lib/config/repos/lib_config_Tenon.json new file mode 100644 index 0000000..46e1f57 --- /dev/null +++ b/jobs/lib/config/repos/lib_config_Tenon.json @@ -0,0 +1,46 @@ +{ + "apps": [ + { + "name": "app-rtos-benchmark", + "version": "release-v0.1.0", + "url": "https://gitee.com/tenonos/app-rtos-benchmark.git" + } + ], + "plats": [ + { + "name": "plat-raspi", + "version": "release-v0.1.0", + "url": "https://gitee.com/tenonos/plat-raspi.git" + }, + { + "name": "plat-rk3568", + "version": "release-v0.1.0", + "url": "https://gitee.com/tenonos/plat-rk3568.git" + } + ], + "libs": [ + { + "name": "lib-tlsf", + "version": "release-v0.1.0", + "url": "https://gitee.com/tenonos-mirror/lib-tlsf.git" + }, + { + "name": "lib-musl", + "version": "master", + "url": "https://gitee.com/tenonos-mirror/lib-musl.git" + }, + { + "name": "lib-reserved-mem", + "version": "master", + "url": "https://gitee.com/tenonos/lib-reserved-mem.git" + } + ], + "tenon": { + "branch": "master", + "url": "https://gitee.com/tenonos/tenon.git" + }, + "bsp": { + "branch": "master", + "url": "https://gitee.com/tenonos/board-support-package.git" + } +} diff --git a/jobs/lib/module.env b/jobs/lib/module.env index ee42bf8..a212887 100644 --- a/jobs/lib/module.env +++ b/jobs/lib/module.env @@ -1 +1 @@ -export REPO_MODULE=(feature|kconfig|build|generated|script) \ No newline at end of file +export REPO_MODULE="(feature|kconfig|build|generated|script)" \ No newline at end of file diff --git a/jobs/lib/warning_check/known_warnings b/jobs/lib/warning_check/known_warnings new file mode 100644 index 0000000..ed9c742 --- /dev/null +++ b/jobs/lib/warning_check/known_warnings @@ -0,0 +1,4 @@ +.*/syscall_prologue.h:[0-9]+:[0-9]+: warning: ‘noreturn’ function does return +/usr/lib/gcc-cross/aarch64-linux-gnu/11/../../../../aarch64-linux-gnu/bin/ld: warning: -z relro ignored +cc1: warning: function may return address of local variable \[-Wreturn-local-addr\] +.*/libmusl/origin.* \ No newline at end of file -- Gitee From 3c75af79a891af5850c69252745ebba3c689ef04 Mon Sep 17 00:00:00 2001 From: wangguokun Date: Wed, 12 Feb 2025 17:49:37 +0800 Subject: [PATCH 3/8] ci/lib: [bugfix]Configuration file management of multiple lib micro libraries Signed-off-by: wangguokun --- jobs/lib/.jenkinsfile | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/jobs/lib/.jenkinsfile b/jobs/lib/.jenkinsfile index bd05aa8..0efb955 100644 --- a/jobs/lib/.jenkinsfile +++ b/jobs/lib/.jenkinsfile @@ -270,23 +270,32 @@ pipeline { set -e +x mkdir -p ${final_config_path} - for file in "${lib_config_path}"/*; do + for file in "${tenon_config_path}"/*; do [ -f "\$file" ] || continue filename=\$(basename "\$file") - matched_name=\${filename#"${env.giteeTargetRepoName}-"} - if [ -f "${tenon_config_path}/\$matched_name" ]; then - echo "合并配置: \$filename ->\$matched_name" - cat "\$file" "${tenon_config_path}/\$matched_name" > "${final_config_path}/\$filename" + lib_file="${lib_config_path}/${env.giteeTargetRepoName}-\$filename" + + if [ -f "\$lib_file" ]; then + echo "合并配置: \$lib_file + \$file -> ${final_config_path}/\$filename" + cat "\$lib_file" \$file > "${final_config_path}/\$filename" else - echo "警告: ${tenon_config_path}/\$matched_name 不存在,仅复制 \$file" - cp "\$file" "${final_config_path}/\$filename" + echo "警告: ${lib_config_path}/${env.giteeTargetRepoName}-\$filename 不存在!" fi done """, returnStatus: true ) def dir = new File(final_config_path) + def mergedFiles = sh( + script: "find ${final_config_path} -type f | wc -l", + returnStdout: true + ).trim().toInteger() + + if (mergedFiles < 3) { + addGiteeMRComment comment: "编译失败,请联系管理员确认CI是否支持该微库!" + error "错误: 成功合并的配置文件数量少于 3(${mergedFiles}),请检查!" + } parallel_handler.parallel_tool(dir, { compile_handler.compile_check(it, stageStatus) }, stageStatus) stageStatus[env.STAGE_NAME] = [status: 0, remark: ["所有配置文件编译成功"]] } @@ -306,7 +315,7 @@ pipeline { steps { script { exeNum += 1 - def dir = new File("${env.WORKSPACE}/jobs/lib/config/compile") + def dir = new File("${env.WORKSPACE}/jobs/tenon/config/compile") parallel_handler.parallel_tool(dir, { uttest_handler.ut_test_check(it, stageStatus) }, stageStatus) stageStatus[env.STAGE_NAME] = [status: 0, remark: ["所有配置文件UT测试通过"]] } -- Gitee From 13be0e1b1de1203f858fcabaf25cd1926ce28dce Mon Sep 17 00:00:00 2001 From: wangguokun Date: Wed, 12 Feb 2025 17:51:03 +0800 Subject: [PATCH 4/8] ci/lib: add lib-phy config files Signed-off-by: wangguokun --- .../compile/lib-phy-config_ukmmap_schedcoop_syscall-shim | 1 + .../compile/lib-phy-config_ukmmap_schedprio_nolibc_tickless | 1 + .../compile/lib-phy-config_ukvmem_schedprio_syscall-shim | 1 + jobs/lib/config/repos/lib_config_Tenon.json | 5 +++++ 4 files changed, 8 insertions(+) create mode 100644 jobs/lib/config/compile/lib-phy-config_ukmmap_schedcoop_syscall-shim create mode 100644 jobs/lib/config/compile/lib-phy-config_ukmmap_schedprio_nolibc_tickless create mode 100644 jobs/lib/config/compile/lib-phy-config_ukvmem_schedprio_syscall-shim diff --git a/jobs/lib/config/compile/lib-phy-config_ukmmap_schedcoop_syscall-shim b/jobs/lib/config/compile/lib-phy-config_ukmmap_schedcoop_syscall-shim new file mode 100644 index 0000000..f2d2398 --- /dev/null +++ b/jobs/lib/config/compile/lib-phy-config_ukmmap_schedcoop_syscall-shim @@ -0,0 +1 @@ +CONFIG_LIBTNPHY=y diff --git a/jobs/lib/config/compile/lib-phy-config_ukmmap_schedprio_nolibc_tickless b/jobs/lib/config/compile/lib-phy-config_ukmmap_schedprio_nolibc_tickless new file mode 100644 index 0000000..f2d2398 --- /dev/null +++ b/jobs/lib/config/compile/lib-phy-config_ukmmap_schedprio_nolibc_tickless @@ -0,0 +1 @@ +CONFIG_LIBTNPHY=y diff --git a/jobs/lib/config/compile/lib-phy-config_ukvmem_schedprio_syscall-shim b/jobs/lib/config/compile/lib-phy-config_ukvmem_schedprio_syscall-shim new file mode 100644 index 0000000..f2d2398 --- /dev/null +++ b/jobs/lib/config/compile/lib-phy-config_ukvmem_schedprio_syscall-shim @@ -0,0 +1 @@ +CONFIG_LIBTNPHY=y diff --git a/jobs/lib/config/repos/lib_config_Tenon.json b/jobs/lib/config/repos/lib_config_Tenon.json index 46e1f57..9df773c 100644 --- a/jobs/lib/config/repos/lib_config_Tenon.json +++ b/jobs/lib/config/repos/lib_config_Tenon.json @@ -33,6 +33,11 @@ "name": "lib-reserved-mem", "version": "master", "url": "https://gitee.com/tenonos/lib-reserved-mem.git" + }, + { + "name": "lib-phy", + "version": "master", + "url": "https://gitee.com/tenonos/lib-phy.git" } ], "tenon": { -- Gitee From 6dc56ac29987070fed87eda744919eede211fc4a Mon Sep 17 00:00:00 2001 From: wangguokun Date: Wed, 12 Feb 2025 18:35:10 +0800 Subject: [PATCH 5/8] ci/entry: Provide a unified CI task portal Signed-off-by: wangguokun --- jobs/entry/.jenkinsfile | 95 +++++++++++++++++++++++++++++++++++++ jobs/lib/.jenkinsfile | 40 ++++++++++++---- jobs/tenon/.jenkinsfile | 28 +++++++++-- utils/commet/comment.groovy | 17 +++++++ utils/constant/path.groovy | 4 +- 5 files changed, 170 insertions(+), 14 deletions(-) create mode 100644 jobs/entry/.jenkinsfile create mode 100644 utils/commet/comment.groovy diff --git a/jobs/entry/.jenkinsfile b/jobs/entry/.jenkinsfile new file mode 100644 index 0000000..d687094 --- /dev/null +++ b/jobs/entry/.jenkinsfile @@ -0,0 +1,95 @@ +import groovy.transform.Field +import java.util.Base64 + +@Field def env_path +@Field def child_job + +pipeline { + agent any + + stages { + stage("任务解析") { + steps { + script { + def repo_name = env.giteeTargetRepoName + if (repo_name.startsWith("lib-")) { + echo "目标微库为lib微库, 即将调用lib微库任务" + child_job = "LIB" + } else if (repo_name.startsWith("Tenon")){ + echo "目标微库为lib微库, 即将调用lib微库任务" + child_job = "CI_Tenon" + } else { + error "未知目标微库:${repo_name}!请确认CI是否支持该类型微库!" + } + + env_path = "${env.WORKSPACE}/../pass_env/${repo_name}.txt" + } + } + } + stage('保存gitee相关环境变量') { + steps { + script { + + sh ( + label: "gitee env", + script: """ + #!/bin/sh + set -e +x + if [ -f "${env_path}" ]; then + echo "文件 ${env_path} 已存在, 重新格式化该文件" + rm -r ${env_path} + touch ${env_path} + else + echo "文件 ${env_path} 不存在,创建目录并创建文件" + mkdir -p "\$(dirname "${env_path}")" + touch "${env_path}" + fi + env | grep '^gitee' > ${env_path} + """, + returnStatus: true + ) + } + } + } + stage('保存Json环境变量') { + steps { + script { + sh ( + label: "gitee env", + script: """ + #!/bin/sh + set -e +x + echo 'jsonBody=${env.jsonBody}' >> ${env_path} + """, + returnStatus: true + ) + sh ( + label: "gitee env", + script: """ + #!/bin/sh + set -e +x + echo 'ref=${env.ref}' >> ${env_path} + echo 'sha=${env.sha}' >> ${env_path} + echo 'noteBody=${env.noteBody}' >> ${env_path} + + """, + returnStatus: true + ) + } + script { + def allEnvVars = sh(script: "env", returnStdout: true).trim() + println "系统环境变量:\n${allEnvVars}" + } + } + } + stage('子任务分发') { + steps { + script { + build job: "${child_job}", parameters: [ + string(name: 'repo_name', value: env.giteeTargetRepoName) + ] + } + } + } + } +} diff --git a/jobs/lib/.jenkinsfile b/jobs/lib/.jenkinsfile index 0efb955..8e1f10f 100644 --- a/jobs/lib/.jenkinsfile +++ b/jobs/lib/.jenkinsfile @@ -24,6 +24,10 @@ def repo_handler pipeline { agent any + parameters { + string(name: 'repo_name', defaultValue: '', description: '目标仓库名') + } + environment { CI_LOG_URL = "[日志信息](${env.BUILD_URL}console)" SKIP_COMMAND = "/skip" @@ -34,6 +38,19 @@ pipeline { } stages { + stage("环境变量预处理") { + steps { + script { + def env_path = "${env.WORKSPACE}/../pass_env/${params.repo_name}.txt" + def envVars = readFile("${env_path}").trim().split('\n') + envVars.each { line -> + def (key, value) = line.tokenize('=') + env[key] = value.trim() + } + } + } + } + stage("初始化变量") { steps { script { @@ -45,6 +62,7 @@ pipeline { uttest_handler = load(constant.UTTEST_CHECKER_PATH) error_handler = load(constant.FAILURES_CHECKER_PATH) repo_handler = load(constant.REPOS_CHECKER_PATH) + commet_by_curl = load(constant.ADDGITEECOMMEMTBYCURL) } script { @@ -68,7 +86,7 @@ pipeline { def r = "开始CI测试,任务编号:${env.BUILD_NUMBER} \n" + "任务链接:${env.BUILD_URL}pipeline-console/" - addGiteeMRComment comment: r + commet_by_curl.addGiteeCommentByCurl(r) // 获取 CPU 核心数 cpuCores = sh(script: 'nproc', returnStdout: true).trim() @@ -82,7 +100,8 @@ pipeline { steps { script { if (!env.giteeTargetNamespace || !env.giteeTargetRepoName || !env.giteePullRequestIid) { - addGiteeMRComment comment: "${env.STAGE_NAME} 失败: 关键环境变量缺失" + def comments = "${env.STAGE_NAME} 失败: 关键环境变量缺失" + commet_by_curl.addGiteeCommentByCurl(comments) error """关键环境变量为空: - giteeTargetNamespace=${env.giteeTargetNamespace} - giteeTargetRepoName=${env.giteeTargetRepoName} @@ -114,8 +133,7 @@ pipeline { 未配置 ${env.giteeTargetRepoName} 微库相应的 CI 配置或编译文件! 请联系管理员确认该lib微库已配置CI! """ - - addGiteeMRComment comment: errorMessage.trim() + commet_by_curl.addGiteeCommentByCurl(errorMessage.trim()) error errorMessage.trim() } @@ -147,7 +165,8 @@ pipeline { if (targetLib?.version) { repoBranchMap[env.giteeTargetRepoName] = targetLib.version } else { - addGiteeMRComment comment: "${env.STAGE_NAME}失败" + def comments = "${env.STAGE_NAME}失败" + commet_by_curl.addGiteeCommentByCurl(comments) error "配置文件格式错误: ${repo_config_name}" } @@ -155,7 +174,8 @@ pipeline { if (repo_config.tenon?.branch) { repoBranchMap["tenon"] = repo_config.tenon.branch } else { - addGiteeMRComment comment: "${env.STAGE_NAME}失败" + def comments = "${env.STAGE_NAME}失败" + commet_by_curl.addGiteeCommentByCurl(comments) error "配置文件格式错误: ${repo_config_name}" } @@ -293,7 +313,8 @@ pipeline { ).trim().toInteger() if (mergedFiles < 3) { - addGiteeMRComment comment: "编译失败,请联系管理员确认CI是否支持该微库!" + def comments = "编译失败,请联系管理员确认CI是否支持该微库!" + commet_by_curl.addGiteeCommentByCurl(comments) error "错误: 成功合并的配置文件数量少于 3(${mergedFiles}),请检查!" } parallel_handler.parallel_tool(dir, { compile_handler.compile_check(it, stageStatus) }, stageStatus) @@ -336,10 +357,11 @@ pipeline { def statusText = stageInfo.status == 0 ? "成功" : "**失败**" def remarkText = stageInfo.remark.join("
") ret += "|${stageName}|${statusText}|${remarkText}|\n" - } - addGiteeMRComment comment: "结束CI测试,计划测试" + "**$tolNum**" + "项-->已经测试" + "**$exeNum**" + "项:成功" + "**$sucNum**" + "项,失败" + "**$failNum**" + "项,跳过" + "**$skipNum**" + "项"+ + } + def comments = "结束CI测试,计划测试" + "**$tolNum**" + "项-->已经测试" + "**$exeNum**" + "项:成功" + "**$sucNum**" + "项,失败" + "**$failNum**" + "项,跳过" + "**$skipNum**" + "项"+ "\n" + ret + "\n\n任务链接: ${env.BUILD_URL}pipeline-console/ " + commet_by_curl.addGiteeCommentByCurl(comments) } } } diff --git a/jobs/tenon/.jenkinsfile b/jobs/tenon/.jenkinsfile index 117a7a9..59ba531 100644 --- a/jobs/tenon/.jenkinsfile +++ b/jobs/tenon/.jenkinsfile @@ -25,6 +25,10 @@ def benchmark_handler pipeline { agent any + parameters { + string(name: 'repo_name', defaultValue: '', description: '目标仓库名') + } + environment { CI_LOG_URL = "[日志信息](${env.BUILD_URL}console)" BENCH_COMMAND = "/bench" @@ -36,6 +40,19 @@ pipeline { } stages { + stage("环境变量预处理") { + steps { + script { + def env_path = "${env.WORKSPACE}/../pass_env/${params.repo_name}.txt" + def envVars = readFile("${env_path}").trim().split('\n') + envVars.each { line -> + def (key, value) = line.tokenize('=') + env[key] = value.trim() + } + } + } + } + stage("初始化变量") { steps { script { @@ -47,6 +64,7 @@ pipeline { uttest_handler = load(constant.UTTEST_CHECKER_PATH) error_handler = load(constant.FAILURES_CHECKER_PATH) repo_handler = load(constant.REPOS_CHECKER_PATH) + commet_by_curl = load(constant.ADDGITEECOMMEMTBYCURL) } script { @@ -70,7 +88,7 @@ pipeline { def r = "开始CI测试,任务编号:${env.BUILD_NUMBER} \n" + "任务链接:${env.BUILD_URL}pipeline-console/" - addGiteeMRComment comment: r + commet_by_curl.addGiteeCommentByCurl(r) // 获取 CPU 核心数 cpuCores = sh(script: 'nproc', returnStdout: true).trim() @@ -111,7 +129,8 @@ pipeline { if (repo_config.tenon?.branch) { repoBranchMap["tenon"] = repo_config.tenon.branch } else { - addGiteeMRComment comment: "${env.STAGE_NAME}失败" + def comments = "${env.STAGE_NAME}失败" + commet_by_curl.addGiteeCommentByCurl(comments) error "配置文件格式错误: ${repo_config_name}" } @@ -279,10 +298,11 @@ pipeline { def statusText = stageInfo.status == 0 ? "成功" : "**失败**" def remarkText = stageInfo.remark.join("
") ret += "|${stageName}|${statusText}|${remarkText}|\n" - } - addGiteeMRComment comment: "结束CI测试,计划测试" + "**$tolNum**" + "项-->已经测试" + "**$exeNum**" + "项:成功" + "**$sucNum**" + "项,失败" + "**$failNum**" + "项,跳过" + "**$skipNum**" + "项"+ + } + def comments = "结束CI测试,计划测试" + "**$tolNum**" + "项-->已经测试" + "**$exeNum**" + "项:成功" + "**$sucNum**" + "项,失败" + "**$failNum**" + "项,跳过" + "**$skipNum**" + "项"+ "\n" + ret + "\n\n任务链接: ${env.BUILD_URL}pipeline-console/ " + commet_by_curl.addGiteeCommentByCurl(comments) } } } diff --git a/utils/commet/comment.groovy b/utils/commet/comment.groovy new file mode 100644 index 0000000..1b4769c --- /dev/null +++ b/utils/commet/comment.groovy @@ -0,0 +1,17 @@ +def addGiteeCommentByCurl(String comment) { + def owner = env.giteeTargetNamespace.toLowerCase() + def repo = env.giteeTargetRepoName.toLowerCase() + def number = env.giteePullRequestIid + + def url = "https://gitee.com/api/v5/repos/${owner}/${repo}/pulls/${number}/comments" + withCredentials([string(credentialsId: 'gitee_access_token', variable: 'GITEE_TOKEN')]) { + sh """ + curl -X POST "${url}" \\ + -H "Authorization: Bearer \${GITEE_TOKEN}" \\ + -H "Content-Type: application/json;charset=UTF-8" \\ + -d '{"body":"${comment}"}' + """ + } +} + +return this \ No newline at end of file diff --git a/utils/constant/path.groovy b/utils/constant/path.groovy index 0ffc921..e2c124e 100644 --- a/utils/constant/path.groovy +++ b/utils/constant/path.groovy @@ -16,6 +16,7 @@ def UTTEST_CHECKER_PATH = "${env.WORKSPACE}/jobs/tenon/uttest_check.groovy" def FAILURES_CHECKER_PATH = "${env.WORKSPACE}/utils/error_handle/error_check.groovy" def REPOS_CHECKER_PATH = "${env.WORKSPACE}/utils/config_parser/repo_handle.groovy" def BENCHMARK_CHECKER_PATH = "${env.WORKSPACE}/jobs/tenon/benchmark_check/benchmark_check.groovy" +def ADDGITEECOMMEMTBYCURL = "${env.WORKSPACE}//utils/commet/comment.groovy" return [ PR_API_PATH: PR_API_PATH, @@ -35,5 +36,6 @@ return [ UTTEST_CHECKER_PATH: UTTEST_CHECKER_PATH, FAILURES_CHECKER_PATH: FAILURES_CHECKER_PATH, REPOS_CHECKER_PATH: REPOS_CHECKER_PATH, - BENCHMARK_CHECKER_PATH: BENCHMARK_CHECKER_PATH + BENCHMARK_CHECKER_PATH: BENCHMARK_CHECKER_PATH, + ADDGITEECOMMEMTBYCURL: ADDGITEECOMMEMTBYCURL ] -- Gitee From d7538b5f080019f14cb75edb515438e24ab84390 Mon Sep 17 00:00:00 2001 From: wangguokun Date: Fri, 14 Feb 2025 18:40:33 +0800 Subject: [PATCH 6/8] ci/entry: [bugfix]: Unable to handle PR description text has special characters Signed-off-by: wangguokun --- README.md | 108 ++++++++++++++++++++++++++++++---------- jobs/entry/.jenkinsfile | 34 ++++++------- jobs/lib/.jenkinsfile | 18 ++++++- jobs/tenon/.jenkinsfile | 18 ++++++- 4 files changed, 128 insertions(+), 50 deletions(-) diff --git a/README.md b/README.md index 3747bff..d894395 100644 --- a/README.md +++ b/README.md @@ -1,43 +1,82 @@ # CI +## 项目架构 + +CI 仓库为 TenonOS 生态提供持续集成服务,覆盖代码质量检查、构建、测试等流程。其架构如下: + +``` ++------------------------+ +------------------------+ +| Jenkins CI | | Gitee API | +| (Pipeline Engine) |<---->| (PR, Issues, Comments) | ++------------------------+ +------------------------+ + | ++-----------v-----------+ +------------------------+ +| CI Configuration |----->| Common Utilities | +| (Repository Specific) | | (Reusable Components) | ++-----------+-----------+ +------------------------+ + | ++-----------v-----------+ +------------------------+ +| Build & Test Jobs |----->| Parallel Execution | +| (Compile, Test, Check)| | (Resource Handling) | ++-----------+-----------+ +------------------------+ + | ++-----------v-----------+ +| Feedback & Reports | +| (Comments, Status) | ++------------------------+ +``` + +### 核心组件 + +1. **Pipeline 引擎**: 基于 Jenkins 实现 CI 任务调度和执行 +2. **Gitee 集成**: 支持 PR 管理、评论和状态更新 +3. **通用工具库**: 提供跨仓库共享的工具集 +4. **并行执行框架**: 高效分配资源执行构建和测试任务 +5. **仓库自定义配置**: 允许各仓库定义特定的CI流程 + ## 目录结构 ```sh ├── jobs /* 各仓库门禁CI jobs */ │ ├── common /* 抽象出的公共流程,所有仓库都可以使用这些方法构建pipeline */ │ │ ├── commit_check /* 静态检查 */ │ │ ├── title_check /* PR标题检测 */ -│ │ ├── ... │ │ └── warning_check /* 编译告警检测 */ +│ ├── entry /* 入口脚本 */ │ ├── tenon /* Tenon仓库门禁CI job */ │ │ ├── config /* 配置文件 */ │ │ │ ├── benchmark /* 性能测试用配置文件 */ │ │ │ ├── compile /* 编译用配置文件 */ │ │ │ └── repos /* 仓库列表JSON文件 */ -│ │ ├── benchmark_check /* 以下为各仓库差异化脚本。构建仓库的pipeline时, */ -│ │ │ /* 请尽量使用jobs/common、utils目录下的公共流程 */ -│ │ │ /* 和方法,差异化的部分,微库在各仓库目录下。 */ -│ │ ├── compile_check.groovy -│ │ ├── ... -│ │ └── uttest_check.groovy -│ ├── lib /* 以下为各仓库门禁CI job */ -│ ├── mortise -│ ├── plat -│ ├── app -│ └── board-support-package +│ │ ├── benchmark_check /* 性能测试检查 */ +│ │ ├── compile_check.groovy /* 编译检查脚本 */ +│ │ ├── uttest_check.groovy /* 单元测试脚本 */ +│ │ └── warning_check /* 告警检查 */ +│ ├── lib /* Lib仓库门禁CI job */ +│ ├── mortise /* Mortise仓库门禁CI job */ +│ ├── plat /* Plat仓库门禁CI job */ +│ ├── app /* App仓库门禁CI job */ +│ └── board-support-package /* BSP仓库门禁CI job */ └── utils /* 门禁CI 通用工具 */ - ├── auth - ├── constant - ├── dependent_handle - ├── error_handle - ├── gitee_api - ├── parallel_handle - └── version_mgr - + ├── auth /* 认证相关 */ + ├── commet /* 评论处理 */ + ├── config_parser /* 配置解析 */ + ├── constant /* 常量定义 */ + ├── dependent_handle /* 依赖处理 */ + ├── error_handle /* 错误处理 */ + ├── gitee_api /* Gitee API交互 */ + │ ├── enterprise /* 企业API */ + │ ├── issue /* 问题API */ + │ ├── milestone /* 里程碑API */ + │ ├── organization /* 组织API */ + │ └── pr /* PR相关API */ + ├── parallel_handle /* 并行处理 */ + └── version_mgr /* 版本管理 */ ``` -## Tenon CI 支持功能 + +## 支持功能 ### 拉取依赖PR -在 PR 模板 “相关PR: ”选项中添加依赖 PR 的链接,门禁 CI 会拉取依赖 PR 的代码。 +在 PR 模板 "相关PR: "选项中添加依赖 PR 的链接,门禁 CI 会拉取依赖 PR 的代码。 PR 模板书写格式: ```sh @@ -116,13 +155,28 @@ commit 信息使用英文字母或字符,并遵循以下格式: commit 描述信息格式检查,常用仓库支持的module与PR标题检查相同。 -### 编译 -在编译流程中,门禁 CI 将依据配置文件,例如:jobs/tenon/config/compile,进行并行编译。预设的Kconfig配置文件涵盖多个典型场景,如实时和非实时场景等(基于抢占式调度器和协作式调度器)。 +### 编译流程 + +在编译流程中,门禁 CI 会根据 Jenkinsfile 定义的流程,按照以下步骤执行: + +1. 环境初始化: 准备构建环境和代码 +2. 依赖处理: 拉取和处理依赖PR +3. 并行编译: 根据配置文件(如jobs/tenon/config/compile)并行执行多种配置的编译 +4. 告警检查: 分析并报告编译过程中的警告信息 + +预设的Kconfig配置文件涵盖多个典型场景,支持实时和非实时场景等(基于抢占式调度器和协作式调度器)。 ### UT测试 -并行编译结束后,CI将基于生成的二进制文件,并行地进行单元测试。 + +并行编译结束后,CI将基于生成的二进制文件,执行以下测试流程: + +1. 测试准备: 设置测试环境和依赖 +2. 并行测试: 同时运行多个测试套件 +3. 结果收集: 汇总测试结果并生成报告 +4. 反馈提交: 将测试结果反馈到PR评论中 ### 触发器指令 + #### /skip 触发一次 CI 任务,并根据传入的参数跳过指定的阶段 (stage)。支持跳过的阶段及其对应的参数如下表所示: @@ -137,7 +191,7 @@ commit 描述信息格式检查,常用仓库支持的module与PR标题检查 ```sh /skip [stageParam1], [stageParam2], ... ``` -- stageParam:可选参数,可传多个,参数间用逗号分隔,具体参数见上表的 “参数名称” 一栏。 +- stageParam:可选参数,可传多个,参数间用逗号分隔,具体参数见上表的 "参数名称" 一栏。 - 示例: - /skip title, checkpatch @@ -145,4 +199,4 @@ commit 描述信息格式检查,常用仓库支持的module与PR标题检查 - UT 测试依赖前置步骤编译,禁止单独跳过 compile 而执行 ut 测试 #### /bench -执行一次基准性能测试 (benchmark)。 \ No newline at end of file +执行一次基准性能测试 (benchmark)。评论区输入 `/bench` 指令将触发性能测试流程,测试完成后会在PR评论中返回结果。 diff --git a/jobs/entry/.jenkinsfile b/jobs/entry/.jenkinsfile index d687094..bba8bcc 100644 --- a/jobs/entry/.jenkinsfile +++ b/jobs/entry/.jenkinsfile @@ -2,6 +2,7 @@ import groovy.transform.Field import java.util.Base64 @Field def env_path +@Field def json_path @Field def child_job pipeline { @@ -23,13 +24,13 @@ pipeline { } env_path = "${env.WORKSPACE}/../pass_env/${repo_name}.txt" + json_path = "${env.WORKSPACE}/../pass_env/${repo_name}_json.txt" } } } stage('保存gitee相关环境变量') { steps { script { - sh ( label: "gitee env", script: """ @@ -48,6 +49,17 @@ pipeline { """, returnStatus: true ) + sh ( + label: "gitee env", + script: """ + #!/bin/sh + set -e +x + echo 'ref=${env.ref}' >> ${env_path} + echo 'sha=${env.sha}' >> ${env_path} + echo 'noteBody=${env.noteBody}' >> ${env_path} + """, + returnStatus: true + ) } } } @@ -59,27 +71,11 @@ pipeline { script: """ #!/bin/sh set -e +x - echo 'jsonBody=${env.jsonBody}' >> ${env_path} - """, - returnStatus: true - ) - sh ( - label: "gitee env", - script: """ - #!/bin/sh - set -e +x - echo 'ref=${env.ref}' >> ${env_path} - echo 'sha=${env.sha}' >> ${env_path} - echo 'noteBody=${env.noteBody}' >> ${env_path} - - """, + printf "%s" '${env.jsonBody}' > "${json_path}" + """, returnStatus: true ) } - script { - def allEnvVars = sh(script: "env", returnStdout: true).trim() - println "系统环境变量:\n${allEnvVars}" - } } } stage('子任务分发') { diff --git a/jobs/lib/.jenkinsfile b/jobs/lib/.jenkinsfile index 8e1f10f..0a9bf0b 100644 --- a/jobs/lib/.jenkinsfile +++ b/jobs/lib/.jenkinsfile @@ -42,11 +42,25 @@ pipeline { steps { script { def env_path = "${env.WORKSPACE}/../pass_env/${params.repo_name}.txt" + def json_path = "${env.WORKSPACE}/../pass_env/${params.repo_name}_json.txt" def envVars = readFile("${env_path}").trim().split('\n') envVars.each { line -> - def (key, value) = line.tokenize('=') - env[key] = value.trim() + line = line.trim() + if (line.isEmpty() || !line.contains('=')) { + echo "跳过无效行: '${line}'" + return + } + def key = line.substring(0, line.indexOf('=')).trim() + def value = line.substring(line.indexOf('=') + 1).trim() + if (key.isEmpty()) { + echo "跳过无效键值对: '${line}'" + return + } + + env[key] = value ?: "" } + def jsonBody = readFile("${json_path}").trim() + env["jsonBody"] = jsonBody } } } diff --git a/jobs/tenon/.jenkinsfile b/jobs/tenon/.jenkinsfile index 59ba531..a7434fa 100644 --- a/jobs/tenon/.jenkinsfile +++ b/jobs/tenon/.jenkinsfile @@ -44,11 +44,25 @@ pipeline { steps { script { def env_path = "${env.WORKSPACE}/../pass_env/${params.repo_name}.txt" + def json_path = "${env.WORKSPACE}/../pass_env/${params.repo_name}_json.txt" def envVars = readFile("${env_path}").trim().split('\n') envVars.each { line -> - def (key, value) = line.tokenize('=') - env[key] = value.trim() + line = line.trim() + if (line.isEmpty() || !line.contains('=')) { + echo "跳过无效行: '${line}'" + return + } + def key = line.substring(0, line.indexOf('=')).trim() + def value = line.substring(line.indexOf('=') + 1).trim() + if (key.isEmpty()) { + echo "跳过无效键值对: '${line}'" + return + } + + env[key] = value ?: "" } + def jsonBody = readFile("${json_path}").trim() + env["jsonBody"] = jsonBody } } } -- Gitee From 3eadb91647a6cb97e27f775832ef37cb1c5d9578 Mon Sep 17 00:00:00 2001 From: sunhaoyi Date: Mon, 23 Dec 2024 16:00:33 +0800 Subject: [PATCH 7/8] ci/mortise: supports static checking and compile-and-run checking Signed-off-by: sunhaoyi --- jobs/common/misra_cppcheck/misra-c-2012.txt | 287 ++++++++++++++++++ jobs/common/misra_cppcheck/misra_cppcheck.sh | 53 ++++ jobs/entry/.jenkinsfile | 3 + jobs/mortise/.jenkinsfile | 287 ++++++++++++++++++ .../dependent_handle/compile_handle.groovy | 70 +++++ .../mortise/dependent_handle/pr_handle.groovy | 55 ++++ jobs/mortise/module.env | 2 +- 7 files changed, 756 insertions(+), 1 deletion(-) create mode 100755 jobs/common/misra_cppcheck/misra-c-2012.txt create mode 100755 jobs/common/misra_cppcheck/misra_cppcheck.sh create mode 100644 jobs/mortise/.jenkinsfile create mode 100644 jobs/mortise/dependent_handle/compile_handle.groovy create mode 100644 jobs/mortise/dependent_handle/pr_handle.groovy diff --git a/jobs/common/misra_cppcheck/misra-c-2012.txt b/jobs/common/misra_cppcheck/misra-c-2012.txt new file mode 100755 index 0000000..02ebf04 --- /dev/null +++ b/jobs/common/misra_cppcheck/misra-c-2012.txt @@ -0,0 +1,287 @@ +Appendix A Summary of guidelines +Rule 1.1 +The program shall contain no violations of the standard C syntax and constraints and shall not exceed the implementation's translation limits +Rule 1.2 +Language extensions should not be used +Rule 1.3 +There shall be no occurrence of undefined or critical unspecified behaviour +Rule 2.1 +A project shall not contain unreachable code +Rule 2.2 +There shall be no dead code +Rule 2.3 +A project should not contain unused type declarations +Rule 2.4 +A project should not contain unused tag declarations +Rule 2.5 +A project should not contain unused macro declarations +Rule 2.6 +A function should not contain unused label declarations +Rule 2.7 +There should be no unused parameters in functions +Rule 3.1 +The character sequences /* and // shall not be used within a comment +Rule 3.2 +Line-splicing shall not be used in // comments +Rule 4.1 +Octal and hexadecimal escape sequences shall be terminated +Rule 4.2 +Trigraphs should not be used +Rule 5.1 +External identifiers shall be distinct +Rule 5.2 +Identifiers declared in the same scope and name space shall be distinct +Rule 5.3 +An identifier declared in an inner scope shall not hide an identifier declared in an outer scope +Rule 5.4 +Macro identifiers shall be distinct +Rule 5.5 +Identifiers shall be distinct from macro names +Rule 5.6 +A typedef name shall be a unique identifier +Rule 5.7 +A tag name shall be a unique identifier +Rule 5.8 +Identifiers that define objects or functions with external linkage shall be unique +Rule 5.9 +Identifiers that define objects or functions with internal linkage should be unique +Rule 6.1 +Bit-fields shall only be declared with an appropriate type +Rule 6.2 +Single-bit named bit fields shall not be of a signed type +Rule 7.1 +Octal constants shall not be used +Rule 7.2 +A u or U suffix shall be applied to all integer constants that are represented in an unsigned type +Rule 7.3 +The lowercase character l shall not be used in a literal suffix +Rule 7.4 +A string literal shall not be assigned to an object unless the object's type is pointer to const-qualified char +Rule 8.1 +Types shall be explicitly specified +Rule 8.2 +Function types shall be in prototype form with named parameters +Rule 8.3 +All declarations of an object or function shall use the same names and type qualifiers +Rule 8.4 +A compatible declaration shall be visible when an object or function with external linkage is defined +Rule 8.5 +An external object or function shall be declared once in one and only one file +Rule 8.6 +An identifier with external linkage shall have exactly one external definition +Rule 8.7 +Functions and objects should not be defined with external linkage if they are referenced in only one translation unit +Rule 8.8 +The static storage class specifier shall be used in all declarations of objects and functions that have internal linkage +Rule 8.9 +An object should be defined at block scope if its identifier only appears in a single function +Rule 8.10 +An inline function shall be declared with the static storage class +Rule 8.11 +When an array with external linkage is declared its size should be explicitly specified +Rule 8.12 +Within an enumerator list the value of an implicitly-specified enumeration constant shall be unique +Rule 8.13 +A pointer should point to a const-qualified type whenever possible +Rule 8.14 +The restrict type qualifier shall not be used +Rule 9.1 +The value of an object with automatic storage duration shall not be read before it has been set +Rule 9.2 +The initializer for an aggregate or union shall be enclosed in braces +Rule 9.3 +Arrays shall not be partially initialized +Rule 9.4 +An element of an object shall not be initialized more than once +Rule 9.5 +Where designated initializers are used to initialize an array object the size of the array shall be specified explicitly +Rule 10.1 +Operands shall not be of an inappropriate essential type +Rule 10.2 +Expressions of essentially character type shall not be used inappropriately in addition and subtraction operations +Rule 10.3 +The value of an expression shall not be assigned to an object with anarrower essential type or of a different essential type category +Rule 10.4 +Both operands of an operator in which the usual arithmetic conversions are performed shall have the same essential type category +Rule 10.5 +The value of an expression should not be cast to an inappropriate essential type +Rule 10.6 +The value of a composite expression shall not be assigned to an objectwith wider essential type +Rule 10.7 +If a composite expression is used as one operand of an operator in which the usual arithmetic conversions are performed then the other operand shall not have wider essential type +Rule 10.8 +The value of a composite expression shall not be cast to a different essential type category or a wider essential type +Rule 11.1 +Conversions shall not be performed between a pointer to a function and any other type +Rule 11.2 +Conversions shall not be performed between a pointer to an incomplete type and any other type +Rule 11.3 +A cast shall not be performed between a pointer to object type and a pointer to a different object type +Rule 11.4 +A conversion should not be performed between a pointer to object and an integer type +Rule 11.5 +A conversion should not be performed from pointer to void into pointer to object +Rule 11.6 +A cast shall not be performed between pointer to void and an arithmetic type +Rule 11.7 +A cast shall not be performed between pointer to object and a non-integer arithmetic type +Rule 11.8 +A cast shall not remove any const or volatile qualification from the type pointed to by a pointer +Rule 11.9 +The macro NULL shall be the only permitted form of integer null pointer constant +Rule 12.1 +The precedence of operators within expressions should be made explicit +Rule 12.2 +The right hand operand of a shift operator shall lie in the range zero to one less than the width in bits of the essential type of the left hand operand +Rule 12.3 +The comma operator should not be used +Rule 12.4 +Evaluation of constant expressions should not lead to unsigned integer wrap-around +Rule 13.1 +Initializer lists shall not contain persistent side effects +Rule 13.2 +The value of an expression and its persistent side effects shall be the same under all permitted evaluation orders +Rule 13.3 +A full expression containing an increment (++) or decrement (--) operator should have no other potential side effects other than that caused by the increment or decrement operator +Rule 13.4 +The result of an assignment operator should not be used +Rule 13.5 +The right hand operand of a logical && or || operator shall not contain persistent side effects +Rule 13.6 +The operand of the sizeof operator shall not contain any expression which has potential side effects +Rule 14.1 +A loop counter shall not have essentially floating type +Rule 14.2 +A for loop shall be well-formed +Rule 14.3 +Controlling expressions shall not be invariant +Rule 14.4 +The controlling expression of an if statement and the controlling expression of an iteration-statement shall have essentially Boolean type +Rule 15.1 +The goto statement should not be used +Rule 15.2 +The goto statement shall jump to a label declared later in the same function +Rule 15.3 +Any label referenced by a goto statement shall be declared in the same block or in any block enclosing the goto statement +Rule 15.4 +There should be no more than one break or goto statement used to terminate any iteration statement +Rule 15.5 +A function should have a single point of exit at the end +Rule 15.6 +The body of an iteration-statement or a selection-statement shall be a compound-statement +Rule 15.7 +All if ... else if constructs shall be terminated with an else statement +Rule 16.1 +All switch statements shall be well-formed +Rule 16.2 +A switch label shall only be used when the most closely-enclosing compound statement is the body of a switch statement +Rule 16.3 +An unconditional break statement shall terminate every switch-clause +Rule 16.4 +Every switch statement shall have a default label +Rule 16.5 +A default label shall appear as either the first or the last switch label of a switch statement +Rule 16.6 +Every switch statement shall have at least two switch-clauses +Rule 16.7 +A switch-expression shall not have essentially Boolean type +Rule 17.1 +The features of shall not be used +Rule 17.2 +Functions shall not call themselves either directly or indirectly +Rule 17.3 +A function shall not be declared implicitly +Rule 17.4 +All exit paths from a function with non-void return type shall have an explicit return statement with an expression +Rule 17.5 +The function argument corresponding to a parameter declared to have an array type shall have an appropriate number of elements +Rule 17.6 +The declaration of an array parameter shall not contain the static keyword between the [ ] +Rule 17.7 +The value returned by a function having non-void return type shall be used +Rule 17.8 +A function parameter should not be modified +Rule 18.1 +A pointer resulting from arithmetic on a pointer operand shall address an element of the same array as that pointer operand +Rule 18.2 +Subtraction between pointers shall only be applied to pointers that address elements of the same array +Rule 18.3 +The relational operators > >= < and <= shall not be applied to objects of pointer type except where they point into the same object +Rule 18.4 +The + - += and -= operators should not be applied to an expression of pointer type +Rule 18.5 +Declarations should contain no more than two levels of pointer nesting +Rule 18.6 +The address of an object with automatic storage shall not be copied to another object that persists after the first object has ceased to exist +Rule 18.7 +Flexible array members shall not be declared +Rule 18.8 +Variable-length array types shall not be used +Rule 19.1 +An object shall not be assigned or copied to an overlapping object +Rule 19.2 +The union keyword should not be used +Rule 20.1 +#include directives should only be preceded by preprocessor directives or comments +Rule 20.2 +The ' or \ characters and the /* or // character sequences shall not occur in a header file name +Rule 20.3 +The #include directive shall be followed by either a or filename sequence +Rule 20.4 +A macro shall not be defined with the same name as a keyword +Rule 20.5 +#undef should not be used +Rule 20.6 +Tokens that look like a preprocessing directive shall not occur within amacro argument +Rule 20.7 +Expressions resulting from the expansion of macro parameters shall be enclosed in parentheses +Rule 20.8 +The controlling expression of a #if or #elif preprocessing directive shall evaluate to 0 or 1 +Rule 20.9 +All identifiers used in the controlling expression of #if or #elif preprocessing directives shall be #define'd before evaluation +Rule 20.10 +The # and ## preprocessor operators should not be used +Rule 20.11 +A macro parameter immediately following a # operator shall not immediately be followed by a ## operator +Rule 20.12 +A macro parameter used as an operand to the # or ## operators which is itself subject to further macro replacement shall only be used as an operand to these operators +Rule 20.13 +A line whose first token is # shall be a valid preprocessing directive +Rule 20.14 +All #else #elif and #endif preprocessor directives shall reside in the same file as the #if #ifdef or #ifndef directive to which they are related +Rule 21.1 +#define and #undef shall not be used on a reserved identifier or reserved macro name +Rule 21.2 +A reserved identifier or macro name shall not be declared +Rule 21.3 +The memory allocation and deallocation functions of shall not be used +Rule 21.4 +The standard header file shall not be used +Rule 21.5 +The standard header file shall not be used +Rule 21.6 +The Standard Library input/output functions shall not be used +Rule 21.7 +The atof atoi atol and atoll functions of shall not be used +Rule 21.8 +The library functions abort exit getenv and system of shall not be used +Rule 21.9 +The library functions bsearch and qsort of shall not be used +Rule 21.10 +The Standard Library time and date functions shall not be used +Rule 21.11 +The standard header file shall not be used +Rule 21.12 +The exception handling features of should not be used +Rule 22.1 +All resources obtained dynamically by means of Standard Library functions shall be explicitly released +Rule 22.2 +A block of memory shall only be freed if it was allocated by means of a Standard Library function +Rule 22.3 +The same file shall not be open for read and write access at the same time on different streams +Rule 22.4 +There shall be no attempt to write to a stream which has been opened as read-only +Rule 22.5 +A pointer to a FILE object shall not be dereferenced +Rule 22.6 +The value of a pointer to a FILE shall not be used after the associated stream has been closed diff --git a/jobs/common/misra_cppcheck/misra_cppcheck.sh b/jobs/common/misra_cppcheck/misra_cppcheck.sh new file mode 100755 index 0000000..e67b06f --- /dev/null +++ b/jobs/common/misra_cppcheck/misra_cppcheck.sh @@ -0,0 +1,53 @@ +#!/bin/bash +# +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2024 The TenonOS Authors +# + +# 获取misra tool的工作目录 +misra_tool_path=$(dirname "$(realpath "$0")") + +# 获取需检查格式的文件路径(只检查c文件),路径之间以空格分隔 +if [ "$1" -eq 0 ]; then + # 获取当前目录下所有 .c 和 .h 文件的路径 + files=$(find . -type f \( -iname "*.c" -o -iname "*.h" \) -print | tr '\n' ' ') +else + files=$(git diff --name-only HEAD~$1 HEAD | grep -E '\.c$|\.h$') + misrac_check_count=$(echo "$files" | tr ' ' '\n' | wc -l) +fi + +#制作cppcheck的addon功能的json文件 +misra_addon='{ + "script": "misra.py", + "args": [ + "--rule-texts=${MISRA_RULE_TXT_PATH}" + ] +}' +misra_json_path=$misra_tool_path/misra_addon.json +misra_rule_txt_path=$misra_tool_path/misra-c-2012.txt +misra_check_log=$misra_tool_path/misra_check.log + +echo $misra_addon >$misra_json_path +touch $misra_check_log + +sed -i "s|\${MISRA_RULE_TXT_PATH}|"$misra_rule_txt_path"|" "$misra_json_path" + +# 执行misra标准的检查 +if [ -n "$files" ]; then + echo "Perform the misra coding standard check, target files : $misrac_check_count" + cppcheck --quiet --addon=$misra_json_path $files >$misra_check_log 2>&1 + + if [ -s "$misra_check_log" ]; then + echo "FAILED: Misra coding standards check failed:" + cat $misra_check_log + ret=1 + else + echo "SUCCESS: The misra coding standards check was passed." + ret=0 + fi +else + echo "There are no documents that need to be checked against the misra coding standard." +fi +rm $misra_json_path +rm $misra_check_log +exit $ret diff --git a/jobs/entry/.jenkinsfile b/jobs/entry/.jenkinsfile index bba8bcc..d5134f9 100644 --- a/jobs/entry/.jenkinsfile +++ b/jobs/entry/.jenkinsfile @@ -19,6 +19,9 @@ pipeline { } else if (repo_name.startsWith("Tenon")){ echo "目标微库为lib微库, 即将调用lib微库任务" child_job = "CI_Tenon" + } else if (repo_name.startsWith("Mortise")){ + echo "目标微库为Moritse, 即将调用Mortise" + child_job = "CI_Mortise" } else { error "未知目标微库:${repo_name}!请确认CI是否支持该类型微库!" } diff --git a/jobs/mortise/.jenkinsfile b/jobs/mortise/.jenkinsfile new file mode 100644 index 0000000..9fa4c3d --- /dev/null +++ b/jobs/mortise/.jenkinsfile @@ -0,0 +1,287 @@ +import groovy.transform.Field + +@Field int nBuildThreads = 1 +@Field def constant +@Field def module +@Field def tolNum = 2 +@Field def exeNum = 0 +@Field def skipNum = 0 +@Field def sucNum = 0 +@Field def failNum = 0 +@Field def stageStatus = [:] +@Field def prComment = "" +@Field def userParam = [] + +def pr_handler +def checker + +pipeline { + agent any + + parameters { + string(name: 'repo_name', defaultValue: '', description: '目标仓库名') + } + + environment { + CI_LOG_URL = "[日志信息](${env.BUILD_URL}console)" + BENCH_COMMAND = "/bench" + SKIP_COMMAND = "/skip" + } + + options { + ansiColor('xterm') + } + + stages { + stage("环境变量预处理") { + steps { + script { + def env_path = "${env.WORKSPACE}/../pass_env/${params.repo_name}.txt" + def json_path = "${env.WORKSPACE}/../pass_env/${params.repo_name}_json.txt" + def envVars = readFile("${env_path}").trim().split('\n') + envVars.each { line -> + line = line.trim() + if (line.isEmpty() || !line.contains('=')) { + echo "跳过无效行: '${line}'" + return + } + def key = line.substring(0, line.indexOf('=')).trim() + def value = line.substring(line.indexOf('=') + 1).trim() + if (key.isEmpty()) { + echo "跳过无效键值对: '${line}'" + return + } + + env[key] = value ?: "" + } + def jsonBody = readFile("${json_path}").trim() + env["jsonBody"] = jsonBody + } + } + } + stage("初始化变量") { + steps { + script { + constant = load("${env.WORKSPACE}/utils/constant/path.groovy") + pr_handler = load("${env.WORKSPACE}/jobs/mortise/dependent_handle/pr_handle.groovy") + compile_handler = load("${env.WORKSPACE}/jobs/mortise/dependent_handle/compile_handle.groovy") + checker = load(constant.TITLE_CHECKER_DIR_PATH + "checkout.groovy") + commet_by_curl = load(constant.ADDGITEECOMMEMTBYCURL) + } + + script { + module = sh ( + script: """ + #!/bin/sh + set -e +x + . ${env.WORKSPACE}/jobs/mortise/module.env + echo \$REPO_MODULE + """, + returnStdout: true + ).trim() + } + + } + } + + stage('初始化任务') { + steps { + script { + def r = "开始CI测试,任务编号:${env.BUILD_NUMBER} \n" + + "任务链接:${env.BUILD_URL}pipeline-console/" + + commet_by_curl.addGiteeCommentByCurl(r) + // 获取 CPU 核心数 + cpuCores = sh(script: 'nproc', returnStdout: true).trim() + echo "Detected CPU Cores: ${cpuCores}" + nBuildThreads = Integer.parseInt(cpuCores) / 2 + } + } + } + + stage("仓库初始化") { + steps { + script { + sh ( + label: "Get mortise source code", + script: """ + #!/bin/sh + set -e +x + cd ${env.WORKSPACE} + git clone git@gitee.com:tenonos/mortise.git + cd mortise + make update_depends + """, + returnStatus: true + ) + def pr_data = [ + [ + repo_owner: "tenonos", + repo_name: "mortise", + branch_name: "master", + pr_number: "${env.giteePullRequestIid}" + ] + ] + + pr_handler.GetDependedPRInfo(pr_data) + pr_handler.MergePR(pr_data) + } + } + } + + stage("评论解析") { + when { + expression { + env.giteeActionType == "NOTE" && + env.giteeTriggerPhrase && + env.giteeTriggerPhrase.startsWith(env.SKIP_COMMAND) + } + } + steps { + script { + prComment = env.giteeTriggerPhrase.trim() + def userParamString = "" + if (prComment.startsWith(env.SKIP_COMMAND)) { + userParamString = prComment.minus(env.SKIP_COMMAND).trim() + } + userParam = userParamString.split(/\s*,\s*/) + } + } + } + + stage("PR标题合法性检查") { + when { + expression { + def skip_flag = userParam.contains("title") + if (skip_flag) { + skipNum += 1 + } + return !skip_flag + } + } + steps { + script { + exeNum += 1 + def res = checker.checkTitle(env.giteePullRequestTitle, module) + + if (!res) { + stageStatus[env.STAGE_NAME] = [status: 1, remark: ["标题要求可见:[PR标题合法性检查](https://gitee.com/yingyisunhaoyi/ci/)"]] + error "check title failed ${module}" + } + + stageStatus[env.STAGE_NAME] = [status: 0, remark: ""] + + } + } + } + + stage("Commit静态检查") { + when { + expression { + def skip_flag = userParam.contains("checkpatch") + if (skip_flag) { + skipNum += 1 + } + return !skip_flag + } + } + steps { + script { + exeNum += 1 + def env_jsonbody = readJSON text: env.jsonBody + def commit_num = env_jsonbody.pull_request.commits + def checkpatch_res = sh ( + label: "checkpatch", + script: """ + #!/bin/bash + set -e +x + cd mortise + . ${env.WORKSPACE}/jobs/tenon/module.env + git format-patch HEAD~${commit_num}..HEAD + ${env.WORKSPACE}/jobs/common/commit_check/checkpatch.uk ./00* + """, + returnStatus: true + ) + + if (checkpatch_res) { + stageStatus[env.STAGE_NAME] = [status: 1, remark: "${CI_LOG_URL}"] + error "Commit静态检测失败" + } + stageStatus[env.STAGE_NAME] = [status: 0, remark: ["检查DCO、Commit标题格式、代码规范性等"]] + } + } + } + stage("Misra静态检查") { + when { + expression { + return 0 + } + } + steps { + script { + exeNum += 1 + def env_jsonbody = readJSON text: env.jsonBody + def commit_num = env_jsonbody.pull_request.commits + def cppcheck_res = sh ( + label: "cppcheck", + script: """ + #!/bin/bash + set -e +x + cd mortise + ${env.WORKSPACE}/jobs/common/misra_cppcheck/misra_cppcheck.sh ${commit_num} + """, + returnStatus: true + ) + + if (cppcheck_res) { + stageStatus[env.STAGE_NAME] = [status: 1, remark: "${CI_LOG_URL}"] + error "Misra静态检查失败" + } + stageStatus[env.STAGE_NAME] = [status: 0, remark: ["检查代码MISRA合规性"]] + } + } + } + stage("编译与运行检查") { + when { + expression { + def skip_flag = userParam.contains("compile") + if (skip_flag) { + skipNum += 1 + } + return !skip_flag + } + } + steps { + script { + exeNum += 1 + compile_handler.compile_check("qemu-aarch64-virt","tenonos",stageStatus) + compile_handler.compile_check("qemu-aarch64-virt","linux",stageStatus) + stageStatus[env.STAGE_NAME] = [status: 0, remark: ["编译与运行测试成功"]] + } + } + } + + } + + post { + always { + script { + def ret = "|任务名|测试状态|备注|\n|-|-|-|\n" + stageStatus.each { stageName, stageInfo -> + if (stageInfo.status) { + failNum += 1 + } else { + sucNum += 1 + } + def statusText = stageInfo.status == 0 ? "成功" : "**失败**" + def remarkText = stageInfo.remark.join("
") + ret += "|${stageName}|${statusText}|${remarkText}|\n" + } + def comments = "结束CI测试,计划测试" + "**$tolNum**" + "项-->已经测试" + "**$exeNum**" + "项:成功" + "**$sucNum**" + "项,失败" + "**$failNum**" + "项,跳过" + "**$skipNum**" + "项"+ + "\n" + ret + + "\n\n任务链接: ${env.BUILD_URL}pipeline-console/ " + commet_by_curl.addGiteeCommentByCurl(comments) + } + } + } +} diff --git a/jobs/mortise/dependent_handle/compile_handle.groovy b/jobs/mortise/dependent_handle/compile_handle.groovy new file mode 100644 index 0000000..a902366 --- /dev/null +++ b/jobs/mortise/dependent_handle/compile_handle.groovy @@ -0,0 +1,70 @@ +def compile_check(plat_name,demo,global_stageStatus) { + + def log_dir = "${env.WORKSPACE}/mortise/workdir/log/${plat_name}_${demo}" + def tool_dir = "${env.WORKSPACE}/mortise/workdir/tools" + def build_log = "${log_dir}/build.log" + def run_log = "${log_dir}/run.log" + def compile_res = sh ( + label: "compile", + script: """ + #!/bin/bash + set -e +x + if [ ! -d "${tool_dir}" ]; then + mkdir -p ${tool_dir} + ln -s ${env.WORKSPACE}/../tool/gcc-arm-10.3-2021.07-x86_64-aarch64-none-elf \ + ${tool_dir} + fi + mkdir -p ${log_dir} + cd ${env.WORKSPACE}/mortise/examples + + if [ ${demo} = "linux" ]; then + make PLATFORM=${plat_name} ARCH=aarch64 DEMO=${demo} \ + LINUX_WORKDIR_PATH=${env.WORKSPACE}/../linux_workdir \ + > ${build_log} 2>&1 + fi + + if [ ${demo} = "tenonos" ]; then + make PLATFORM=${plat_name} ARCH=aarch64 DEMO=${demo} > ${build_log} 2>&1 + fi + + """, + returnStatus: true + ) + if (compile_res != 0) { + global_stageStatus[env.STAGE_NAME] = [status: 1, remark: ["编译失败 PLATFORM=${plat_name} DEMO=${demo} "]] + error "编译失败: PLATFORM=${plat_name} DEMO=${demo}" + } + def run_res = sh ( + label: "run", + script: """ + #!/bin/bash + set +x + cd ${env.WORKSPACE}/mortise/examples + make run PLATFORM=${plat_name} DEMO=${demo} ARCH=aarch64 >> ${run_log} 2>&1 & + finish_log=0 + + if [ ${demo} = "tenonos" ]; then + sleep 5s + finish_log=\$(grep -a "Hello, World!" ${run_log} | wc -l) + fi + + if [ ${demo} = "linux" ]; then + sleep 30s + finish_log=\$(grep -a "buildroot login:" ${run_log} | wc -l) + fi + + if [ "\$finish_log" -eq 0 ]; then + exit 1 + else + exit 0 + fi + """, + returnStatus: true + ) + if (run_res != 0) { + global_stageStatus[env.STAGE_NAME] = [status: 1, remark: ["运行测试失败 PLATFORM=${plat_name} DEMO=${demo} "]] + error "运行测试失败: PLATFORM=${plat_name} DEMO=${demo}" + } +} + +return this diff --git a/jobs/mortise/dependent_handle/pr_handle.groovy b/jobs/mortise/dependent_handle/pr_handle.groovy new file mode 100644 index 0000000..c2cc513 --- /dev/null +++ b/jobs/mortise/dependent_handle/pr_handle.groovy @@ -0,0 +1,55 @@ +@NonCPS +def GetDependedPRInfo(List> pr_data) { + def prDesc = env.giteePullRequestDescription + /* 检测并获取依赖PR的信息 */ + if (!prDesc) { + return + } + if (prDesc.contains("相关PR:")) { + prDesc = prDesc.substring(prDesc.indexOf("相关PR:") + 5) + print(prDesc) + /* 获取相关PR的链接 */ + pattern = /\[(?:.*(?:\[.+\])*.*)\]\(.+\/([^\/]+)\/([^\/]+)\/pulls\/(\d+)\)/ + def matcher = (prDesc =~ pattern) + def prinfo = "" + while(matcher.find()){ + /* 分离链接中的信息 */ + prinfo = [ + repo_owner: matcher.group(1), + repo_name: matcher.group(2), + pr_number: matcher.group(3) + ] + pr_data.add(prinfo) + } + } +} + +def MergePR(List> pr_data){ + pr_data.each { pr -> + def res = sh ( + label: "merge PR", + script: """ + #!/bin/bash + set -e +x + + cd mortise + cd \$(find . -maxdepth 3 -type d -name ${pr.repo_name} -print | xargs realpath) + + is_shallow=\$(git rev-parse --is-shallow-repository) + if [ "\$is_shallow" = "true" ]; then + git fetch --unshallow + fi + git fetch git@gitee.com:${pr.repo_owner}/${pr.repo_name}.git pull/${pr.pr_number}/head:pr_${pr.pr_number} + git checkout pr_${pr.pr_number} + git rebase ${pr.branch_name} + """, + returnStatus: true + ) + if(res != 0) { + addGiteeMRComment comment: "${pr.repo_name} rebase 失败" + error "rebase failed, repo: ${pr.repo_name}" + } + } +} + +return this diff --git a/jobs/mortise/module.env b/jobs/mortise/module.env index 931ed5b..0132eb5 100644 --- a/jobs/mortise/module.env +++ b/jobs/mortise/module.env @@ -1 +1 @@ -export REPO_MODULE=(arch|lib|driver|doc|example|build) \ No newline at end of file +export REPO_MODULE="(arch|doc|driver|example|lib|platform|build|ci)" \ No newline at end of file -- Gitee From 672837b36161c597e6b4278e1ea0cb0592dfe6d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AD=99=E6=98=8A=E4=B8=80?= Date: Thu, 26 Feb 2026 10:15:49 +0800 Subject: [PATCH 8/8] ci: clean up remnants from the previous build MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 孙昊一 --- jobs/mortise/.jenkinsfile | 11 +++++++++++ jobs/tenon/.jenkinsfile | 11 +++++++++++ utils/error_handle/error_check.groovy | 2 +- 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/jobs/mortise/.jenkinsfile b/jobs/mortise/.jenkinsfile index 9fa4c3d..ea6ea02 100644 --- a/jobs/mortise/.jenkinsfile +++ b/jobs/mortise/.jenkinsfile @@ -102,6 +102,17 @@ pipeline { stage("仓库初始化") { steps { script { + sh ( + label: "Clean workspace", + script: """ + #!/bin/sh + set -e +x + cd ${env.WORKSPACE} + # 清理上次构建残留的目录 + rm -rf mortise + """, + returnStatus: true + ) sh ( label: "Get mortise source code", script: """ diff --git a/jobs/tenon/.jenkinsfile b/jobs/tenon/.jenkinsfile index a7434fa..38c56ac 100644 --- a/jobs/tenon/.jenkinsfile +++ b/jobs/tenon/.jenkinsfile @@ -115,6 +115,17 @@ pipeline { stage("仓库初始化") { steps { script { + sh ( + label: "Clean workspace", + script: """ + #!/bin/sh + set -e +x + cd ${env.WORKSPACE} + # 清理上次构建残留的目录 + rm -rf workdir + """, + returnStatus: true + ) def repo_config_name = "lib_config_Tenon.json" def repo_config_path = "${env.WORKSPACE}/jobs/tenon/config/repos/${repo_config_name}" def repoBranchMap = [:] diff --git a/utils/error_handle/error_check.groovy b/utils/error_handle/error_check.groovy index 0d6d50e..fc46e8d 100644 --- a/utils/error_handle/error_check.groovy +++ b/utils/error_handle/error_check.groovy @@ -19,7 +19,7 @@ def failure_record(global_stageStatus, stage_failure, log_Path = null) { #!/bin/bash set -e +x logName=\$(basename ${log_Path}) - echo "${logName} of ${stage_failure.config_name}" + echo "\${logName} of ${stage_failure.config_name}" cat ${log_Path} """, returnStatus: true -- Gitee