Declarative full-config export / import (GitOps) #23

Closed
opened 2026-07-07 17:38:09 +00:00 by claude-bot · 1 comment
Contributor

Goal

Export the entire Iris configuration — user events, approved schemes, segments, and settings — as a single declarative file, and import it to rebuild or migrate an instance.

Why it's valuable

The existing export is WLED-presets.json-shaped (device state), not Iris state. A full config bundle enables version-controlling a setup, migrating between hosts, and disaster recovery of curated work. It complements the off-site backup issue (#18) at the config layer rather than the DB-file layer.

Sketch

  • GET /export/config → a versioned YAML/JSON bundle (events + schemes + segments + settings).
  • POST /import/config → validates and applies, creating a backup first (reuse the existing pre-op backup service).
  • Deterministic ordering so the file diffs cleanly in git.

Acceptance criteria

  • Round-trip: export → fresh instance → import reproduces events, schemes, segments, settings
  • Pre-import backup created
  • Stable, diff-friendly serialization
  • Tests for round-trip fidelity

Proposed enhancement (brainstorm follow-up).

## Goal Export the entire Iris configuration — user events, approved schemes, segments, and settings — as a single declarative file, and import it to rebuild or migrate an instance. ## Why it's valuable The existing export is WLED-`presets.json`-shaped (device state), not Iris state. A full config bundle enables version-controlling a setup, migrating between hosts, and disaster recovery of curated work. It complements the off-site backup issue (#18) at the config layer rather than the DB-file layer. ## Sketch - `GET /export/config` → a versioned YAML/JSON bundle (events + schemes + segments + settings). - `POST /import/config` → validates and applies, creating a backup first (reuse the existing pre-op backup service). - Deterministic ordering so the file diffs cleanly in git. ## Acceptance criteria - [ ] Round-trip: export → fresh instance → import reproduces events, schemes, segments, settings - [ ] Pre-import backup created - [ ] Stable, diff-friendly serialization - [ ] Tests for round-trip fidelity Proposed enhancement (brainstorm follow-up).
claude-bot added this to the v1.1.0 milestone 2026-07-15 15:32:56 +00:00
Author
Contributor

Done — #122 merged, CI green.

  • Round trip reproduces events, schemes, segments, settings
  • Pre-import backup created
  • Stable, diff-friendly serialization
  • Tests for round-trip fidelity

The round-trip test is weaker than it looks

Worth recording, because the obvious test for this feature does not do what it appears to.

Export → wipe → import → export → compare proves stability (import understands everything export writes) but not completeness. A field missing from the exporter is missing from both bundles, so they still compare equal. I checked this rather than assuming it, by deleting effect_speed from the exporter and watching the round-trip stay green.

I had already written the stronger claim into the test's docstring. That is corrected, and completeness is now a separate TestExportCoverage that walks the SQLAlchemy columns and fails when a model gains a field the exporter does not carry, with an explicit allow-list of deliberate omissions (surrogate keys, timestamps, stale). With effect_speed deleted, that one fails as it should.

This is the third time in this milestone a test has agreed with the code rather than with reality — after the leds.segs fixture (#62) and the preset mock aliasing (#115).

What the bundle excludes, and why

Builtin events regenerate per year, so exporting them adds rows that differ between instances by nothing that matters and conflict on import. Webhooks and the calendar feed token are credentials, and a file whose purpose is to be committed to a repository is the wrong home for those. Logs are history, not configuration.

The feed token is excluded structurally: settings serialise through RuntimeSettings, and the token is not one of its fields, so there is no path by which it reaches the file even if someone later adds it to the settings table. A test asserts it does not appear.

Determinism

Collections sort by a stable key and YAML is emitted with sorted keys. One non-obvious choice: segments sort by name, not sort_ordersort_order is itself editable, so sorting by it would reshuffle the whole file whenever someone reordered the UI list, producing a diff that says nothing.

Behaviour worth knowing

  • Schemes export when approved or promoted to source: user (#99 promotes on edit), so edited-but-unapproved work is preserved.
  • Import merges; restoring one host's bundle onto another does not delete the second host's events. Segments are the exception and are replaced wholesale, since they describe one physical strip.
  • A scheme whose event is absent is skipped and counted rather than written as a dangling row.
  • Settings route through set_all_settings, so imports get the same validation the API applies rather than a second, looser path.
Done — #122 merged, CI green. - [x] **Round trip reproduces events, schemes, segments, settings** - [x] **Pre-import backup created** - [x] **Stable, diff-friendly serialization** - [x] **Tests for round-trip fidelity** ### The round-trip test is weaker than it looks Worth recording, because the obvious test for this feature does not do what it appears to. Export → wipe → import → export → compare proves **stability** (import understands everything export writes) but not **completeness**. A field missing from the exporter is missing from *both* bundles, so they still compare equal. I checked this rather than assuming it, by deleting `effect_speed` from the exporter and watching the round-trip stay green. I had already written the stronger claim into the test's docstring. That is corrected, and completeness is now a separate `TestExportCoverage` that walks the SQLAlchemy columns and fails when a model gains a field the exporter does not carry, with an explicit allow-list of deliberate omissions (surrogate keys, timestamps, `stale`). With `effect_speed` deleted, that one fails as it should. This is the third time in this milestone a test has agreed with the code rather than with reality — after the `leds.segs` fixture (#62) and the preset mock aliasing (#115). ### What the bundle excludes, and why Builtin events regenerate per year, so exporting them adds rows that differ between instances by nothing that matters and conflict on import. Webhooks and the calendar feed token are credentials, and a file whose purpose is to be committed to a repository is the wrong home for those. Logs are history, not configuration. The feed token is excluded **structurally**: settings serialise through `RuntimeSettings`, and the token is not one of its fields, so there is no path by which it reaches the file even if someone later adds it to the settings table. A test asserts it does not appear. ### Determinism Collections sort by a stable key and YAML is emitted with sorted keys. One non-obvious choice: segments sort by **name, not `sort_order`** — `sort_order` is itself editable, so sorting by it would reshuffle the whole file whenever someone reordered the UI list, producing a diff that says nothing. ### Behaviour worth knowing - Schemes export when approved **or** promoted to `source: user` (#99 promotes on edit), so edited-but-unapproved work is preserved. - Import **merges**; restoring one host's bundle onto another does not delete the second host's events. Segments are the exception and are replaced wholesale, since they describe one physical strip. - A scheme whose event is absent is skipped and counted rather than written as a dangling row. - Settings route through `set_all_settings`, so imports get the same validation the API applies rather than a second, looser path.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
rbrooks/Iris-WLED#23
No description provided.