Read a local plugin's version from its file header (#16118)

* Read a local plugin's version from its file header

read_install_state() let the sidecar's installed_version override the version
parsed from the entry file's PEP 723 header. That is right for cloud plugins,
whose header may be stale, but a local plugin has no other source of truth, so
the UI kept showing the version recorded at first install after the file was
edited. For local installs, use the header version.

* Keep the cloud installed version of an unsubscribed plugin

A cloud plugin kept as local after unsubscribing is rewritten with installed_from=local and no cloud_uuid, which the local-header rule could not tell apart from a real local install. Its installed_version is still the cloud one while the header may lag, so the next scan showed the stale header version. Record the conversion in the install state and exclude those plugins from the header rule. Add tests for a local plugin following its header and for an unsubscribed cloud plugin keeping its cloud version.

* Add tests for the version of a local plugin

A local plugin's version comes from its entry file header. Cover the case of a plugin edited after install, and a plugin unsubscribed from the cloud, which is now a normal local package.
This commit is contained in:
Riccardo BRAMATI
2026-10-11 03:09:32 +08:00
committed by GitHub
parent ea10c84d6b
commit 07a12f5fe4
2 changed files with 48 additions and 1 deletions
+8 -1
View File
@@ -754,7 +754,14 @@ bool read_install_state(const boost::filesystem::path& plugin_dir, PluginDescrip
// truth for a cloud plugin's installed version: it records the version fetched from
// the cloud at install time, independent of the (possibly stale) manifest/PEP723
// header that scan_directory parses into entry.version.
if (!state.installed_version.empty())
//
// A local plugin has no cloud copy to diverge from: the entry file's own header is the only
// source of truth for its version. Trusting the sidecar there would pin the version shown
// in the UI to whatever it was at first install, even after the file is edited.
const bool is_local_install = state.installed_from == "local" && state.cloud_uuid.empty();
if (is_local_install && !entry.version.empty())
entry.installed_version = entry.version;
else if (!state.installed_version.empty())
entry.installed_version = state.installed_version;
if (!state.cloud_uuid.empty())
entry.cloud = CloudPluginState{state.cloud_uuid, true, false, false};
+40
View File
@@ -144,3 +144,43 @@ TEST_CASE("install-state sidecar is the source of truth for a cloud plugin's ins
read_install_state(plugin_dir, scanned);
CHECK(scanned.installed_version == "1.2.0");
}
TEST_CASE("a local plugin's installed version follows its entry file header", "[PluginInstall]")
{
ScopedDataDir data_dir_guard("local-installed-version");
const fs::path plugin_dir = data_dir_guard.dir / "plugin";
fs::create_directories(plugin_dir);
// Sidecar written when the local plugin was first installed at 1.0.0.
PluginInstallState state;
state.installed_from = "local";
state.installed_version = "1.0.0";
REQUIRE(write_install_state(plugin_dir, state));
// The user then edited the plugin file: the freshly scanned header says 1.1.0.
PluginDescriptor scanned;
scanned.version = "1.1.0";
REQUIRE(read_install_state(plugin_dir, scanned));
CHECK(scanned.installed_version == "1.1.0");
}
TEST_CASE("an unsubscribed cloud plugin follows its header version", "[PluginInstall]")
{
ScopedDataDir data_dir_guard("unsubscribed-installed-version");
const fs::path plugin_dir = data_dir_guard.dir / "plugin";
fs::create_directories(plugin_dir);
// What keep_installed_plugin_as_local leaves behind: a normal local package. The sidecar still
// holds the version fetched from the cloud (1.2.0), but the header (1.0.0) is the source of truth.
PluginInstallState state;
state.installed_from = "local";
state.installed_version = "1.2.0";
REQUIRE(write_install_state(plugin_dir, state));
PluginDescriptor scanned;
scanned.version = "1.0.0";
REQUIRE(read_install_state(plugin_dir, scanned));
CHECK(scanned.installed_version == "1.0.0");
}