Export and import the whole configuration as one declarative file (#23) #122
No reviewers
Labels
No labels
area/ai
area/backend
area/frontend
area/infra
area/scheduler
area/wled
good-first-issue
priority/high
priority/low
priority/medium
type/bug
type/chore
type/ci-cd
type/docs
type/feature
type/qa
v1.0.0
v1.1.0
v1.2.0
v2.0.0
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
rbrooks/Iris-WLED!122
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/23-config-bundle"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #23.
GET /export/configproduces a versioned YAML bundle of settings, segments, user events and curated schemes.POST /import/configapplies one, taking a backup first and running in a single transaction, so a bad file cannot half-migrate an instance.What's deliberately not in it
The feed token is excluded structurally rather than by a filter: settings serialise through
RuntimeSettings, and the token isn't one of its fields, so there's no path by which it reaches the file even if someone later adds it to the settings table. There's a test asserting the token doesn't appear in the output.Determinism is the point of the format
Collections sort by a stable key and YAML is emitted with sorted keys, so an unchanged instance re-exports byte-identically and a real change produces a small diff.
One non-obvious choice: segments sort by name, not
sort_order.sort_orderis itself editable, so sorting by it would reshuffle the whole file whenever someone reordered the UI list — a diff that says nothing.On the round-trip test, which is weaker than it looks
The obvious test is export → wipe → import → export → compare. I wrote it, and then checked whether it actually catches a dropped field by deleting
effect_speedfrom the exporter.It stayed green. A field missing from the exporter is missing from both bundles, so they still compare equal. It proves stability — import understands everything export writes — not completeness.
I'd written "catches a dropped field that per-field assertions would miss" in the docstring. That was wrong, and it's now corrected to say what the test actually does.
Completeness is a separate
TestExportCoveragethat walks the SQLAlchemy columns and fails when a model gains a field the exporter doesn't carry, with an explicit allow-list of deliberate omissions (surrogate keys, timestamps,stale). Witheffect_speeddeleted, that one fails as it should. It's the test that will catch the next person adding a column.This is the third time this session a test has turned out to agree with the code rather than with reality — after the
leds.segsfixture and the preset mock aliasing.Other behaviour worth noting
source: user— #99 promotes on edit, so an edited-but-unapproved scheme is real work and dropping it would lose exactly what the bundle exists to preserve.set_all_settings, so an import gets the same validation and unknown-key rejection the API applies rather than a second, looser path.Verification
ruff,mypycleanalembic checkreports no driftuvx pre-commit run --all-filespasses🤖 Generated with Claude Code