Marketplace: support an optional beta build per plugin - #157
DianaSensei wants to merge 1 commit into
Conversation
A market plugin can now advertise a beta build alongside its stable one —
same id, same label/description, a separate version + manifest URL(s)
nested under an optional `beta` field. Design constraint from the user:
only ONE version of a given plugin is ever installed at a time, regardless
of channel — picking the other channel replaces whatever's installed, it
never installs a second copy alongside it. So the identity/conflict rules
(assertNoConflictingInstall, findCoupledRecord, recordKey) needed no
changes at all — installing "the beta" is just installing from a different
URL for the same id, exactly like an update already works.
market.ts: MarketPluginVariant factors out the three fields needed to
actually install something (version, pluginManifestUrl, serviceManifestUrl)
— MarketPlugin embeds one directly (the stable build) and optionally a
second under `beta`. toMarketPlugin validates each independently: a
malformed `beta` sub-object only drops the beta option for that plugin, it
doesn't reject the whole catalog entry (same principle already applied to
a malformed entry in the plugins array).
SettingsMarketplace.tsx: MarketPluginCard now infers which channel is
currently installed by comparing the installed version against
plugin.version vs plugin.beta.version (no schema change needed on the
installed-record side — a plugin only ever has one record, whichever
channel it came from). Not installed + a beta exists → both "Install" and
"Install Beta" show up front. Installed → the primary button still does
its normal update-or-installed thing for whichever channel is running, and
a secondary "Switch to Beta"/"Switch to Stable" button offers the other
channel. installTarget now carries {plugin, channel} instead of just
plugin, so the confirm dialog resolves its URLs from the right variant.
Added a channel badge next to the version (visible only when the plugin
actually has a beta build, so plugins that never published one don't get
a "Stable" label nobody asked for), and switched the version shown to the
plugin's ACTUAL installed version rather than always the catalog's stable
version.
New i18n keys: installBeta, switchToBeta, switchToStable, channelStable,
channelBeta (vi copy intentionally distinct from en — "Stable"/"Beta" as
bare loanwords would trip i18n.test.ts's untranslated-pair check).
Tests: market.test.ts covers parsing/validating the beta field in
isolation; SettingsMarketplace.test.tsx covers the fresh-install two-button
case, installing beta resolves the beta URL (not stable's), and both
switch directions show the right button/hide the wrong one. Verified each
new assertion by temporarily reverting the corresponding logic and
confirming the test fails for the right reason, then restoring it.
This is the plugin/market half of beta-channel support — the app's own
update channel (DevTool itself) is a separate, larger piece of work for
later.
|
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #157 +/- ##
==========================================
+ Coverage 42.66% 42.71% +0.05%
==========================================
Files 300 300
Lines 19917 19942 +25
Branches 4933 4948 +15
==========================================
+ Hits 8498 8519 +21
- Misses 10402 10406 +4
Partials 1017 1017
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Important
The installed-channel inference misclassifies a beta that is behind the catalog's current beta as stable, producing a contradictory "Stable" badge and a primary Update to {stable} action that is actually a downgrade. Worth fixing before merge.
Reviewed changes
MarketPluginVariant+betafield —market.tsfactors the installable fields into a shared variant type and parsesbetavia the same validator, dropping only a malformed beta.- Channel-aware actions —
SettingsMarketplace.tsxthreads astable | betachannel throughinstallTarget/installUrls, andMarketPluginCardrenders Install/Install Beta, Update + Switch to Stable, or Update + Switch to Beta. - Channel badge — a Stable/Beta pill next to the installed version, shown only when the catalog advertises a beta.
- i18n + tests — new EN/VI strings; catalog-parse tests and install/switch UI tests.
ℹ️ Nitpicks
MarketPluginCard'sunsupportedwarning is computed fromplugin.targets(the stable variant) even when the card is on the beta channel or the user is about to install beta.plugin.beta.targetsis parsed inmarket.tsbut never consulted, so a beta built for a narrower target set won't get the card-level warning. The install dialog still reports the real per-manifest target support, so this is limited to the advisory warning.
Important
Pullfrog covered this run's model usage. DeepSeek Flash is fast and cheap — expect lighter work than a frontier model. This model allowance is temporary and separate from your Pullfrog plan. Connect a model-provider subscription or API key →
DeepSeek Flash (model usage covered by Pullfrog) | 𝕏
| // version cũ không còn trong catalog) mặc định 'stable' — đúng giả định gốc | ||
| // trước khi có beta: mọi bản cài đều là stable. | ||
| const installedChannel: 'stable' | 'beta' | undefined = | ||
| installedVersion === undefined ? undefined : installedVersion === plugin.beta?.version ? 'beta' : 'stable'; |
There was a problem hiding this comment.
A beta that is behind the catalog's current beta is classified as stable here. When installedVersion equals neither plugin.version nor the current plugin.beta.version, the card renders a "Stable" badge next to e.g. demo@1.1.0-beta.1, and the primary button becomes Update to {plugin.version} — a downgrade presented as an update. The comment frames this as only affecting versions removed from the catalog, but it is the normal path every time a new -beta.N ships; a test pinning a stale beta would be worth adding.
Technical details
# Installed beta behind the catalog beta is misread as stable
## Affected sites
- `src/components/SettingsMarketplace.tsx:281-282` — `installedChannel` only returns `'beta'` on exact equality with the current `plugin.beta.version`; every other non-stable version falls through to `'stable'`.
- `src/components/SettingsMarketplace.tsx:305-311` — the badge then labels that version `channelStable`.
- `src/components/SettingsMarketplace.tsx:366-369` — the primary action is enabled and labelled `updateTo: plugin.version` (stable), so it replaces the installed beta with the older stable build.
## Required outcome
- An installed prerelease/beta that is not the catalog's latest beta should still be recognised as the beta channel, so the badge, highlight, and primary action (update to `plugin.beta.version`) match the installed build, and switching to stable stays an explicit secondary action.
## Suggested approach (optional)
- Either persist the channel on the installed record (the PR deliberately avoids a Rust schema change), or infer beta-ness from the installed version string's prerelease component rather than exact equality against one catalog value. A version-compare helper is preferable to raw `includes('-')` if one already exists in the repo.
## Open questions for the human (optional)
- Is `plugin.beta` guaranteed to always hold the latest beta (older betas absent from the catalog)? If older betas can appear too, the exact-equality match needs to consider the full set.



Summary
MarketPlugingains an optionalbetafield (MarketPluginVariant: version, pluginManifestUrl, serviceManifestUrl?, targets?), parsed via a sharedtoMarketPluginVariant/toMarketPlugininsrc/lib/market.ts, so a catalog entry can carry a stable release and a beta release side by side.SettingsMarketplace.tsxinfers the installed channel by comparing the installed version against the catalog's stable vs. beta version (no schema change to the installed-plugin record), and renders channel-aware install/switch actions:This is the consumer (devtool app) side of beta-channel support for plugins. The producer side (release scripts emitting/merging a
betacatalog entry from a-beta.Ntag, plus marking such GitHub releases--prerelease) has been implemented and pushed across all 4 plugin branches indeveloper-desktop-util-plugin. App-level (DevTool-itself) beta channel support is a separate, deferred follow-up.Test plan
npx tsc --noEmitnpx vitest run(1406/1406 passing, including new tests inmarket.test.tsandSettingsMarketplace.test.tsx)🤖 Generated with Claude Code
https://claude.ai/code/session_01BCypCuViyQDKGWxWspXKs2
Generated by Claude Code