feat(release): publish devices.json as Release asset on v* tag push - #16
Conversation
Adds .github/workflows/release.yml — on v* tag push, exports the PWA device bundle via scripts/export_web_devices.py and attaches devices.json to the tag's GitHub Release. Prerequisite for extracting the parsons-remote PWA into its own repo (homelab#256). Downstream repos fetch this artifact at build time so the JSON never has to be committed alongside its source YAML — derive-don't- duplicate stays enforced. Uses `gh release create --generate-notes || true` + `gh release upload --clobber` for a two-step idempotent publish that survives re-runs of the same tag without third-party actions to pin. Refs #15
…n sanity check Addresses code-reviewer feedback on the release workflow: 1. Silent-success class-of-bug (ansible changed=0 shape from memory.md): the sanity check only asserted `lights` was non-empty, so a subtly broken exporter (empty commands dict, PT2260 filter regression) would produce valid JSON with lights but wrong shape. Now runs the existing test_export_web_devices.py fence before building — 5s cost, closes the gap. 2. Sanity-check `python` was the system Python (currently 3.12 on ubuntu-latest), not the uv-managed 3.13 the rest of the workflow uses. Switched to `uv run python` so one interpreter runs everything; also tightened the shape assertion to check each entry has unit + non-empty commands. 3. `uv sync --frozen` bumped to `--all-groups` so pytest is available. Matches the fleet-standard pattern in ci.yml. Refs #15
|
Note: GHA minutes are exhausted for July per the 14:25 ET PM tick — this PR's own |
|
Looks good with one question: given a situation like right now where we are out of Github Action Minutes until August,
|
…elease.sh Tom's Q2 on #16: "can we move most of the logic into a bash script so it's do-able locally?" — yes. release.yml now runs setup steps (checkout, uv install, uv sync --frozen) then calls `bash scripts/publish-devices-release.sh`. The script owns the exporter regression fence, JSON build, JSON sanity check, and idempotent `gh release create || true` + `gh release upload --clobber` — one source of truth for CI and local runs. Local usage: TAG=v0.1.0 scripts/publish-devices-release.sh CI usage: TAG="$GITHUB_REF_NAME" scripts/publish-devices-release.sh Falls back to `git describe --tags --exact-match HEAD` when TAG is unset locally so a tagged HEAD works without ceremony. Refs #15
|
On the two questions: Q1 — versioning if the workflow fails: Bit of good news buried under the cap situation: radiofrequency is public, so it's on GitHub's unlimited-minutes tier for public repos. The July private-repo minute exhaustion doesn't touch this workflow — it will run on the first Even for the hypothetical "workflow failed for some other reason" case, the tag doesn't lose anything:
So worst case is "tag exists but no asset yet"; downstream fetches would 404 until re-run — same failure mode as a network blip during upload. No orphan releases, no version skew. Q2 — extract to a bash script: Done in 3727bc0. New file
Workflow now just does setup (checkout, uv install, Local invocation: TAG=v0.1.0 scripts/publish-devices-release.sh
# or, with a tagged HEAD checked out:
git checkout v0.1.0 && scripts/publish-devices-release.shThe Ready for merge whenever you are — you can cut |
Summary
Adds
.github/workflows/release.yml— on everyv*tag push, runs the exporter regression test, buildsdevices.jsonfromdevices/*.yaml, and attaches it to the tag's GitHub Release as an asset. Downstream consumers (the extractedparsons-remotePWA, next in the extraction sequence) fetch a stable, immutable URL at build time so the derived JSON never needs to be committed alongside its source YAML.Why now / how this fits
PR 1 of the 3-PR extraction of the current homelab
services/22-parsons-remote/PWA into its own repo — driven by homelab#256. Tom's picks on that scoping memo:devices.jsonflow: α — radiofrequency CI publishes the derived JSON on tag push, downstream fetches by version.parsons-remote.Design
on: push: tags: ['v*']— decoupled fromci.yml(which stays PR/main-push driven).gh release create --generate-notes || true+gh release upload --clobber. Re-runs on the same tag are safe.tests/test_export_web_devices.pyregression fence (shape / sort / JSON-serializability) so a subtle exporter break can't ship valid-JSON-but-wrong-shape. Fixes a code-reviewer finding aligned with the "ansiblechanged=0" class-of-bug in our own durables.contents: readat workflow scope;contents: writescoped to the publish job only.actions/checkout@v7.0.0SHA,astral-sh/setup-uv@v8.3.2SHA).ghis a pre-installed runner tool, no new action to pin.Test plan
uv run pytest -q— 65 passed (see inline showboat below).window,couch,speaker,chairs), 14 KB JSON.python -c "import yaml; yaml.safe_load(open(...))".v0.1.0tag onmain— that's the only way to exercise the tag-push trigger against the real GitHub Release API.Captured test run (showboat)
Issue #15 test run — release workflow local verification
2026-07-19T18:37:37Z by Showboat 0.6.1
uv run python -c 'import json; d = json.load(open("/tmp/rf-devices.json")); lights = d["lights"]; print(f"lights: {len(lights)}"); print("units:", [l["unit"] for l in lights])'Out of scope (explicit)
ci.yml— orthogonal; agent.md rule is "pin opportunistically when you touch a repo's CI" and this PR adds a new workflow rather than editing the existing one.v*tag — that's Tom's call after this lands.parsons-remoterepo, fetches the artifact at build) and PR 3 (homelab swaps the copy task for a release-artifact download, deletes the old tree) — filed as separate tracker issues.Closes #15
Refs homelab#256