1name: Run tests with twister
2
3on:
4  push:
5    branches:
6      - main
7      - v*-branch
8      - collab-*
9  pull_request_target:
10    branches:
11      - main
12      - v*-branch
13      - collab-*
14  schedule:
15    # Run at 03:00 UTC on every Sunday
16    - cron: '0 3 * * 0'
17
18concurrency:
19  group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.head_ref || github.ref }}
20  cancel-in-progress: true
21
22jobs:
23  twister-build-prep:
24    uses: ./.github/workflows/twister-prep.yaml
25
26  twister-build:
27    runs-on:
28      group: zephyr-runner-v2-linux-x64-4xlarge
29    needs: twister-build-prep
30    if: needs.twister-build-prep.outputs.size != 0
31    container:
32      image: ghcr.io/zephyrproject-rtos/ci-repo-cache:v0.27.4.20241026
33      options: '--entrypoint /bin/bash'
34    strategy:
35      fail-fast: false
36      matrix:
37        subset: ${{fromJSON(needs.twister-build-prep.outputs.subset)}}
38    timeout-minutes: 1440
39    env:
40      CCACHE_DIR: /node-cache/ccache-zephyr
41      CCACHE_REMOTE_STORAGE: "redis://cache-*.keydb-cache.svc.cluster.local|shards=1,2,3"
42      CCACHE_REMOTE_ONLY: "true"
43      # `--specs` is ignored because ccache is unable to resolve the toolchain specs file path.
44      CCACHE_IGNOREOPTIONS: '-specs=* --specs=*'
45      BSIM_OUT_PATH: /opt/bsim/
46      BSIM_COMPONENTS_PATH: /opt/bsim/components
47      TWISTER_COMMON: '--no-detailed-test-id --force-color --inline-logs -v -N -M --retry-failed 3 --timeout-multiplier 2 '
48      WEEKLY_OPTIONS: ' -M --build-only --all --show-footprint --report-filtered'
49      PR_OPTIONS: ' --clobber-output --integration'
50      PUSH_OPTIONS: ' --clobber-output -M --show-footprint --report-filtered'
51      COMMIT_RANGE: ${{ github.event.pull_request.base.sha }}..${{ github.event.pull_request.head.sha }}
52      BASE_REF: ${{ github.base_ref }}
53    steps:
54      - name: Print cloud service information
55        run: |
56          echo "ZEPHYR_RUNNER_CLOUD_PROVIDER = ${ZEPHYR_RUNNER_CLOUD_PROVIDER}"
57          echo "ZEPHYR_RUNNER_CLOUD_NODE = ${ZEPHYR_RUNNER_CLOUD_NODE}"
58          echo "ZEPHYR_RUNNER_CLOUD_POD = ${ZEPHYR_RUNNER_CLOUD_POD}"
59
60      - name: Apply container owner mismatch workaround
61        run: |
62          # FIXME: The owner UID of the GITHUB_WORKSPACE directory may not
63          #        match the container user UID because of the way GitHub
64          #        Actions runner is implemented. Remove this workaround when
65          #        GitHub comes up with a fundamental fix for this problem.
66          git config --global --add safe.directory ${GITHUB_WORKSPACE}
67
68      - name: Clone cached Zephyr repository
69        continue-on-error: true
70        run: |
71          git clone --shared /repo-cache/zephyrproject/zephyr .
72          git remote set-url origin ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}
73
74      - name: Checkout
75        uses: actions/checkout@v4
76        with:
77          ref: ${{ github.event.pull_request.head.sha }}
78          fetch-depth: 0
79          persist-credentials: false
80
81      - name: Environment Setup
82        run: |
83          if [ "${{github.event_name}}" = "pull_request_target" ]; then
84            git config --global user.email "bot@zephyrproject.org"
85            git config --global user.name "Zephyr Builder"
86            rm -fr ".git/rebase-apply"
87            rm -fr ".git/rebase-merge"
88            git rebase origin/${BASE_REF}
89            git clean -f -d
90            git log  --pretty=oneline | head -n 10
91          fi
92          echo "$HOME/.local/bin" >> $GITHUB_PATH
93          echo "$HOME/.cargo/bin" >> $GITHUB_PATH
94
95          west init -l . || true
96          west config manifest.group-filter -- +ci,+optional
97          west config --global update.narrow true
98          west update --path-cache /repo-cache/zephyrproject 2>&1 1> west.update.log || west update --path-cache /repo-cache/zephyrproject 2>&1 1> west.update.log || ( rm -rf ../modules ../bootloader ../tools && west update --path-cache /repo-cache/zephyrproject)
99          west forall -c 'git reset --hard HEAD'
100
101          echo "ZEPHYR_SDK_INSTALL_DIR=/opt/toolchains/zephyr-sdk-$( cat SDK_VERSION )" >> $GITHUB_ENV
102
103      - name: Check Environment
104        run: |
105          cmake --version
106          gcc --version
107          cargo --version
108          rustup target list --installed
109          ls -la
110          echo "github.ref: ${{ github.ref }}"
111          echo "github.base_ref: ${{ github.base_ref }}"
112          echo "github.ref_name: ${{ github.ref_name }}"
113
114      - name: Set up ccache
115        run: |
116          mkdir -p ${CCACHE_DIR}
117          ccache -M 10G
118          ccache -p
119          ccache -z -s -vv
120
121      - name: Update BabbleSim to manifest revision
122        run: |
123          export BSIM_VERSION=$( west list bsim -f {revision} )
124          echo "Manifest points to bsim sha $BSIM_VERSION"
125          cd /opt/bsim_west/bsim
126          git fetch -n origin ${BSIM_VERSION}
127          git -c advice.detachedHead=false checkout ${BSIM_VERSION}
128          west update
129          make everything -s -j 8
130
131      - if: github.event_name == 'push'
132        name: Run Tests with Twister (Push)
133        id: run_twister
134        run: |
135          export ZEPHYR_BASE=${PWD}
136          export ZEPHYR_TOOLCHAIN_VARIANT=zephyr
137          ./scripts/twister --subset ${{matrix.subset}}/${{ strategy.job-total }} ${TWISTER_COMMON} ${PUSH_OPTIONS}
138          if [ "${{matrix.subset}}" = "1" ]; then
139            ./scripts/zephyr_module.py --twister-out module_tests.args
140            if [ -s module_tests.args ]; then
141              ./scripts/twister +module_tests.args --outdir module_tests ${TWISTER_COMMON} ${PUSH_OPTIONS}
142            fi
143          fi
144
145      - if: github.event_name == 'pull_request_target'
146        name: Run Tests with Twister (Pull Request)
147        id: run_twister_pr
148        run: |
149          rm -f testplan.json
150          export ZEPHYR_BASE=${PWD}
151          export ZEPHYR_TOOLCHAIN_VARIANT=zephyr
152          python3 ./scripts/ci/test_plan.py -c origin/${BASE_REF}.. --pull-request --no-detailed-test-id
153          ./scripts/twister --subset ${{matrix.subset}}/${{ strategy.job-total }} --load-tests testplan.json ${TWISTER_COMMON} ${PR_OPTIONS}
154          if [ "${{matrix.subset}}" = "1" -a ${{needs.twister-build-prep.outputs.fullrun}} = 'True' ]; then
155            ./scripts/zephyr_module.py --twister-out module_tests.args
156            if [ -s module_tests.args ]; then
157              ./scripts/twister +module_tests.args --outdir module_tests ${TWISTER_COMMON} ${PR_OPTIONS}
158            fi
159          fi
160
161      - if: github.event_name == 'schedule'
162        name: Run Tests with Twister (Daily)
163        id: run_twister_sched
164        run: |
165          export ZEPHYR_BASE=${PWD}
166          export ZEPHYR_TOOLCHAIN_VARIANT=zephyr
167          ./scripts/twister --subset ${{matrix.subset}}/${{ strategy.job-total }} ${TWISTER_COMMON} ${WEEKLY_OPTIONS}
168          if [ "${{matrix.subset}}" = "1" ]; then
169            ./scripts/zephyr_module.py --twister-out module_tests.args
170            if [ -s module_tests.args ]; then
171              ./scripts/twister +module_tests.args --outdir module_tests ${TWISTER_COMMON} ${WEEKLY_OPTIONS}
172            fi
173          fi
174
175      - name: Print ccache stats
176        if: always()
177        run: |
178          ccache -s -vv
179
180      - name: Upload Unit Test Results
181        if: always()
182        uses: actions/upload-artifact@v4
183        with:
184          name: Unit Test Results (Subset ${{ matrix.subset }})
185          if-no-files-found: ignore
186          path: |
187            twister-out/twister.xml
188            twister-out/twister.json
189            module_tests/twister.xml
190            testplan.json
191
192      - if: matrix.subset == 1 && github.event_name == 'push'
193        name: Save the list of Python packages
194        shell: bash
195        run: |
196          FREEZE_FILE="frozen-requirements.txt"
197          timestamp="$(date)"
198          version="$(git describe --abbrev=12 --always)"
199          echo -e "# Generated at $timestamp ($version)\n" > $FREEZE_FILE
200          pip freeze | tee -a $FREEZE_FILE
201
202      - if: matrix.subset == 1 && github.event_name == 'push'
203        name: Upload the list of Python packages
204        uses: actions/upload-artifact@v4
205        with:
206          name: Frozen PIP package set
207          path: |
208            frozen-requirements.txt
209
210  twister-test-results:
211    name: "Publish Unit Tests Results"
212    needs:
213      - twister-build
214    runs-on: ubuntu-22.04
215    # the build-and-test job might be skipped, we don't need to run this job then
216    if: success() || failure()
217
218    steps:
219      - name: Download Artifacts
220        uses: actions/download-artifact@v4
221        with:
222          path: artifacts
223
224      - name: Merge Test Results
225        run: |
226          pip install junitparser junit2html
227          junitparser merge artifacts/*/*/twister.xml junit.xml
228          junit2html junit.xml junit.html
229
230      - name: Upload Unit Test Results in HTML
231        if: always()
232        uses: actions/upload-artifact@v4
233        with:
234          name: HTML Unit Test Results
235          if-no-files-found: ignore
236          path: |
237            junit.html
238
239      - name: Publish Unit Test Results
240        uses: EnricoMi/publish-unit-test-result-action@v2
241        with:
242          check_name: Unit Test Results
243          files: "**/twister.xml"
244          comment_mode: off
245  twister-status-check:
246    if: always()
247    name: "Check Twister Status"
248    needs:
249      - twister-build-prep
250      - twister-build
251    uses: ./.github/workflows/ready-to-merge.yml
252    with:
253      needs_context: ${{ toJson(needs) }}
254