Skip to content

feat(updater): give CEC Support a self-updater - #49

Open
mrjeeves wants to merge 2 commits into
mainfrom
claude/allmystuff-cecsupport-auto-updates-r6rjxi
Open

feat(updater): give CEC Support a self-updater#49
mrjeeves wants to merge 2 commits into
mainfrom
claude/allmystuff-cecsupport-auto-updates-r6rjxi

Conversation

@mrjeeves

Copy link
Copy Markdown
Owner

Why

CEC Support had no update mechanism at all — this is a new feature, not a repair.

It already kept the engine beneath it current: on bring-up it asks a reused, separately-installed allmystuff-serve to update itself to ALLMYSTUFF_PIN, and AllMyStuff in turn asks myownmesh to update to its pin, each layer falling back to its own bundled sidecar when the update can't be had. But nothing ever updated CEC Support itself. A repo-wide grep for updater|self_update|check_now|tick_forever|releases/latest across every .rs/.ts/.svelte/.toml returned zero hits; the version in Settings was appVersion() — a label read from the bundle, no network call, no comparison.

This adds the missing top of that chain, so the whole stack moves forward instead of only its lower two thirds.

cec-support-updater

Modelled on allmystuff-updater, but self-contained and in the light root workspace, so cargo test here still builds with no webview or media stack. Self-contained rather than a git dep on AllMyStuff's updater because a tag dep can only ever carry already-released code — the same call allmystuff-updater made when it was ported from myownmesh-updater. Each app in the ecosystem carries its own.

  • One artifact (the cec-support binary).
  • Fail-closed verification: a published SHA-256 sidecar is mandatory; a valid detached minisign signature is required once CEC_RELEASE_PUBKEY is baked in.
  • Stage-then-apply — the swap happens at the start of the next launch, since a running executable can't reliably replace itself.
  • policy.rs ported verbatim, so a patch/minor/major bump means the same thing across all three apps.
  • Downgrade guard: a stale pending.json naming an older version is discarded rather than applied.

It carries over the fixes made in AllMyStuff#198 rather than repeating the bugs: Program Files is not treated as a package manager (that's where our own MSI lands), writability is probed at runtime, a package-managed install still checks and reports ManualUpdateAvailable, the interval is stamped only after a successful fetch, and every outcome is logged.

Wiring

  • apply_pending_if_any() runs first in main, right after the CEC home is resolved.
  • The ticker is spawned in setup and emits update://checked, so a release found in the background reaches the customer without them opening Settings — a background task holds no handle to the UI, which is precisely how an updater ends up looking like it never runs.
  • Five Tauri commands (update_status, update_check, update_apply, update_relaunch, update_set_prefs).
  • A new Updates card in Settings, written for a customer rather than a technician: a plain version line, a "Check now" button, an on/off toggle, and — when something is staged — an inline Restart and update.

Release artifacts

release.yml published only *-setup.exe and .msi. A self-updater can't apply either; it swaps a binary. Without a plain archive every background check would find a newer release and then fail to stage it. It now also packages cec-support-windows-x86_64.zip plus the mandatory .sha256 sidecar, named to match platform_asset().

Testing

cargo test --workspace — 22 pass (17 new). cargo clippy --workspace --all-targets -- -D warnings and cargo fmt --all --check clean. pnpm check clean (106 files, 0 errors) and pnpm build succeeds. Release workflow YAML parses with the steps in the intended order.

The Tauri GUI crate does not compile in this sandbox — as its own manifest notes, it depends on AllMyStuff's node engine (webview + media stack) via git. The frontend is what this repo validates, and CI covers the rest.

One thing worth a look

CEC pins a specific AllMyStuff version (.allmystuff-rev) and asks a reused node to match it. Now that CEC can also move itself forward, a CEC update carries a new pin with it — which is the intended behaviour, but it does mean the pin is what keeps the two halves compatible. Worth confirming that matches how you want the ecosystem to move.


Generated by Claude Code

claude added 2 commits July 29, 2026 07:14
CEC Support had no update mechanism at all. It kept the engine *beneath*
it current — on bring-up it asks a reused `allmystuff-serve` to move to
the version CEC pins, and AllMyStuff in turn asks `myownmesh` to update
to its pin, each layer falling back to its own bundled sidecar — but
nothing ever updated CEC Support itself. The version in Settings was a
label read from the bundle, not a check: no release feed, no timer, no
network call.

This adds the missing top of that chain, so the whole stack moves forward
instead of only its lower two thirds.

`cec-support-updater` is modelled on `allmystuff-updater` but
self-contained, in the light root workspace so `cargo test` here still
builds with no webview or media stack. Self-contained rather than a git
dep on AllMyStuff's updater, because a tag dep can only carry
already-released code — the same call `allmystuff-updater` made when it
was ported from `myownmesh-updater`. One artifact (the `cec-support`
binary), fail-closed verification (a published SHA-256 is mandatory;
minisign required once a key is baked in), stage-then-apply on next
launch, and the shared apply-policy semantics so a patch bump means the
same thing across all three apps.

It carries over what was just fixed in AllMyStuff rather than repeating
it: Program Files is not treated as a package manager (that is where our
own MSI lands), writability is probed at runtime, a package-managed
install still checks and reports `ManualUpdateAvailable`, the interval is
stamped only after a successful fetch, and every outcome is logged.

Wiring: `apply_pending_if_any()` runs first in `main` (after the CEC home
is resolved), the ticker is spawned in setup and emits `update://checked`
so a found release reaches the customer without them opening Settings,
five Tauri commands back a new Updates card, and a staged update offers
"Restart and update" inline.

release.yml only published installers, which a self-updater cannot apply
— it swaps a binary. It now also packages the portable
`cec-support-windows-x86_64.zip` plus the mandatory `.sha256` sidecar,
named to match `platform_asset()`.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FMPNLjvNYQCeR3xGfdDUCg
When a log file is available both sinks correctly get `with_ansi(false)`.
The no-log-file path fell through to `builder.init()`, which leaves
tracing-subscriber's default of ANSI unconditionally ON — it does not
check for a TTY.

That path is the one a service takes when the CEC home isn't writable, so
its stdout — captured by journald/rsyslog — reached the system log wrapped
in escape sequences, bloating every line and breaking grep.

Colour is now gated on `std::io::stdout().is_terminal()`.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FMPNLjvNYQCeR3xGfdDUCg
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants