refactor(ci): replace inline fixture-collector bash with a typed, tested script - #3
Conversation
The "Collect fixtures" step was ~90 lines of inline bash (find + sed + grep + jq) with no type checking or tests. Extract it to a single-file, dependency-free Python script run by `uv` (PEP 723), gated by a new `check-script` job: ruff, ruff format --check, mypy --strict, pytest. The build job now `needs: check-script`. The script keeps a pure functional core (parsing, transport/group classification, canonical naming, manifest shaping) with a thin IO shell (directory listing, copies, manifest write). Artifact kinds are a tagged union (Exe | MergedHex | Hex | Elf) chosen by exhaustive match, so the launch command, mcuboot flag, and qemu load flag all derive from one selected image rather than from re-sniffing strings. Manifest output is unchanged for all real Twister output (validated by an end-to-end `uv run` plus 36 unit/integration tests). Latent bash bugs are fixed in passing, none observable on real inputs: - merged.hex no longer silently overrides a co-present .exe - `run` derives from the Exe artifact, not from whichever branch set it - qemu load flag is chosen by image kind, not by sniffing the filename - a build-dir name without exactly one dot now errors, not mis-slices - multiple merged_*.hex now errors instead of silently taking the first Also bump out-of-date action pins: astral-sh/setup-uv@v8 (new), and softprops/action-gh-release@v2 -> v3 (a Node 20 -> 24 runtime bump, safe on ubuntu-24.04). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR refactors the CI “Collect fixtures” logic by replacing a large inline bash script in the build workflow with a typed, dependency-free Python script (PEP 723, run via uv), and adds a dedicated CI job to lint, type-check, and test that script before the build runs.
Changes:
- Add
.github/scripts/collect_fixtures.pyto collect Twister outputs into canonically named fixtures and emit a sorted JSON manifest. - Add
.github/scripts/test_collect_fixtures.pyplus.github/scripts/pyproject.tomlto enforceruff,mypy --strict, andpytestcoverage for the script. - Update
.github/workflows/build.yamlto introduce acheck-scriptgating job, run the collector viauv, and bump the release action pin.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
.github/workflows/build.yaml |
Adds check-script, gates build on it, switches fixture collection to uv run ...collect_fixtures.py, and updates the release action pin. |
.github/scripts/collect_fixtures.py |
New typed fixture collector script implementing naming, artifact selection, manifest shaping, and IO. |
.github/scripts/test_collect_fixtures.py |
New unit/integration tests defining the behavioral contract for the collector. |
.github/scripts/pyproject.toml |
Tooling config for ruff/mypy/pytest for the script + tests. |
.github/scripts/.gitignore |
Ignores local Python tooling caches for the scripts directory. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
astral-sh/setup-uv publishes moving major tags only up to v7; v8 ships as v8.0.0 / v8.1.0 with no v8 alias, so `@v8` failed to resolve on the runner. Pin the exact latest release. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Addresses PR review feedback: - check-script ran pytest on the runner's default interpreter (3.12 on ubuntu-24.04); pin UV_PYTHON=3.13 so the gate tests the same runtime the script targets (requires-python >=3.13) and the build leg uses. - select_artifact now raises when a build dir carries both a native_sim .exe and a merged MCUboot image -- different target classes that never coexist -- instead of silently preferring merged. Legitimate sysbuild dirs (merged.hex + zephyr.hex + zephyr.elf together) are unaffected and still resolve via the merged > exe > hex > elf ladder. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Thanks — addressed both in f7c75c7:
|
Resolve the build.yaml collect-step conflict: keep the typed-script invocation (uv run collect_fixtures.py) and take main's mps2/an385 matrix addition. main's two new commits add a runnable mps2/an385 serial-recovery fixture whose bootable image is MCUboot itself (mcuboot/zephyr/zephyr.elf), with the app shipped as a .signed.bin. Port that into the typed collector: select_artifact gains a lowest-priority mcuboot/zephyr/zephyr.elf fallback (after zephyr.elf), matching main's merged bash precedence -- without it the new fixture would be silently dropped from the manifest. serial_recovery.nrf52840dk keeps MERGED_HEX_FILES, so it still resolves to merged.hex (unchanged). Verified: ruff, mypy --strict, pytest (40, +3 recovery-path tests), and a uv-run smoke test reproducing main's manifest entry for the mps2 recovery fixture (qemu_cmd byte-identical, mcuboot=false, serial_recovery=true). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
What
Replaces the ~90-line inline bash in the Collect fixtures step with a single-file, dependency-free Python script (.github/scripts/collect_fixtures.py) run by
uv(PEP 723), plus a new check-script CI job that gates it onruff,ruff format --check,mypy --strict, andpytest(36 tests). Thebuildjob nowneeds: check-script.Why
The step was untyped, untested string-munging (
find+sed+grep+jq). The rewrite is a pure functional core (parsing, transport/group classification, canonical naming, manifest shaping) with a thin IO shell (listing, copies, manifest write), and an exhaustively-matched tagged-union artifact model (Exe | MergedHex | Hex | Elf).Behaviour
Manifest output is unchanged for all real Twister output — verified by an end-to-end
uv runagainst a synthetictwister-out(the emittedqemu_cmdis byte-identical to the oldjqform) and by the unit/integration tests. Latent bash bugs fixed in passing, none observable on real inputs:merged.hexsilently overriding a co-present.exerunpointing at whichever path last set$artifactinstead of the executableconfig/targetmerged_*.hexThe first two are mutually-exclusive in practice (native_sim vs sysbuild), so the change is behaviour-preserving; the rewrite just makes them structurally impossible.
Also — out-of-date action pins
astral-sh/setup-uv@v8(new)softprops/action-gh-release@v2→@v3(Node 20 → 24 runtime bump; safe onubuntu-24.04)checkout/setup-python/upload-artifact/download-artifactare already at their current majors — left as-is.Test plan
ruff checkandruff format --checkcleanmypy --strictclean (script + tests)pytest36/36 (incl. doctests + an end-to-endmain())uv runend-to-end manifest matches the oldjqoutputcheck-script+ both build legs (native_sim,arm) green on this PR🤖 Generated with Claude Code