diff --git a/.github/workflows/Main.yml b/.github/workflows/Main.yml new file mode 100644 index 000000000..9b66e8fd7 --- /dev/null +++ b/.github/workflows/Main.yml @@ -0,0 +1,138 @@ +name: CI/CD Workflow + +on: + pull_request_target: + branches: + - '*' + types: [labeled] + release: + types: [published] + +jobs: + check: + if: github.event.label.name == 'ci ready' || github.event_name == 'release' + uses: ./.github/workflows/cppcheck.yml + with: + image_name: registry.agibot.com/agibot-tech/gitlab-ci + image_tag: latest + secrets: inherit + + test_gcc11_amd64: + uses: ./.github/workflows/test-workflow.yml + with: + image_name: registry.agibot.com/agibot-tech/aimrt_ci-amd64-gcc11-ubuntu22_04 + image_tag: latest + run_platform: amd64 + test_report: true + secrets: inherit + needs: [check] + + test_gcc11_arm64: + uses: ./.github/workflows/test-workflow.yml + with: + image_name: registry.agibot.com/agibot-tech/aimrt_ci-arm64-gcc11-ubuntu22_04 + image_tag: latest + run_platform: arm64 + secrets: inherit + needs: [test_gcc11_amd64] + + test_msvc: + uses: ./.github/workflows/test-msvc-workflow.yml + with: + run_platform: msvc + secrets: inherit + needs: [test_gcc11_amd64] + + test_gcc12_amd64: + uses: ./.github/workflows/test-workflow.yml + with: + image_name: registry.agibot.com/agibot-tech/aimrt_ci-amd64-gcc12-ubuntu22_04 + image_tag: latest + run_platform: amd64 + secrets: inherit + needs: [test_gcc11_amd64] + + test_gcc13_amd64: + uses: ./.github/workflows/test-workflow.yml + with: + image_name: registry.agibot.com/agibot-tech/aimrt_ci-amd64-gcc13-ubuntu22_04 + image_tag: latest + run_platform: amd64 + secrets: inherit + needs: [test_gcc11_amd64] + + test_clang15_amd64: + uses: ./.github/workflows/test-workflow.yml + with: + image_name: registry.agibot.com/agibot-tech/aimrt_ci-amd64-clang15-ubuntu22_04 + image_tag: latest + run_platform: amd64 + secrets: inherit + needs: [test_gcc11_amd64] + + test_clang16_amd64: + uses: ./.github/workflows/test-workflow.yml + with: + image_name: registry.agibot.com/agibot-tech/aimrt_ci-amd64-clang16-ubuntu22_04 + image_tag: latest + run_platform: amd64 + secrets: inherit + needs: [test_gcc11_amd64] + + test_clang17_amd64: + uses: ./.github/workflows/test-workflow.yml + with: + image_name: registry.agibot.com/agibot-tech/aimrt_ci-amd64-clang17-ubuntu22_04 + image_tag: latest + run_platform: amd64 + secrets: inherit + needs: [test_gcc11_amd64] + + test_clang18_amd64: + uses: ./.github/workflows/test-workflow.yml + with: + image_name: registry.agibot.com/agibot-tech/aimrt_ci-amd64-clang18-ubuntu22_04 + image_tag: latest + run_platform: amd64 + secrets: inherit + needs: [test_gcc11_amd64] + + + build_x86: + uses: ./.github/workflows/build-workflow.yml + with: + image_name: registry.agibot.com/agibot-tech/aimrt_ci-amd64-gcc11-ubuntu22_04 + image_tag: v20240927 + run_platform: amd64 + secrets: inherit + needs: [test_msvc, test_gcc11_arm64, test_gcc12_amd64, test_gcc13_amd64, test_clang15_amd64, test_clang16_amd64, test_clang17_amd64, test_clang18_amd64] + + build_arm64: + uses: ./.github/workflows/build-workflow.yml + with: + image_name: registry.agibot.com/agibot-tech/aimrt_ci-arm64-gcc11-ubuntu22_04 + image_tag: latest + run_platform: arm64 + secrets: inherit + needs: [test_msvc, test_gcc11_arm64, test_gcc12_amd64, test_gcc13_amd64, test_clang15_amd64, test_clang16_amd64, test_clang17_amd64, test_clang18_amd64] + + build_msvc: + uses: ./.github/workflows/build-msvc-workflow.yml + with: + run_platform: msvc + secrets: inherit + needs: [test_msvc, test_gcc11_arm64, test_gcc12_amd64, test_gcc13_amd64, test_clang15_amd64, test_clang16_amd64, test_clang17_amd64, test_clang18_amd64] + + + release: + uses: ./.github/workflows/release-workflow.yml + with: + image_name: registry.agibot.com/agibot-tech/gitlab-ci + image_tag: latest + secrets: inherit + if: | + github.event_name == 'release' || + startsWith(github.ref, 'refs/tags/release') || + startsWith(github.ref, 'refs/tags/v') || + startsWith(github.ref, 'refs/heads/release') + needs: [build_x86, build_arm64, build_msvc] diff --git a/.github/workflows/build-msvc-workflow.yml b/.github/workflows/build-msvc-workflow.yml new file mode 100644 index 000000000..26750d5c5 --- /dev/null +++ b/.github/workflows/build-msvc-workflow.yml @@ -0,0 +1,79 @@ +name: build Workflow + +on: + workflow_call: + inputs: + run_platform: + required: false + type: string + default: msvc + secrets: + BUILD_CMD: + required: true + +jobs: + build: + runs-on: ${{ inputs.run_platform }} + steps: + - name: Checkout code + uses: actions/checkout@v4 + env: + https_proxy: ${{ secrets.https_proxy }} + http_proxy: ${{ secrets.http_proxy }} + with: + ref: ${{ github.event.pull_request.head.sha }} + + - name: Setup Visual Studio Developer Command Prompt + uses: microsoft/setup-msbuild@v1.0.2 + + - name: Install Chocolatey + run: | + Set-ExecutionPolicy Bypass -Scope Process -Force + [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072 + iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1')) + if (Test-Path "C:\ProgramData\chocolatey\bin\choco.exe") { + Write-Host "Chocolatey is installed." + } else { + Write-Host "Chocolatey installation failed." + } + $env:Path += ";C:\ProgramData\chocolatey\bin" + [Environment]::SetEnvironmentVariable("Path", $env:Path, "Machine") + env: + https_proxy: ${{ secrets.https_proxy }} + http_proxy: ${{ secrets.http_proxy }} + + - name: Install Python using Chocolatey + run: | + $chocoPath = "C:\ProgramData\chocolatey" + $env:Path += ";$chocoPath\bin" + [Environment]::SetEnvironmentVariable("Path", $env:Path, "Machine") + + # 使用完整路径执行 choco 命令 + & "$chocoPath\bin\choco.exe" install python --version=3.11.0 -y + + env: + https_proxy: ${{ secrets.https_proxy }} + http_proxy: ${{ secrets.http_proxy }} + + + - name: Run build stage + shell: powershell + run: | + python --version + ${{ secrets.BUILD_CMD_MSVC}} + if ($LASTEXITCODE -eq 1) { + Write-Host "build failed" + exit 1 + } else { + Write-Host "build succeeded" + } + + - name: upload build artifact + if: github.action + uses: actions/upload-artifact@v3 + with: + name: build_artifact + path: | + ${{vars.WHL_ARTIFACTS_DIR}} + retention-days: 3 + diff --git a/.github/workflows/build-workflow.yml b/.github/workflows/build-workflow.yml new file mode 100644 index 000000000..0844d7070 --- /dev/null +++ b/.github/workflows/build-workflow.yml @@ -0,0 +1,68 @@ +name: build Workflow + +on: + workflow_call: + inputs: + image_name: + required: true + type: string + default: ubuntu + image_tag: + required: false + type: string + default: latest + run_platform: + required: false + type: string + default: amd64 + secrets: + TEST_CMD: + required: true + +jobs: + build: + runs-on: ${{ inputs.run_platform }} + container: + image: ${{ inputs.image_name }}:${{ inputs.image_tag }} + steps: + + - name: Checkout code + uses: actions/checkout@v4 + env: + https_proxy: ${{ secrets.https_proxy }} + http_proxy: ${{ secrets.http_proxy }} + with: + ref: ${{ github.event.pull_request.head.sha }} + + - name: Run build stage + env: + https_proxy: "" + http_proxy: "" + no_proxy: "*" + shell: bash + run: | + if [ -d "./build" ]; then + ls -lah ./build + fi + + echo "removing CMakeCache.txt" + rm -rf ./build/CMakeCache.txt + + source /opt/ros/humble/setup.bash + eval "${{ secrets.BUILD_CMD}}" + + echo "ls -lah ${{vars.PROJECT_ARTIFACTS_DIR}}" + ls -lah ${{vars.PROJECT_ARTIFACTS_DIR}} + + echo "ls -lah ${{vars.WHL_ARTIFACTS_DIR}}" + ls -lah ${{vars.WHL_ARTIFACTS_DIR}} + + - name: upload build artifact + if: github.action + uses: actions/upload-artifact@v3 + with: + name: build_artifact + path: | + ${{vars.WHL_ARTIFACTS_DIR}} + retention-days: 3 + \ No newline at end of file diff --git a/.github/workflows/cppcheck.yml b/.github/workflows/cppcheck.yml new file mode 100644 index 000000000..427199a68 --- /dev/null +++ b/.github/workflows/cppcheck.yml @@ -0,0 +1,246 @@ +name: check Workflow + +on: + workflow_call: + inputs: + image_name: + required: true + type: string + default: ubuntu + image_tag: + required: false + type: string + default: latest + +jobs: + check: + runs-on: amd64 + container: + image: ${{ inputs.image_name }}:${{ inputs.image_tag }} + steps: + - name: Checkout code + uses: actions/checkout@v4 + env: + https_proxy: ${{ secrets.https_proxy }} + http_proxy: ${{ secrets.http_proxy }} + with: + ref: ${{ github.event.pull_request.head.sha }} + + + - name: Get short sha + id: slug + run: echo "sha8=$(echo ${GITHUB_SHA} | cut -c1-8)" >> $GITHUB_OUTPUT + + - name: generate cppcheck.sh + run: | + echo "generate /tmp/cppcheck.sh ..." + cat << EOF >/tmp/cppcheck.sh + #!/bin/bash + + echo "Running check.sh" + + default_ignore=( + "build/" + ".*_test.cc" + \${CPPCHECK_IGNORE[@]} + ) + echo "default_ignore is : \${default_ignore[@]}" + # 构造忽略参数 + ignore_args="" + for ignore in \${default_ignore[@]}; do + # 判断是否为目录 + if [ -d "\$ignore" ]; then + ignore_args="-i\$ignore \$ignore_args" + else + files=\$(find . ! -path '*/build/*' -regex "\$ignore" -type f) + for file in \$files; do + ignore_args="-i\$file \$ignore_args" + done + fi + done + + # 如果当前目录下没有cc或者cpp或者cxx或者c文件,则直接退出,不需要进行cppcheck检查 + if [ -z "\$(find . -name "*.cc" -o -name "*.cpp" -o -name "*.cxx" -o -name "*.c")" ]; then + echo "no cc or cpp or cxx files, not need to cppcheck" + exit 0 + fi + echo "check cmd is :" + echo "cppcheck . --enable=warning,style,performance,portability,missingInclude --xml \$ignore_args" + cppcheck . --enable=warning,style,performance,portability,missingInclude --xml \$ignore_args 2>/tmp/cppcheck.xml + + + # 如果cppcheck检查出错,直接退出 + if [ \$? -ne 0 ]; then + echo "cppcheck failed" + exit -1 + fi + echo "github.sha is : ${{ github.sha }}" + echo "short_sha is : ${{ steps.slug.outputs.sha8 }}" + + # 生成报告并提交到指定http文件服务器上 + if [ -n "${{secrets.CPPCHECK_REPORT_POST_URL}}" ]; then + check_report_dir=${{ github.repository }}/${{ github.ref_name }}/${{ steps.slug.outputs.sha8 }} + mkdir -p \$check_report_dir + echo "github.repository_owner is : ${{ github.repository_owner }}" + echo "github.repository.name is : ${{ github.repository }}" + echo "github.ref_name is : ${{ github.ref_name }}" + echo "steps.slug.outputs.sha8 is : ${{ steps.slug.outputs.sha8 }}" + echo "check_report_dir is : \$check_report_dir" + + cppcheck-htmlreport --file=/tmp/cppcheck.xml --title=CSA --report-dir=\$check_report_dir --source-dir=./ + echo "check_report_dir is : \$check_report_dir" + + + # zip 压缩 \$check_report_dir + zip -r \${{ steps.slug.outputs.sha8 }}.zip \$check_report_dir + curl -F file=@${{ steps.slug.outputs.sha8 }}.zip -F unzip=true \${{secrets.CPPCHECK_REPORT_POST_URL}} + + # 判断推送是否成功 + if [ \$? -ne 0 ]; then + echo "cppcheck report push failed" + exit -1 + fi + + # 删除临时文件 + rm -rf \$check_report_dir + rm -rf \${{ steps.slug.outputs.sha8 }}.zip + + # 打印报告地址 + # echo "cppcheck report url: ${{secrets.CPPCHECK_REPORT_POST_URL}}/\$check_report_dir/index.html" + echo -e "\\033[1;35m cppcheck report url: ${{secrets.CPPCHECK_REPORT_URL}}/\$check_report_dir/index.html \\033[0m" + fi + + EOF + - name: generate cppcheck_analyze.py + run: | + echo "generate /tmp/cppcheck_analyze.py ..." + + cat << EOF >/tmp/cppcheck_analyze.py + #!/usr/bin/env python3 + + import os + import sys + import xml.dom.minidom as minidom + + #cppcheck 所有的错误类型的严重性 + severityList=['information', 'performance', 'style', 'portability', 'warning', 'error'] + + informationSeverityCount = 0 + performanceSeverityCount = 0 + styleSeverityCount = 0 + portabilitySeverityCount = 0 + warningSeverityCount = 0 + errorSeverityCount = 0 + + # 如果 /tmp/cppcheck.xml 文件不存在,则直接退出 + if not os.path.exists('/tmp/cppcheck.xml'): + print("/tmp/cppcheck.xml not exists") + sys.exit(0) + + # 打开cppcheck输出文件 + with open('/tmp/cppcheck.xml', 'r') as file: + # 解析XML文件 + dom = minidom.parse(file) + + # 获取所有的error元素 + errors = dom.getElementsByTagName('error') + + for error in errors: + # 分别统计每种严重性的错误数 + for i in range(len(severityList)): + if error.getAttribute('severity') == severityList[i]: + if i == 0: + informationSeverityCount += 1 + elif i == 1: + performanceSeverityCount += 1 + elif i == 2: + styleSeverityCount += 1 + elif i == 3: + portabilitySeverityCount += 1 + elif i == 4: + warningSeverityCount += 1 + elif i == 5: + # 如果是syntaxError则不计入errorSeverityCount + if error.getAttribute('id') == 'syntaxError': + continue + # 如果是internalAstError则不计入errorSeverityCount + if error.getAttribute('id') == 'internalAstError': + continue + # 如果是internalAstError则不计入errorSeverityCount + if error.getAttribute('id') == 'unknownMacro': + continue + # 如 果msg中包含"Syntax"则不计入errorSeverityCount + if error.getAttribute('msg').find("Syntax") != -1: + continue + errorSeverityCount += 1 + print(error.getAttribute('msg')) + + # 打印结果 + # print(severityList) + print("informationSeverityCount: ", informationSeverityCount) + print("performanceSeverityCount: ", performanceSeverityCount) + print("styleSeverityCount: ", styleSeverityCount) + print("portabilitySeverityCount: ", portabilitySeverityCount) + print("warningSeverityCount: ", warningSeverityCount) + print("errorSeverityCount: ", errorSeverityCount) + + # 获取环境变量,如果超过阈值则返回-1,否则返回0 + + + # 获取环境变量 + informationSeverityMax = os.environ.get('INFORMATION_SEVERITY_MAX') + performanceSeverityMax = os.environ.get('PERFORMANCE_SEVERITY_MAX') + styleSeverityMax = os.environ.get('STYLE_SEVERITY_MAX') + portabilitySeverityMax = os.environ.get('PORTABILITY_SEVERITY_MAX') + warningSeverityMax = os.environ.get('WARNING_SEVERITY_MAX') + errorSeverityMax = os.environ.get('ERROR_SEVERITY_MAX') + + # 如果环境变量不存在则不做处理 + if informationSeverityMax != None: + if informationSeverityCount >= int(informationSeverityMax): + print("informationSeverityCount[{}] >= informationSeverityMax[{}]".format(informationSeverityCount, informationSeverityMax)) + sys.exit(-1) + + if performanceSeverityMax != None: + if performanceSeverityCount >= int(performanceSeverityMax): + print("performanceSeverityCount[{}] >= performanceSeverityMax[{}]".format(performanceSeverityCount, performanceSeverityMax)) + sys.exit(-1) + + if styleSeverityMax != None: + if styleSeverityCount >= int(styleSeverityMax): + print("styleSeverityCount[{}] >= styleSeverityMax[{}]".format(styleSeverityCount, styleSeverityMax)) + sys.exit(-1) + + if portabilitySeverityMax != None: + if portabilitySeverityCount >= int(portabilitySeverityMax): + print("portabilitySeverityCount[{}] >= portabilitySeverityMax[{}]".format(portabilitySeverityCount, portabilitySeverityMax)) + sys.exit(-1) + + if warningSeverityMax != None: + if warningSeverityCount >= int(warningSeverityMax): + print("warningSeverityCount[{}] >= warningSeverityMax[{}]".format(warningSeverityCount, warningSeverityMax)) + sys.exit(-1) + + if errorSeverityMax != None: + if errorSeverityCount >= int(errorSeverityMax): + print("errorSeverityCount[{}] >= errorSeverityMax[{}]".format(errorSeverityCount, errorSeverityMax)) + sys.exit(-1) + + EOF + - name: run cppcheck + run: | + chmod +x /tmp/cppcheck.sh + chmod +x /tmp/cppcheck_analyze.py + /tmp/cppcheck.sh + /tmp/cppcheck_analyze.py + + - name: run format + run: | + ./format.sh + git config --global --add safe.directory '*' + git status + if [ -n "$(git status --porcelain)" ]; then + echo "check ${{ github.ref_name }} failed" + exit 1 + fi + \ No newline at end of file diff --git a/.github/workflows/release-workflow.yml b/.github/workflows/release-workflow.yml new file mode 100644 index 000000000..12a18a24b --- /dev/null +++ b/.github/workflows/release-workflow.yml @@ -0,0 +1,90 @@ +name: deploy Workflow + +on: + workflow_call: + inputs: + image_name: + required: true + type: string + default: ubuntu + image_tag: + required: false + type: string + default: latest + run_platform: + required: false + type: string + default: amd64 + secrets: + TEST_CMD: + required: true + +jobs: + release: + runs-on: ${{ inputs.run_platform }} + container: + image: ${{ inputs.image_name }}:${{ inputs.image_tag }} + + steps: + - name: download build artifact + uses: actions/download-artifact@v3 + env: + https_proxy: ${{ secrets.https_proxy }} + http_proxy: ${{ secrets.http_proxy }} + with: + name: build_artifact + path: | + ${{vars.WHL_ARTIFACTS_DIR}} + + + - name: Get short sha + id: slug + run: echo "sha8=$(echo ${GITHUB_SHA} | cut -c1-8)" >> $GITHUB_OUTPUT + + - name: Set up release information + id: release_info + shell: bash + run: | + VERSION=$(echo ${{ github.ref_name }} | sed 's/^v//') + echo "RELEASE_NAME=${{ github.ref_name }}" >> $GITHUB_OUTPUT + echo "RELEASE_DIR=${{ github.repository }}/${{ github.ref_name }}/${{ github.sha }}" >> $GITHUB_OUTPUT + echo "VERSION=${VERSION}" >> $GITHUB_OUTPUT + + + - name: Upload wheel packages + shell: bash + id: whl_upload + run: | + set -x + ls -lah ${{ vars.WHL_ARTIFACTS_DIR }} + echo ${{ vars.WHL_ARTIFACTS_DIR }} + echo "${{ secrets.ARTIFACTS_URL }}/${{ steps.release_info.outputs.VERSION }}" + + find "${{ vars.WHL_ARTIFACTS_DIR }}" -name "*.whl" -exec curl -F "file=@{}" "${{ secrets.ARTIFACTS_WHL_URL }}/${{ steps.release_info.outputs.VERSION }}" \; + echo "上传 whl 包成功" + + + - name: 上传Release 产物 + uses: softprops/action-gh-release@v1 + env: + https_proxy: ${{ secrets.https_proxy }} + http_proxy: ${{ secrets.http_proxy }} + GITHUB_TOKEN: ${{ secrets.PATOKEN }} + with: + name: ${{ github.ref_name }} + files: | + ${{ vars.WHL_ARTIFACTS_DIR }}/*.whl + + - name: 显示 report 文件 + shell: bash + run: | + if [ -n "${{secrets.CPPCHECK_REPORT_POST_URL}}" ]; then + check_report_dir=${{ github.repository }}/${{ github.ref_name }}/${{ steps.slug.outputs.sha8 }} + echo "cppcheck report url: ${{secrets.CPPCHECK_REPORT_URL}}/$check_report_dir/index.html" + fi + if [ -n "${{secrets.TEST_COVERAGE_REPORT_POST_URL}}" ]; then + test_coverage_dir=${{ github.repository_owner }}/${{ github.event.repository.name }}/${{ github.ref_name }}/${{ steps.slug.outputs.sha8 }} + echo "test coverage report url: ${{secrets.TEST_COVERAGE_REPORT_URL}}/$test_coverage_dir/index.html" + fi + + diff --git a/.github/workflows/test-msvc-workflow.yml b/.github/workflows/test-msvc-workflow.yml new file mode 100644 index 000000000..3246dfa3c --- /dev/null +++ b/.github/workflows/test-msvc-workflow.yml @@ -0,0 +1,71 @@ +name: test Workflow + +on: + workflow_call: + inputs: + run_platform: + required: false + type: string + default: msvc + secrets: + BUILD_CMD: + required: true + +jobs: + test: + runs-on: ${{ inputs.run_platform }} + steps: + - name: Checkout code + uses: actions/checkout@v4 + env: + https_proxy: ${{ secrets.https_proxy }} + http_proxy: ${{ secrets.http_proxy }} + with: + ref: ${{ github.event.pull_request.head.sha }} + + - name: Setup Visual Studio Developer Command Prompt + uses: microsoft/setup-msbuild@v1.0.2 + + - name: Install Chocolatey + run: | + Set-ExecutionPolicy Bypass -Scope Process -Force + [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072 + iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1')) + if (Test-Path "C:\ProgramData\chocolatey\bin\choco.exe") { + Write-Host "Chocolatey is installed." + } else { + Write-Host "Chocolatey installation failed." + } + $env:Path += ";C:\ProgramData\chocolatey\bin" + [Environment]::SetEnvironmentVariable("Path", $env:Path, "Machine") + env: + https_proxy: ${{ secrets.https_proxy }} + http_proxy: ${{ secrets.http_proxy }} + + - name: Install Python using Chocolatey + run: | + $chocoPath = "C:\ProgramData\chocolatey" + $env:Path += ";$chocoPath\bin" + [Environment]::SetEnvironmentVariable("Path", $env:Path, "Machine") + + # 使用完整路径执行 choco 命令 + & "$chocoPath\bin\choco.exe" install python --version=3.11.0 -y + + env: + https_proxy: ${{ secrets.https_proxy }} + http_proxy: ${{ secrets.http_proxy }} + + + - name: Run test stage + shell: powershell + run: | + python --version + ${{ secrets.TEST_CMD_MSVC}} + if ($LASTEXITCODE -eq 1) { + Write-Host "test failed" + exit 1 + } else { + Write-Host "test succeeded" + } + + diff --git a/.github/workflows/test-workflow.yml b/.github/workflows/test-workflow.yml new file mode 100644 index 000000000..7ed0aca55 --- /dev/null +++ b/.github/workflows/test-workflow.yml @@ -0,0 +1,203 @@ +name: test Workflow + +on: + workflow_call: + inputs: + image_name: + required: true + type: string + default: ubuntu + image_tag: + required: false + type: string + default: latest + run_platform: + required: false + type: string + default: amd64 + test_report: + required: false + type: boolean + default: false + secrets: + TEST_CMD: + required: true + +jobs: + test: + runs-on: ${{ inputs.run_platform }} + container: + image: ${{ inputs.image_name }}:${{ inputs.image_tag }} + steps: + - name: Checkout code + uses: actions/checkout@v4 + env: + https_proxy: ${{ secrets.https_proxy }} + http_proxy: ${{ secrets.http_proxy }} + with: + ref: ${{ github.event.pull_request.head.sha }} + + - name: Get short sha + id: slug + run: echo "sha8=$(echo ${GITHUB_SHA} | cut -c1-8)" >> $GITHUB_OUTPUT + + - name: generate test_coverage.sh + if: inputs.test_report == true + run: | + echo "generate /tmp/test_coverage.sh ..." + + cat << EOF >/tmp/test_coverage.sh + #!/bin/bash + + # 生成报告并提交到指定http文件服务器上 + if [ -z "${{ secrets.TEST_COVERAGE_REPORT_POST_URL }}" ]; then + echo "TEST_COVERAGE_REPORT_POST_URL is empty, not generate test coverage report" + exit 0 + fi + + ret=0 + + # 创建一个\$workdir文件夹,用于存放所有的.gcda文件 + workdir='/tmp/gtest_report' + + rm \$workdir -rf + + mkdir -p \$workdir + + default_ignore=( + "_test.cc" + "_test.c" + "_test.cpp" + "*/build/_deps/*" + ) + + default_test_cov_ignore=( + "*/include/*" + "*/build/_deps/*" + \${TEST_COVERAGE_IGNORE[@]} + ) + + # 查找同时存在 *.gcda 和 *.gcno 文件的文件 + gcda_files=\$(find ./build -type f -name "*.gcda") + for gcda_file in \$gcda_files; do + # 如果匹配了 \$default_ignore 中的文件,则跳过 + for ignore in \${default_ignore[@]}; do + # 使用正则表达式匹配 + if [[ \$gcda_file =~ \$ignore ]]; then + echo "ignore \$gcda_file" + continue 2 + fi + done + + gcno_file="\${gcda_file%.gcda}.gcno" + if [ -f "\$gcno_file" ]; then + cp "\$gcda_file" "\$workdir" + cp "\$gcno_file" "\$workdir" + fi + done + + cd \$workdir + + # 如果当前目录下没有gcda文件,则直接退出,不需要进行测试覆盖率检查 + if [ -z "\$(find . -name "*.gcda")" ]; then + echo "no gcda files, not need to test coverage" + exit 0 + fi + + # 使用 gcov 来生成覆盖率数据 + gcov *.gcno >/dev/null 2>&1 + + # 生成报告 + lcov -c -d . -o coverage.info >/dev/null 2>&1 + + # 过滤掉不需要的信息 + + for ignore in \${default_test_cov_ignore[@]}; do + lcov --remove coverage.info "\$ignore" -o coverage.info >/dev/null 2>&1 + done + + # 列出测试覆盖率 + lcov -l coverage.info + + # 判断整体覆盖率是否达标 + test_coverage=\$(lcov -l coverage.info | grep "Total:" | sed 's/|/| /g' | awk '{print \$2}' | awk -F '%' '{print \$1}') + # if error to get test_coverage + if [ -z "\$test_coverage" ]; + then + echo "no test coverage, skip" + exit 0 + fi + + # 生成html报告 + + + test_coverage_dir=${{ github.repository_owner }}/${{ github.event.repository.name }}/${{ github.ref_name }}/${{ steps.slug.outputs.sha8 }} + mkdir -p \$test_coverage_dir + + genhtml coverage.info -o \$test_coverage_dir >/dev/null 2>&1 + + # zip 压缩 \$test_coverage_dir + zip -r ${{ steps.slug.outputs.sha8 }}.zip \$test_coverage_dir + + # 生成报告并提交到指定http文件服务器上 + curl -F file=@\${{ steps.slug.outputs.sha8 }}.zip -F unzip=true \${{secrets.TEST_COVERAGE_REPORT_POST_URL}} + + # 判断推送是否成功 + if [ \$? -ne 0 ]; then + echo "test report push failed ..." + ret=1 + fi + + # 如果 TEST_COVERAGE_THRESHOLD 变量为空,则默认为 0 + if [ -z "\$TEST_COVERAGE_THRESHOLD" ]; then + TEST_COVERAGE_THRESHOLD=0 + fi + + + + echo "test coverage: \$test_coverage" + + # 将TEST_COVERAGE_THRESHOLD转换为数字,防止出现字符串比较的情况 + test_coverage_threshold=\$(echo \$TEST_COVERAGE_THRESHOLD | tr -d '"') + + #比较两个数的大小,一个是小数,一个是整数 + if [ \$(echo "\$test_coverage >= \$test_coverage_threshold" | bc) -eq 1 ]; then + echo "test coverage is greater than \$test_coverage_threshold" + else + echo "test coverage is less than \$test_coverage_threshold" + ret=1 + fi + + # 删除临时文件 + rm -rf \$test_coverage_dir + rm -rf ${{ steps.slug.outputs.sha8 }}.zip + + # 打印报告地址 + echo -e "\\033[1;35m test coverage report url: ${{secrets.TEST_COVERAGE_REPORT_URL}}/\$test_coverage_dir/index.html \\033[0m" + + exit \$ret + + EOF + + - name: Run test stage + shell: bash + env: + https_proxy: "" + http_proxy: "" + no_proxy: "*" + run: | + echo "runnint test in workflow_call" + source /opt/ros/humble/setup.bash + echo "${{ secrets.TEST_CMD}}" + eval "${{ secrets.TEST_CMD}}" + + - name: authorize test_coverage.sh + if: inputs.test_report == true + env: + https_proxy: "" + http_proxy: "" + no_proxy: "*" + run: | + chmod +x /tmp/test_coverage.sh + echo "test_coverage.sh is authorized" + bash /tmp/test_coverage.sh \ No newline at end of file diff --git a/build.sh b/build.sh index 204d6a9f3..d23123abc 100755 --- a/build.sh +++ b/build.sh @@ -1,7 +1,7 @@ #!/bin/bash # exit on error and print each command -set -ex +set -e cmake -B build \ -DCMAKE_BUILD_TYPE=Release \ diff --git a/test.sh b/test.sh index 11b201361..82fdc0a0d 100755 --- a/test.sh +++ b/test.sh @@ -1,7 +1,7 @@ #!/bin/bash # exit on error and print each command -set -ex +set -e cmake -B build \ -DCMAKE_BUILD_TYPE=Release \