Replace bespoke MATLAB HW-CI with pyadi-iio mirror #4
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Hardware Tests | |
| # Modelled on pyadi-iio's hardware-test.yml (same labs, same coordinator, | |
| # same conventions). The shape is intentional: preflight probes the | |
| # coordinator for places, intersects against `test/hw/env/*.yaml` | |
| # committed in this repo, and emits a matrix; per-place shards acquire | |
| # the place + run pytest + release. The bridge tests in test/hw/ | |
| # subprocess MATLAB once per supported board reference name. | |
| permissions: | |
| contents: read | |
| # dorny/test-reporter creates Check Runs for the per-leg JUnit reports. | |
| checks: write | |
| pull-requests: write | |
| on: | |
| push: | |
| branches: [master] | |
| pull_request: | |
| # Gated below by the `hw-test` label so PRs don't auto-grab the | |
| # self-hosted runners. | |
| types: [labeled, opened, synchronize, reopened] | |
| workflow_dispatch: | |
| env: | |
| COORDINATOR: ${{ vars.ADI_LG_COORDINATOR }} | |
| concurrency: | |
| group: hw-${{ github.ref }} | |
| cancel-in-progress: false | |
| jobs: | |
| preflight: | |
| # Gate: on PRs only when the `hw-test` label is applied. Push/dispatch | |
| # always run. | |
| if: >- | |
| github.event_name != 'pull_request' || | |
| contains(github.event.pull_request.labels.*.name, 'hw-test') | |
| runs-on: [self-hosted, hw-coordinator] | |
| timeout-minutes: 3 | |
| outputs: | |
| available_nodes: ${{ steps.probe.outputs.available_nodes }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Bootstrap uv | |
| run: bash .github/scripts/bootstrap-uv.sh | |
| - id: probe | |
| timeout-minutes: 2 | |
| env: | |
| LABGRID_PIP: 'labgrid @ git+https://github.com/tfcollins/labgrid.git@tfcollins/plugin-support' | |
| run: | | |
| set -euo pipefail | |
| HOST="${COORDINATOR%%:*}" | |
| PORT="${COORDINATOR##*:}" | |
| if ! timeout 10 bash -c "</dev/tcp/$HOST/$PORT" 2>/dev/null; then | |
| echo "Coordinator $COORDINATOR unreachable — marking all nodes unavailable." >&2 | |
| echo 'available_nodes=[]' >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| export PATH="$HOME/.local/bin:$PATH" | |
| VENV="$HOME/.cache/trx-hw-ci/labgrid-venv" | |
| if [[ ! -x "$VENV/bin/labgrid-client" ]]; then | |
| echo "Creating labgrid venv at $VENV" >&2 | |
| uv venv --quiet "$VENV" | |
| uv pip install --quiet --python "$VENV/bin/python" "$LABGRID_PIP" | |
| fi | |
| LGCLIENT="$VENV/bin/labgrid-client" | |
| echo "Using labgrid-client: $LGCLIENT" >&2 | |
| echo "--- places -v output ---" >&2 | |
| PLACES_OUT=$(timeout 30 "$LGCLIENT" -x "$COORDINATOR" -v places 2>&1 || true) | |
| echo "$PLACES_OUT" >&2 | |
| echo "--- end places -v output ---" >&2 | |
| python3 - "$PLACES_OUT" >> "$GITHUB_OUTPUT" <<'PY' | |
| import json, os, re, sys | |
| # Discovery-driven matrix (same pattern pyadi-iio uses): | |
| # 1. Coordinator publishes places, each tagged with | |
| # carrier=<X> / daughter-board=<Y>. | |
| # 2. Per-place env yamls live at test/hw/env/<place>.yaml. | |
| # A place is auto-included when its yaml exists in the | |
| # repo. | |
| # 3. We also emit the place's `daughter-board` tag so the | |
| # shard can set $HW_DAUGHTER for the adi_lg_plugins | |
| # pytest plugin's iio_hardware-marker filter. | |
| place_re = re.compile(r"^Place '(?P<name>[^']+)':") | |
| tags_re = re.compile(r"^\s+tags:\s+(?P<tags>.*)$") | |
| places = {} | |
| current = None | |
| for line in sys.argv[1].splitlines(): | |
| m = place_re.match(line) | |
| if m: | |
| current = m.group("name") | |
| places[current] = {} | |
| continue | |
| if current is None: | |
| continue | |
| m = tags_re.match(line) | |
| if m: | |
| for kv in m.group("tags").split(","): | |
| kv = kv.strip() | |
| if "=" in kv: | |
| k, v = kv.split("=", 1) | |
| places[current][k.strip()] = v.strip() | |
| repo_root = os.environ.get("GITHUB_WORKSPACE", ".") | |
| avail = [] | |
| for name, tags in sorted(places.items()): | |
| env_yaml = f"test/hw/env/{name}.yaml" | |
| if os.path.exists(os.path.join(repo_root, env_yaml)): | |
| avail.append({ | |
| "place": name, | |
| "env_remote": env_yaml, | |
| "daughter_board": tags.get("daughter-board", ""), | |
| "carrier": tags.get("carrier", ""), | |
| }) | |
| else: | |
| print( | |
| f"skip: place {name!r} (tags={tags}) — {env_yaml} not in repo", | |
| file=sys.stderr, | |
| ) | |
| print("available_nodes=" + json.dumps(avail)) | |
| PY | |
| hw-coord: | |
| needs: preflight | |
| if: needs.preflight.outputs.available_nodes != '[]' | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| node: ${{ fromJSON(needs.preflight.outputs.available_nodes) }} | |
| name: hw-coord (${{ matrix.node.place }}) | |
| # Per-place runner. `hw-<place>` is the convention from | |
| # adi-labgrid-plugins; the host running the runner must have MATLAB, | |
| # ssh access to lab hosts as needed, and reach the coordinator. | |
| runs-on: [self-hosted, "hw-${{ matrix.node.place }}"] | |
| timeout-minutes: 40 | |
| concurrency: | |
| group: hw-coord-${{ matrix.node.place }} | |
| cancel-in-progress: false | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| # +adi/+common is a git submodule (ToolboxCommon, shared | |
| # across ADI MATLAB toolboxes); MATLAB can't resolve | |
| # adi.common.RxTx without it. | |
| submodules: recursive | |
| - name: Bootstrap uv | |
| run: bash .github/scripts/bootstrap-uv.sh | |
| - name: Install trx HW venv (adi-labgrid-plugins[kuiper] + pytest) | |
| run: bash .github/scripts/install-trx-hw-venv.sh | |
| - name: Acquire coordinator place | |
| env: | |
| LG_COORDINATOR: ${{ env.COORDINATOR }} | |
| run: | | |
| set -euo pipefail | |
| LGCLIENT="$HOME/.cache/trx-hw-ci/venv/bin/labgrid-client" | |
| "$LGCLIENT" -x "$LG_COORDINATOR" -p "${{ matrix.node.place }}" acquire | |
| - name: Run coordinator-mode tests | |
| env: | |
| LG_COORDINATOR: ${{ env.COORDINATOR }} | |
| LG_ENV: ${{ github.workspace }}/${{ matrix.node.env_remote }} | |
| # HW_DAUGHTER drives adi_lg_plugins.pytest_plugin's | |
| # iio_hardware-marker filter: tests whose | |
| # @pytest.mark.iio_hardware([...]) doesn't include this chip | |
| # are auto-skipped. | |
| HW_DAUGHTER: ${{ matrix.node.daughter_board }} | |
| HW_CARRIER: ${{ matrix.node.carrier }} | |
| # MATLAB binary; override per-lab via vars.MATLAB_BIN. | |
| MATLAB_BIN: ${{ vars.MATLAB_BIN || 'matlab' }} | |
| run: | | |
| set -euo pipefail | |
| # The bridge tests in test/hw/test_*.py subprocess MATLAB. | |
| # Bridge skips when iio_uri unresolved (conftest no-op); | |
| # marker filter skips tests whose chip != $HW_DAUGHTER. | |
| "$HOME/.cache/trx-hw-ci/venv/bin/pytest" \ | |
| -v -s -p no:cacheprovider \ | |
| test/hw/ \ | |
| --junitxml=junit-hw-coord-${{ matrix.node.place }}.xml | |
| - name: Release coordinator place | |
| if: always() | |
| env: | |
| LG_COORDINATOR: ${{ env.COORDINATOR }} | |
| run: | | |
| set -euo pipefail | |
| LGCLIENT="$HOME/.cache/trx-hw-ci/venv/bin/labgrid-client" | |
| "$LGCLIENT" -x "$LG_COORDINATOR" -p "${{ matrix.node.place }}" release || true | |
| - name: Publish hw-coord test results to PR | |
| if: always() | |
| uses: dorny/test-reporter@v1 | |
| with: | |
| name: Tests (hw-coord ${{ matrix.node.place }}) | |
| path: junit-hw-coord-${{ matrix.node.place }}.xml | |
| reporter: java-junit | |
| fail-on-error: false | |
| - name: Upload JUnit + MATLAB artifacts | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: junit-hw-coord-${{ matrix.node.place }} | |
| path: | | |
| junit-hw-coord-${{ matrix.node.place }}.xml | |
| ${{ matrix.node.daughter_board && format('*{0}*_HWTestResults.xml', matrix.node.daughter_board) || '*_HWTestResults.xml' }} | |
| failures.txt | |
| retention-days: 14 | |
| if-no-files-found: ignore | |
| publish-pr-test-summary: | |
| name: PR Test Summary | |
| needs: [hw-coord] | |
| if: always() && github.event_name == 'pull_request' && needs.hw-coord.result != 'skipped' | |
| runs-on: [self-hosted, hw-coordinator] | |
| steps: | |
| - uses: actions/download-artifact@v4 | |
| with: | |
| pattern: junit-hw-* | |
| path: junit | |
| merge-multiple: true | |
| - uses: EnricoMi/publish-unit-test-result-action@v2 | |
| with: | |
| files: 'junit/**/*.xml' | |
| check_name: 'Hardware Test Results' | |
| comment_mode: always | |
| report_individual_runs: true |