Repository navigation
feat(update): module-owned update channel + per-module auto-update (WordPress model) - #334
Merged
Merged
Conversation
… 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
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]>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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 channelA 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, justmethod: 'provider'.apply($slug, $descriptor): array→{ok, version?, log?}— the same shape_applyOnereturns.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_checkjob 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'sauto_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.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 withmethod: 'provider'.Integration/Update/AutoUpdateTest— the opt-in round-trip.unitsuite 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