name: Code Coverage with codecov on: push: branches: - main - v*-branch - collab-* permissions: contents: read concurrency: group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.head_ref || github.ref }} cancel-in-progress: true jobs: codecov: if: github.repository_owner == 'zephyrproject-rtos' runs-on: group: zephyr-runner-v2-linux-x64-4xlarge container: image: ghcr.io/zephyrproject-rtos/ci-repo-cache:v0.28.7.20251127 options: '--entrypoint /bin/bash' strategy: fail-fast: false matrix: platform: ["mps2/an385", "native_sim", "qemu_x86", "unit_testing"] include: - platform: 'mps2/an385' normalized: 'mps2_an385' - platform: 'native_sim' normalized: 'native_sim' - platform: 'qemu_x86' normalized: 'qemu_x86' - platform: 'unit_testing' normalized: 'unit_testing' env: CCACHE_DIR: /node-cache/ccache-zephyr CCACHE_REMOTE_STORAGE: "redis://cache-*.keydb-cache.svc.cluster.local|shards=1,2,3" CCACHE_REMOTE_ONLY: "true" # `--specs` is ignored because ccache is unable to resovle the toolchain specs file path. CCACHE_IGNOREOPTIONS: '-specs=* --specs=*' steps: - name: Apply container owner mismatch workaround run: | # FIXME: The owner UID of the GITHUB_WORKSPACE directory may not # match the container user UID because of the way GitHub # Actions runner is implemented. Remove this workaround when # GitHub comes up with a fundamental fix for this problem. git config --global --add safe.directory ${GITHUB_WORKSPACE} - name: Print cloud service information run: | echo "ZEPHYR_RUNNER_CLOUD_PROVIDER = ${ZEPHYR_RUNNER_CLOUD_PROVIDER}" echo "ZEPHYR_RUNNER_CLOUD_NODE = ${ZEPHYR_RUNNER_CLOUD_NODE}" echo "ZEPHYR_RUNNER_CLOUD_POD = ${ZEPHYR_RUNNER_CLOUD_POD}" - name: Update PATH for west run: | echo "$HOME/.local/bin" >> $GITHUB_PATH - name: Clone cached Zephyr repository continue-on-error: true run: | git clone --shared /repo-cache/zephyrproject/zephyr . git remote set-url origin ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY} - name: checkout uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: fetch-depth: 0 - name: Set up Python uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0 with: python-version: 3.12 cache: pip cache-dependency-path: scripts/requirements-actions.txt - name: Install Python packages run: | pip install -r scripts/requirements-actions.txt --require-hashes - name: west setup run: | west init -l . || true west update 1> west.update.log || west update 1> west.update-2.log - name: Environment Setup run: | cmake --version gcc --version ls -la echo "ZEPHYR_SDK_INSTALL_DIR=/opt/toolchains/zephyr-sdk-$( cat SDK_VERSION )" >> $GITHUB_ENV - name: Set up ccache run: | mkdir -p ${CCACHE_DIR} ccache -M 10G ccache -p ccache -z -s -vv - name: Run Tests with Twister (Push) continue-on-error: true run: | export ZEPHYR_BASE=${PWD} export ZEPHYR_TOOLCHAIN_VARIANT=zephyr mkdir -p coverage/reports ./scripts/twister --save-tests ${{matrix.normalized}}-testplan.json ls -la ./scripts/twister \ -i --force-color -N -v --filter runnable -p ${{ matrix.platform }} --coverage \ -T tests --coverage-tool gcovr -xCONFIG_TEST_EXTRA_STACK_SIZE=4096 -e nano \ --timeout-multiplier 2 - name: Build Doxygen Coverage if: matrix.platform == 'unit_testing' run: | pip install -r doc/requirements.txt --require-hashes sudo apt-get update sudo apt-get install -y graphviz # dot is needed but currently missing from the Docker image cmake -B doc/_build -S doc cmake --build doc/_build --target doxygen-coverage - name: Upload Doxygen Coverage Results if: matrix.platform == 'unit_testing' uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: doxygen-coverage-results path: | doc/_build/new.info doc/_build/coverage-report - name: Print ccache stats if: always() run: | ccache -s -vv - name: Rename coverage files if: always() run: | mv twister-out/coverage.json coverage/reports/${{matrix.normalized}}.json - name: Upload Coverage Results if: always() uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: Coverage Data (Subset ${{ matrix.normalized }}) path: | coverage/reports/${{ matrix.normalized }}.json ${{ matrix.normalized }}-testplan.json codecov-results: name: "Publish Coverage Results" needs: codecov runs-on: ubuntu-24.04 # the codecov job might be skipped, we don't need to run this job then if: success() || failure() steps: - name: checkout uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: fetch-depth: 0 - name: Set up Python uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0 with: python-version: 3.12 cache: pip cache-dependency-path: scripts/requirements-actions.txt - name: Install Python packages run: | pip install -r scripts/requirements-actions.txt --require-hashes - name: Download Artifacts uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0 with: path: coverage/reports - name: Move coverage files run: | ls -lRt ./coverage/reports mv ./coverage/reports/*/*testplan.json . mv ./coverage/reports/*/coverage/reports/*.json ./coverage/reports ls -la ./coverage/reports - name: Generate list of coverage files id: get-coverage-files shell: cmake -P {0} run: | file(GLOB INPUT_FILES_LIST "coverage/reports/*.json") set(MERGELIST "") set(FILELIST "") foreach(ITEM ${INPUT_FILES_LIST}) get_filename_component(f ${ITEM} NAME) if(FILELIST STREQUAL "") set(FILELIST "${f}") else() set(FILELIST "${FILELIST},${f}") endif() endforeach() foreach(ITEM ${INPUT_FILES_LIST}) get_filename_component(f ${ITEM} NAME) if(MERGELIST STREQUAL "") set(MERGELIST "--add-tracefile ${f}") else() set(MERGELIST "${MERGELIST} -a ${f}") endif() endforeach() file(APPEND $ENV{GITHUB_OUTPUT} "mergefiles=${MERGELIST}\n") file(APPEND $ENV{GITHUB_OUTPUT} "covfiles=${FILELIST}\n") - name: Merge coverage files run: | pushd ./coverage/reports gcovr ${{ steps.get-coverage-files.outputs.mergefiles }} --merge-mode-functions=separate --json merged.json gcovr ${{ steps.get-coverage-files.outputs.mergefiles }} --merge-mode-functions=separate --cobertura merged.xml popd - name: Get current date id: run_date run: | echo "run_date=$(date --iso-8601=minutes)" >> "$GITHUB_OUTPUT" echo "run_date_short=$(date +'%Y-%m-%d')" >> "$GITHUB_OUTPUT" echo "run_date_year=$(date +'%Y')" >> "$GITHUB_OUTPUT" echo "run_date_month=$(date +'%m')" >> "$GITHUB_OUTPUT" - name: Generate Coverage Report if: always() run: | python3 ./scripts/ci/coverage/coverage_analysis.py \ -t native_sim-testplan.json \ -m MAINTAINERS.yml \ -c coverage/reports/merged.json \ -o coverage-report-${{ steps.run_date.outputs.run_date_short }} \ -f all cp coverage-report-* coverage/reports/ - name: Upload Merged Coverage Results and Report if: always() uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: Coverage Data and report path: | coverage/reports/merged.json coverage/reports/merged.xml coverage/reports/coverage-report-${{ steps.run_date.outputs.run_date_short }}.json coverage/reports/coverage-report-${{ steps.run_date.outputs.run_date_short }}.xlsx - name: Upload test coverage to Codecov if: always() uses: codecov/codecov-action@5a1091511ad55cbe89839c7260b706298ca349f7 # v5.5.1 with: env_vars: OS,PYTHON fail_ci_if_error: false verbose: true token: ${{ secrets.CODECOV_TOKEN }} files: coverage/reports/merged.xml flags: unittests-coverage - name: Upload Doxygen coverage to Codecov if: always() uses: codecov/codecov-action@5a1091511ad55cbe89839c7260b706298ca349f7 # v5.5.1 with: env_vars: OS,PYTHON fail_ci_if_error: false verbose: true token: ${{ secrets.CODECOV_TOKEN }} files: coverage/reports/doxygen-coverage-results/new.info disable_search: true flags: doxygen-coverage