1name: Code Coverage with codecov
2
3on:
4  schedule:
5    - cron: '25 06,18 * * *'
6
7concurrency:
8  group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.head_ref || github.ref }}
9  cancel-in-progress: true
10
11jobs:
12  codecov:
13    if: github.repository_owner == 'zephyrproject-rtos'
14    runs-on:
15      group: zephyr-runner-v2-linux-x64-4xlarge
16    container:
17      image: ghcr.io/zephyrproject-rtos/ci-repo-cache:v0.27.4.20241026
18      options: '--entrypoint /bin/bash'
19    strategy:
20      fail-fast: false
21      matrix:
22        platform: ["mps2/an385", "native_sim", "qemu_x86", "unit_testing"]
23        include:
24          - platform: 'mps2/an385'
25            normalized: 'mps2_an385'
26          - platform: 'native_sim'
27            normalized: 'native_sim'
28          - platform: 'qemu_x86'
29            normalized: 'qemu_x86'
30          - platform: 'unit_testing'
31            normalized: 'unit_testing'
32    env:
33      CCACHE_DIR: /node-cache/ccache-zephyr
34      CCACHE_REMOTE_STORAGE: "redis://cache-*.keydb-cache.svc.cluster.local|shards=1,2,3"
35      CCACHE_REMOTE_ONLY: "true"
36      # `--specs` is ignored because ccache is unable to resovle the toolchain specs file path.
37      CCACHE_IGNOREOPTIONS: '-specs=* --specs=*'
38    steps:
39      - name: Apply container owner mismatch workaround
40        run: |
41          # FIXME: The owner UID of the GITHUB_WORKSPACE directory may not
42          #        match the container user UID because of the way GitHub
43          #        Actions runner is implemented. Remove this workaround when
44          #        GitHub comes up with a fundamental fix for this problem.
45          git config --global --add safe.directory ${GITHUB_WORKSPACE}
46
47      - name: Print cloud service information
48        run: |
49          echo "ZEPHYR_RUNNER_CLOUD_PROVIDER = ${ZEPHYR_RUNNER_CLOUD_PROVIDER}"
50          echo "ZEPHYR_RUNNER_CLOUD_NODE = ${ZEPHYR_RUNNER_CLOUD_NODE}"
51          echo "ZEPHYR_RUNNER_CLOUD_POD = ${ZEPHYR_RUNNER_CLOUD_POD}"
52
53      - name: Update PATH for west
54        run: |
55          echo "$HOME/.local/bin" >> $GITHUB_PATH
56
57      - name: Clone cached Zephyr repository
58        continue-on-error: true
59        run: |
60          git clone --shared /repo-cache/zephyrproject/zephyr .
61          git remote set-url origin ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}
62
63      - name: checkout
64        uses: actions/checkout@v4
65        with:
66          fetch-depth: 0
67
68      - name: west setup
69        run: |
70          west init -l . || true
71          west update 1> west.update.log || west update 1> west.update-2.log
72
73      - name: Environment Setup
74        run: |
75          cmake --version
76          gcc --version
77          ls -la
78
79          echo "ZEPHYR_SDK_INSTALL_DIR=/opt/toolchains/zephyr-sdk-$( cat SDK_VERSION )" >> $GITHUB_ENV
80
81      - name: Set up ccache
82        run: |
83          mkdir -p ${CCACHE_DIR}
84          ccache -M 10G
85          ccache -p
86          ccache -z -s -vv
87
88      - name: Update BabbleSim to manifest revision
89        run: |
90          export BSIM_VERSION=$( west list bsim -f {revision} )
91          echo "Manifest points to bsim sha $BSIM_VERSION"
92          cd /opt/bsim_west/bsim
93          git fetch -n origin ${BSIM_VERSION}
94          git -c advice.detachedHead=false checkout ${BSIM_VERSION}
95          west update
96          make everything -s -j 8
97
98      - name: Run Tests with Twister (Push)
99        continue-on-error: true
100        run: |
101          export ZEPHYR_BASE=${PWD}
102          export ZEPHYR_TOOLCHAIN_VARIANT=zephyr
103          mkdir -p coverage/reports
104          pip install gcovr==6.0
105          ./scripts/twister -E ${{matrix.normalized}}-testplan.json
106          ls -la
107          ./scripts/twister \
108            -i --force-color -N -v --filter runnable -p ${{ matrix.platform }} --coverage \
109            -T tests --coverage-tool gcovr -xCONFIG_TEST_EXTRA_STACK_SIZE=4096 -e nano \
110            --timeout-multiplier 2
111
112      - name: Print ccache stats
113        if: always()
114        run: |
115          ccache -s -vv
116
117      - name: Rename coverage files
118        if: always()
119        run: |
120          mv twister-out/coverage.json coverage/reports/${{matrix.normalized}}.json
121
122      - name: Upload Coverage Results
123        if: always()
124        uses: actions/upload-artifact@v4
125        with:
126          name: Coverage Data (Subset ${{ matrix.normalized }})
127          path: |
128            coverage/reports/${{ matrix.normalized }}.json
129            ${{ matrix.normalized }}-testplan.json
130
131  codecov-results:
132    name: "Publish Coverage Results"
133    needs: codecov
134    runs-on: ubuntu-22.04
135    # the codecov job might be skipped, we don't need to run this job then
136    if: success() || failure()
137
138    steps:
139      - name: checkout
140        uses: actions/checkout@v4
141        with:
142          fetch-depth: 0
143
144      - name: Download Artifacts
145        uses: actions/download-artifact@v4
146        with:
147          path: coverage/reports
148
149      - name: Move coverage files
150        run: |
151          ls -lRt  ./coverage/reports
152          mv ./coverage/reports/*/*testplan.json .
153          mv ./coverage/reports/*/coverage/reports/*.json ./coverage/reports
154          ls -la ./coverage/reports
155
156      - name: Generate list of coverage files
157        id: get-coverage-files
158        shell: cmake -P {0}
159        run: |
160          file(GLOB INPUT_FILES_LIST  "coverage/reports/*.json")
161          set(MERGELIST "")
162          set(FILELIST "")
163          foreach(ITEM ${INPUT_FILES_LIST})
164            get_filename_component(f ${ITEM} NAME)
165            if(FILELIST STREQUAL "")
166              set(FILELIST "${f}")
167            else()
168              set(FILELIST "${FILELIST},${f}")
169            endif()
170          endforeach()
171          foreach(ITEM ${INPUT_FILES_LIST})
172            get_filename_component(f ${ITEM} NAME)
173            if(MERGELIST STREQUAL "")
174              set(MERGELIST "--add-tracefile ${f}")
175            else()
176              set(MERGELIST "${MERGELIST} -a ${f}")
177            endif()
178          endforeach()
179          file(APPEND $ENV{GITHUB_OUTPUT} "mergefiles=${MERGELIST}\n")
180          file(APPEND $ENV{GITHUB_OUTPUT} "covfiles=${FILELIST}\n")
181
182      - name: Merge coverage files
183        run: |
184          pushd ./coverage/reports
185          pip install gcovr==6.0
186          gcovr ${{ steps.get-coverage-files.outputs.mergefiles }}  --merge-mode-functions=separate --json merged.json
187          gcovr ${{ steps.get-coverage-files.outputs.mergefiles }} --merge-mode-functions=separate --cobertura merged.xml
188          popd
189
190      - name: Get current date
191        id: run_date
192        run: |
193            echo "run_date=$(date --iso-8601=minutes)" >> "$GITHUB_OUTPUT"
194            echo "run_date_short=$(date +'%Y-%m-%d')" >> "$GITHUB_OUTPUT"
195            echo "run_date_year=$(date +'%Y')" >> "$GITHUB_OUTPUT"
196            echo "run_date_month=$(date +'%m')" >> "$GITHUB_OUTPUT"
197
198      - name: Generate Coverage Report
199        if: always()
200        run: |
201          pip install xlsxwriter ijson
202          python3 ./scripts/ci/coverage/coverage_analysis.py \
203            -t native_sim-testplan.json \
204            -m MAINTAINERS.yml \
205            -c coverage/reports/merged.json \
206            -o coverage-report-${{ steps.run_date.outputs.run_date_short }} \
207            -f all
208          cp coverage-report-* coverage/reports/
209
210      - name: Upload Merged Coverage Results and Report
211        if: always()
212        uses: actions/upload-artifact@v4
213        with:
214          name: Coverage Data and report
215          path: |
216            coverage/reports/merged.json
217            coverage/reports/merged.xml
218            coverage/reports/coverage-report-${{ steps.run_date.outputs.run_date_short }}.json
219            coverage/reports/coverage-report-${{ steps.run_date.outputs.run_date_short }}.xlsx
220
221      - name: Upload coverage to Codecov
222        if: always()
223        uses: codecov/codecov-action@v4
224        with:
225          env_vars: OS,PYTHON
226          fail_ci_if_error: false
227          verbose: true
228          token: ${{ secrets.CODECOV_TOKEN }}
229          files: coverage/reports/merged.xml
230