Skip to content

feat(update): module-owned update channel + per-module auto-update (WordPress model) - #334

Merged
WebTigers merged 2 commits into
mainfrom
feat/update-provider
Oct 10, 2026
Merged

WebTigers merged 2 commits into
mainfrom
feat/update-provider

Conversation

@WebTigers

@WebTigers WebTigers commented Oct 10, 2026 •

Copy link
Copy Markdown
Owner

Two complementary, vendor-neutral additions to the Module Manager / Updates screen. Core never learns where any private bytes come from; every module stays visible and follows the same update data contracts.

1. Tiger_Update_Provider — a module may own its own update channel

A register() seam (like Tiger_Audience/Tiger_Search). A module registers {id, check, apply} for its slug:

  • check($slug, $installed): ?array → {latest, installed?, repository?, ref?} — core wraps it through the same _descriptor(), so the row is byte-identical to a GitHub one, just method: 'provider'.
  • apply($slug, $descriptor): array → {ok, version?, log?} — the same shape _applyOne returns.

Tiger_Update_Checker::modules() consults a registered provider before the Git path (so even a repo-less "discovered" row still lists in the Module Manager); System_Service_Updates::_applyOne() dispatches apply to the provider. Both fail-safe — a broken provider never flags a phantom update or throws into the screen.

Lets a module be distributed behind its own authority / signed-artifact feed without publishing that private mechanism in open-source core.

2. Per-module auto-update — the WordPress "Enable auto-updates" toggle

Opt-in and OFF by default, per module. An admin flips a switch on the Modules screen; the daily system.update_check job then applies new versions for those (and only those) unattended — everything else stays manual.

  • Tiger_Update_AutoUpdate — the opt-in set as one site-wide option (a slug list, like WP's auto_update_plugins).
  • System_Service_Modules::autoUpdate(slug, on) — the toggle action (admin+, beside activate/deactivate).
  • System_Service_Updates::runScheduledAutoUpdates() — headless, best-effort apply the scheduler calls; applies only opted-in slugs, one failure never aborts the rest, fully caught.
  • Modules screen gains a per-row Auto-update form-switch (role=switch, labelled + aria, in-app toast, reverts on failure); hidden for bundled/core modules.

Channel-agnostic: a module updated over GitHub, a provider, or any route opts in the same way.

Tests

  • Unit/Update/ProviderTest (registry + fail-safe check/apply).
  • Integration/Update/CheckerTest — a provider-owned, repo-less module surfaces with method: 'provider'.
  • Integration/Update/AutoUpdateTest — the opt-in round-trip.
  • Full unit suite green locally (1039 tests); CAPABILITIES.md + CHANGELOG updated.

Companion (out of scope here)

The private client that registers a provider — pointing TigerPanel/TigerBilling at the WebTigers distribution authority (TigerDistributor, TIGER-286) — lives entirely outside core. This PR is only the neutral seam + the auto-update toggle.

🤖 Generated with Claude Code

https://claude.ai/code/session_01ASauLLscjqdsNqBNsx2Typ

WebTigers and others added 2 commits October 10, 2026 03:54
… channel

A neutral, vendor-agnostic seam so a module can be distributed behind its own
authority / signed-artifact feed while the Module Manager keeps treating it
like any other module. Core never learns where the bytes come from.

Why: free-but-private platform modules (e.g. TigerPanel, TigerBilling) ship on
customer-controlled boxes that must not hold a GitHub token and aren't licensed,
so neither the GitHub channel (installFromUrl) nor the licensed-authority channel
fits. Rather than teach core about any particular private scheme — which would
publish a private mechanism in open-source core — this adds a generic extension
point: a module registers a provider; the provider owns detection + apply; the
Updates screen renders and applies it through the SAME data contracts.

- Tiger_Update_Provider: register($slug, {id, check, apply}) like Tiger_Audience/
  Tiger_Search. check()->{latest, installed?, repository?, ref?} ; apply()->{ok,
  version?, log?}. Both dispatchers are fail-safe (a broken provider never flags a
  phantom update nor bubbles an exception).
- Tiger_Update_Checker::modules(): consults a registered provider before the Git
  path, so even a repo-less "discovered" row still lists — as a standard descriptor
  with method 'provider'.
- System_Service_Updates::_applyOne(): dispatches a provider-owned update to the
  provider (before the Git path + the licensed gate).
- Tests: Unit/Update/ProviderTest (registry + fail-safe check/apply); an
  Integration/Update/CheckerTest case proving a provider-owned repo-less module
  surfaces. CAPABILITIES.md + CHANGELOG updated.

Companion: the private client that registers a provider (talking to the WebTigers
distribution authority, TigerDistributor) lives entirely outside core.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Claude-Session: https://claude.ai/code/session_01ASauLLscjqdsNqBNsx2Typ
…to-updates" toggle)

Opt-in and OFF by default, per module: an admin flips a switch on the Modules
screen for the modules they trust to update unattended; the daily update job
then applies new versions for those (and only those), leaving every other
module manual ("Update now" on the Updates screen). Channel-agnostic — a module
updated over GitHub, a provider, or any route opts in the same way.

- Tiger_Update_AutoUpdate: the opt-in set as one site-wide option (a slug list,
  like WordPress's auto_update_plugins). isOn/set/onSlugs, fail-safe.
- System_Service_Modules::autoUpdate(slug, on): the toggle action (admin+, beside
  activate/deactivate).
- System_Service_Updates::runScheduledAutoUpdates(): headless, best-effort apply
  the daily system.update_check job now calls after detection — applies only
  opted-in slugs; one item's failure never aborts the rest; wrapped so a bad run
  can't break the scheduler.
- Modules screen: a per-row Auto-update form-switch (role=switch, labelled +
  aria), reflecting the opt-in, posting to /api; in-app toast feedback, reverts
  on failure. Hidden for bundled/core modules (they update with the platform).
- Integration test for the opt-in round-trip; lang keys; CHANGELOG + CAPABILITIES.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Claude-Session: https://claude.ai/code/session_01ASauLLscjqdsNqBNsx2Typ
@WebTigers WebTigers changed the title feat(update): Tiger_Update_Provider — a module may own its own update channel feat(update): module-owned update channel + per-module auto-update (WordPress model) Oct 10, 2026
@WebTigers
WebTigers merged commit f1c4935 into main Oct 10, 2026
14 checks passed
@WebTigers
WebTigers deleted the feat/update-provider branch October 10, 2026 11:14
WebTigers added a commit that referenced this pull request Oct 10, 2026
…to-update (#335)

Rolls up #334: Tiger_Update_Provider (a module may own how it detects + applies
its own updates, via the neutral register() seam — same descriptor + apply
contract, so the Module Manager treats it like any other) and per-module
auto-update opt-in (the WordPress "Enable auto-updates" toggle; off by default;
the daily job applies only opted-in modules).


Claude-Session: https://claude.ai/code/session_01ASauLLscjqdsNqBNsx2Typ

Co-authored-by: Claude Opus 4.8 <[email protected]>
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.

1 participant