Skip to content

feat(hw-ci): migrate hardware-test workflow to hw-matrix.yml@v2 - #749

Draft
tfcollins wants to merge 24 commits into
mainfrom
feature/gha-hw-v2-migration
Draft

feat(hw-ci): migrate hardware-test workflow to hw-matrix.yml@v2#749
tfcollins wants to merge 24 commits into
mainfrom
feature/gha-hw-v2-migration

Conversation

@tfcollins

Copy link
Copy Markdown
Collaborator

Summary

Migrate pyadi-iio HW-CI off the manifest-driven hw-matrix.yml@v1 and
onto the new discovery-driven hw-matrix-v2.yml shipped in
tfcollins/labgrid-plugins#16.

This PR is stacked on #747 (feature/gha-hw-workflow). It will move
to base main after #747 merges. Keep #747 as the v1 landing pad so the
new workflow has a working consumer to diff against during review.

What changes

  • .github/workflows/hardware-test.yml — now calls
    tfcollins/labgrid-plugins/.github/workflows/hw-matrix-v2.yml. No
    manifest_path; marker_filter: iio_hardware instead.
  • Removed: .github/hw-nodes.json, test/hw/env/{bq,mini2,nemo}.yaml.
    The reusable workflow renders the per-shard labgrid env yaml on the
    fly from each place's boot-strategy tag.
  • .gitignore — drop the !test/hw/env/ exception line (no longer
    needed; the dir is gone).
  • test/hw/test_*_smoke.py — every test now wears
    @pytest.mark.iio_hardware([<daughter>...]). AD9371 uses the
    ["adrv9371", "ad9371"] alias list. Matchback against the live
    coordinator's daughter-board= tag.
  • test/hw/README.md — rewritten to describe the discovery model and
    drop the manifest/env-yaml story.

Tag contract the lab needs

For each live place (mini2 / bq / nemo / …) the coordinator admin sets:

labgrid-client -p <place> set-tags carrier=<carrier> \
    daughter-board=<daughter> boot-strategy=<strategy-class-name>

Concrete current contract per #16's docs:

place carrier daughter-board boot-strategy
mini2 zcu102 ad9081 BootFPGASoC
bq zc706 adrv9371 BootFPGASoCSSH
nemo zcu102 adrv9009 BootFPGASoC

The workflow validates these on every run; an unknown
boot-strategy= value fails the discover job with a clear error
listing the legal class names.

Empty-intersection behavior

If no live place advertises a daughter-board that any marker'd test
asks for, the workflow emits a ::warning:: annotation and discover
succeeds with an empty matrix — no jobs are spawned and the run is
green. Concrete example: if all three lab boards are offline, the run
finishes with a single annotation rather than a wall of failed shards.

Stacking / merge order

  1. tfcollins/labgrid-plugins#16 merges → tag v2 on that repo.
  2. This PR's workflow uses: ref flips from
    @feature/hw-matrix-v2-discovery to @v2, and the pip install
    line for adi-labgrid-plugins flips from @feature/... to @v2.
  3. feat(ci): GHA hardware-test workflow alongside JenkinsfileHW #747 merges (still on v1; no behaviour change required).
  4. This PR rebases on main, gets reviewed, merges.

Test plan

  • CI: PR-label-gated hardware-test.yml skip path stays SKIPPED
    when the hw-test label is absent (matches today's behaviour).
  • CI: with the hw-test label, discover job emits a non-empty
    matrix and hw shards run on the live coordinator. Tag the live
    coordinator places with the contract above before adding the
    label.
  • Jenkins's Tests (hw-coord bq) / Tests (hw-coord mini2) checks
    remain green and independent.
  • Each marker'd test passes pytest --collect-only -m iio_hardware
    locally inside a fresh venv: pip install -e .[dev] git+https://github.com/tfcollins/labgrid-plugins@feature/hw-matrix-v2-discovery.

tfcollins added 6 commits May 14, 2026 00:55
Net-new GH Actions HW pipeline calling the shared hw-matrix reusable
workflow in tfcollins/labgrid-plugins. Runs alongside the existing
JenkinsfileHW for at least two weeks during the migration period; do
not delete the Jenkins config until result delta is understood.

Initial scope: 2 places (pluto, fmcomms2-zcu102), coord leg only,
labeled-PR + nightly cron + manual triggers. Add boards by extending
.github/hw-nodes.json and writing test/hw/test_<board>_smoke.py.

New artifacts:
  - .github/workflows/hardware-test.yml: thin caller of
    tfcollins/labgrid-plugins/.github/workflows/hw-matrix.yml@v1.
  - .github/hw-nodes.json: per-place manifest consumed by hw-matrix.
  - test/hw/conftest.py: 'iio_uri' session fixture that resolves a
    libiio URI from either --iio-uri-override (laptop runs) or
    LG_COORDINATOR + LG_PLACE (set by the workflow before pytest).
  - test/hw/test_pluto_smoke.py, test/hw/test_ad9361_smoke.py:
    minimal smoke tests that open a context and capture a buffer.
    Independent of test/conftest.py and its pytest-libiio fixtures.
  - test/hw/env/{pluto,fmcomms2_zcu102}.yaml: coord-mode env stubs.
  - test/hw/README.md: onboarding for adding boards.

The existing JenkinsfileHW and test/test_*.py are untouched.
The subprocess argument list in _labgrid_show was a single 96-char line;
the repo's pinned black==19.10b0 (via pre-commit) splits long literal
lists one element per line. No behavior change.
Replace placeholder pluto / fmcomms2-zcu102 manifest entries (which
don't exist on coordinator 10.0.0.41) with the actual hardware
behind the lab:

  - mini2 (ZCU102 + AD9081)   -> adi.ad9081  smoke
  - bq    (ZC706 + ADRV9371)  -> adi.ad9371  smoke
  - nemo  (ZC706 + ADRV9009)  -> adi.adrv9009 smoke

Each smoke instantiates the device class via the iio_uri fixture
(which extracts the labgrid place's NetworkService address) and
runs two assertions:
  - ctx.attrs is populated (real libiio context handshake)
  - rx() returns a non-empty buffer (real DMA roundtrip)

No fmcdaq3 class in adi/ yet, so nuc/fmcdaq3 is deferred. Tests
deliberately don't .skip() on connection failure — if a DUT is
offline the test FAILS (which is the right signal, not silent).
Two changes:
  * Replace 'python -m labgrid.remote.client' with the venv's
    labgrid-client script. The module-style invocation against the
    pinned tfcollins/labgrid SHA swallows stdout in some paths, which
    made every test silently skip with "Place" not found.
  * Drop the silent skip — pytest.fail instead, so missing places or
    missing addresses are loud red. The reusable workflow's
    acquire-place composite has already acquired the place; if show
    can't find it now, something is genuinely wrong.

Also always echo the raw 'labgrid-client show' stdout/stderr to
test-stderr so a CI re-run doesn't require another debug roundtrip.
The tfcollins/labgrid fork prints NetworkService params as Python repr
('address': '10.0.0.23'), not flat key-value (address: 10.0.0.23).
The line-prefix matcher missed both; regex now captures either form.
Stacks on PR #747 (feature/gha-hw-workflow). Once that merges, this
PR rebases onto main and lands the v2 migration.

Discovery-driven flow lives entirely in labgrid-plugins (PR #16). On
the consumer side this PR is a deletion-heavy diff:

  * Delete .github/hw-nodes.json (the v1 static per-place manifest).
  * Delete test/hw/env/{mini2,bq,nemo}.yaml (v1 per-place labgrid env
    files). The reusable workflow renders the env yaml on the fly
    from the place's `boot-strategy` tag.
  * Drop the !test/hw/env/ exception line in .gitignore.
  * Update .github/workflows/hardware-test.yml: switch the `uses:`
    ref to hw-matrix-v2.yml; drop manifest_path + legs inputs; add
    marker_filter=iio_hardware + test_root=test/hw; install
    adi-labgrid-plugins in the venv (it provides both the marker
    plugin and the adi-lg-hw-ci CLI the workflow shells out to).
  * Add @pytest.mark.iio_hardware(["..."]) decorators to the three
    smoke tests so the v2 matrix discovers them. Carrier-narrowing
    not used yet — these tests don't depend on a specific carrier.
  * Rewrite test/hw/README.md to describe the v2 model (no manifest,
    no env yaml, marker-driven, lab-admin sets tags).

Workflow ref pinned to
labgrid-plugins@feature/hw-matrix-v2-discovery until that PR merges
and the v2 tag is cut. Flip to @v2 in a follow-up commit before this
PR merges.

Test plan:

  * actionlint .github/workflows/hardware-test.yml → clean.
  * pytest --collect-only -m iio_hardware against test/hw/ in a venv
    with adi-labgrid-plugins installed → 6 tests collected; the
    --hw-ci-export-markers JSON correctly shapes each entry as
    {iio_hardware: [...], iio_carrier: []}.
  * Once the lab admin tags mini2 / bq / nemo on the live
    10.0.0.41:20408 coordinator, dispatching this workflow should
    produce one matrix shard per (place, daughter-board) pair and
    skip-with-annotation any board that isn't live.

Migration status:

  * v1 (hw-matrix.yml@v1) stays in labgrid-plugins indefinitely for
    any consumer that hasn't migrated yet.
  * The legacy JenkinsfileHW pipeline is untouched.
@github-actions

github-actions Bot commented May 14, 2026

Copy link
Copy Markdown

Test Results

1 818 tests  ±0     508 ✅ ±0   8m 15s ⏱️ +40s
    1 suites ±0   1 310 💤 ±0 
    1 files   ±0       0 ❌ ±0 

Results for commit 15808a6. ± Comparison against base commit e0d811d.

♻️ This comment has been updated with latest results.

@tfcollins tfcollins added the hw-test Enable hardware testing label May 14, 2026
tfcollins added 18 commits May 14, 2026 10:43
No code change. The labgrid-plugins reusable workflow at
@feature/hw-matrix-v2-discovery has been updated (PR #16) so the
`discover` job pins to `hw-coordinator` and each `hw` shard pins to
`hw-<place>` — matching the lab's real self-hosted runner labels.

The prior run (25865035443) sat in `queued` indefinitely because the
old default `hw-coord` matched no runner. This commit re-fires the
workflow against the fixed reusable workflow.
labgrid's pytest plugin registers --lg-env. The v2 caller had a typo
that pytest rejected with 'unrecognized arguments: --lg-config' before
any test ran.
BootFPGASoC.__attrs_post_init__ unconditionally calls
KuiperDLDriver.get_boot_files_from_release() which imports pytsk3.
pytsk3 is declared as an optional extra in adi-labgrid-plugins
(see pyproject.toml [kuiper]); without it, every shard's boot step
crashes with ModuleNotFoundError before pytest runs.

Tack [kuiper] on the install line so uv pulls pytsk3 in.
…covery

Previously the conftest fell back to a labgrid-show lookup whenever
--iio-uri-override wasn't passed. That meant the test could end up
poking whichever board the coordinator advertised — and we just learned
the hard way (mini2 ran into this) that NetworkService.address goes
stale across DHCP renewals.

Make the URI an explicit pytest CLI argument:

* hardware-test.yml's pytest_cmd_template adds
  `--iio-uri-override "$IIO_URI_OVERRIDE"`. The hw-matrix-v2 workflow
  resolves the IP from the live board's serial console and exports
  IIO_URI_OVERRIDE before pytest runs.
* test/hw/conftest.py drops the labgrid auto-discovery path entirely.
  If no URI was provided, the fixture skips with a clear message
  instead of silently connecting to whatever the coordinator says.

This eliminates the "what if I'm testing a random board on the LAN?"
class of failure. The workflow IS the contract.
@tfcollins
tfcollins force-pushed the feature/gha-hw-workflow branch 3 times, most recently from 170cee3 to a8698c8 Compare May 20, 2026 20:28
Base automatically changed from feature/gha-hw-workflow to main May 20, 2026 21:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

hw-test Enable hardware testing

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant