fix(34): WR-10 align the marketplace compatibility marker with plugin:install

With a framework version that is not a release (a devel build, an empty
FrameworkVersion) every plugin with a minimum was marked as needing a
newer framework, and Compatible looked only at the newest release, so a
plugin whose older release fits was marked although plugin:install
would install that release. An unknown framework now gives no marker
(and no needs-framework note on the Installed tab), and a plugin whose
newest release is too new is looked up (cached, four at a time) and
counts as compatible when an older release fits.
This commit is contained in:
Jakub Zych
2026-10-29 22:12:54 +01:00
parent 7b193032c8
commit 754ab5423a
4 changed files with 96 additions and 8 deletions

View File

@@ -92,9 +92,11 @@ type InstalledPlugin struct {
// MarketplacePlugin is one plugin of the registry's search on the Plugins
// screen: the registry's listing (name, description, category, featured
// flag, https icon URL, newest release and its framework minimum), whether
// the application already has it (Installed), whether the built framework
// satisfies the newest release's minimum (Compatible) and the summer
// command that installs it.
// the application already has it (Installed), whether summer
// plugin:install can install a release of it with the built framework
// (Compatible: the newest release, or an older one when the newest needs
// a newer framework; true when the built framework version is not a
// release and cannot be judged) and the summer command that installs it.
type MarketplacePlugin struct {
ID string `json:"id"`
Name string `json:"name"`
@@ -375,6 +377,10 @@ func applyUpdate(plugin *InstalledPlugin, versions []kiosk.RegistryVersion, fram
if newest.Version == "" || (compatible && kiosk.CompareVersions(newest.Version, latest.Version) <= 0) {
return
}
if !kiosk.ValidVersion(framework) {
// An unknown or devel framework cannot be judged (WR-10).
return
}
if kiosk.Comparable(plugin.Version) && !kiosk.Newer(newest.Version, plugin.Version) {
return
}
@@ -461,6 +467,7 @@ func (s *service) pluginsMarketplace(w http.ResponseWriter, r *http.Request) {
installed[p.ID] = true
}
framework := frameworkRelease(overview.Framework)
known := kiosk.ValidVersion(framework)
for _, p := range list.Data {
if !marketplaceIDRe.MatchString(p.ID) {
// The ID ends up in a shell command; never offer an odd one.
@@ -470,11 +477,17 @@ func (s *service) pluginsMarketplace(w http.ResponseWriter, r *http.Request) {
ID: p.ID, Name: p.Name, Description: p.Description, Category: p.Category,
Featured: p.Featured, IconURL: httpsURL(p.IconURL),
LatestVersion: p.LatestVersion, LatestFramework: p.LatestFramework,
Installed: installed[p.ID],
Compatible: kiosk.Satisfies(framework, p.LatestFramework),
Installed: installed[p.ID],
// A framework version that is not a release (an unknown or
// devel build) cannot be judged: no marker rather than a
// wrong one on every plugin (WR-10).
Compatible: !known || kiosk.Satisfies(framework, p.LatestFramework),
InstallCommand: "summer plugin:install " + p.ID,
})
}
if known {
catalog.olderCompatible(r.Context(), out.Data, framework)
}
out.Meta.Total = len(out.Data)
if q == "" && category == "" {
out.Meta.Categories = categoriesOf(list.Data)
@@ -484,6 +497,36 @@ func (s *service) pluginsMarketplace(w http.ResponseWriter, r *http.Request) {
writeJSON(w, http.StatusOK, out)
}
// olderCompatible marks as compatible the plugins whose newest release
// needs a newer framework but which have an older release framework
// allows, the one summer plugin:install would pick. It looks their
// releases up (cached, at most registryLookups at a time); a failed
// lookup leaves the plugin as it is.
func (c *pluginCatalog) olderCompatible(ctx context.Context, plugins []MarketplacePlugin, framework string) {
var wg sync.WaitGroup
slots := make(chan struct{}, registryLookups)
for i := range plugins {
plugin := &plugins[i]
if plugin.Compatible {
continue
}
wg.Add(1)
slots <- struct{}{}
go func() {
defer wg.Done()
defer func() { <-slots }()
versions, listed, err := c.releases(ctx, plugin.ID)
if err != nil || !listed {
return
}
if _, ok := kiosk.NewestCompatible(versions, framework); ok {
plugin.Compatible = true
}
}()
}
wg.Wait()
}
// categoriesOf returns the distinct valid categories of the plugins the
// marketplace offers (valid IDs only), sorted.
func categoriesOf(plugins []kiosk.RegistryPlugin) []string {