feat(plugins,host): pluginRoot officiel transmis au contexte activate(ctx) des plugins — #285 (domain ports + infrastructure plugin: racine du package installé résolue depuis la même source de vérité que la substitution manifest ${pluginRoot}; application runtime_catalog + slash_command; backend dto + app-tauri dto_plugins; frontend domain + plugins runtime loader transmettent pluginRoot au contexte activé; bump pointeur IdeaSDK e3a0f46 via feature/285-plugin-root-runtime-api; QA verte: cargo check domain/application/infrastructure/backend/app-tauri, runtime_catalog 5/5, plugin_install_load 5/5, dto_plugins 2/2, frontend typecheck + 33 tests/3 fichiers, cargo fmt --check, git diff --check; réserves: rebuild AppImage + validation live ctx.pluginRoot dans le binaire actif restants)
This commit is contained in:
@ -50,6 +50,7 @@ fn runtime_catalog_dto_carries_bundle_hash_and_contributions() {
|
||||
publisher: None,
|
||||
version: "1.2.3".to_owned(),
|
||||
bundle_url: "idea-plugin://dev.acme.gitgraph/1.2.3/abc/dist/index.js".to_owned(),
|
||||
plugin_root: "/data/idea/plugins/installed/dev.acme.gitgraph".to_owned(),
|
||||
icon_url: None,
|
||||
content_hash: "abc".to_owned(),
|
||||
capabilities: vec![PluginCapability::Ui, PluginCapability::Tooling],
|
||||
@ -64,6 +65,10 @@ fn runtime_catalog_dto_carries_bundle_hash_and_contributions() {
|
||||
"idea-plugin://dev.acme.gitgraph/1.2.3/abc/dist/index.js"
|
||||
);
|
||||
assert_eq!(value["plugins"][0]["contentHash"], "abc");
|
||||
assert_eq!(
|
||||
value["plugins"][0]["pluginRoot"],
|
||||
"/data/idea/plugins/installed/dev.acme.gitgraph"
|
||||
);
|
||||
assert_eq!(
|
||||
value["plugins"][0]["capabilities"],
|
||||
serde_json::json!(["ui", "tooling"])
|
||||
|
||||
@ -184,6 +184,8 @@ pub struct PluginRuntimePlugin {
|
||||
pub version: String,
|
||||
/// Bundle URL.
|
||||
pub bundle_url: String,
|
||||
/// Canonical host-local root of the installed plugin package.
|
||||
pub plugin_root: String,
|
||||
/// Icon URL.
|
||||
pub icon_url: Option<String>,
|
||||
/// Content hash.
|
||||
@ -3022,6 +3024,10 @@ pub(crate) async fn runtime_plugin_from_entry(
|
||||
&descriptor.registry.content_hash,
|
||||
&descriptor.manifest.main,
|
||||
)?;
|
||||
let plugin_root = packages
|
||||
.installed_plugin_root(&descriptor.manifest.id)
|
||||
.await
|
||||
.map_err(map_store)?;
|
||||
let icon_url = match &descriptor.manifest.icon {
|
||||
Some(icon) => Some(checked_plugin_asset_url(
|
||||
packages,
|
||||
@ -3038,6 +3044,7 @@ pub(crate) async fn runtime_plugin_from_entry(
|
||||
publisher: descriptor.manifest.publisher,
|
||||
version: descriptor.manifest.version.as_str().to_owned(),
|
||||
bundle_url: bundle,
|
||||
plugin_root: plugin_root.as_str().to_owned(),
|
||||
icon_url,
|
||||
content_hash: descriptor.registry.content_hash.as_str().to_owned(),
|
||||
capabilities: descriptor.manifest.capabilities,
|
||||
@ -4203,6 +4210,10 @@ mod tests {
|
||||
let catalog = usecase.execute().await.unwrap();
|
||||
|
||||
assert_eq!(catalog.plugins.len(), 1);
|
||||
assert_eq!(
|
||||
catalog.plugins[0].plugin_root,
|
||||
"/installed/dev.acme.gitgraph"
|
||||
);
|
||||
assert_eq!(
|
||||
catalog.plugins[0].capabilities,
|
||||
vec![
|
||||
|
||||
@ -426,6 +426,7 @@ mod tests {
|
||||
publisher: None,
|
||||
version: "1.0.0".to_owned(),
|
||||
bundle_url: "idea-plugin://dev.acme.plugin/1.0.0/hash/dist/index.js".to_owned(),
|
||||
plugin_root: "/plugins/installed/dev.acme.plugin".to_owned(),
|
||||
icon_url: None,
|
||||
content_hash: "abc".to_owned(),
|
||||
capabilities: vec![domain::PluginCapability::Ui],
|
||||
|
||||
@ -382,6 +382,8 @@ pub struct PluginRuntimePluginDto {
|
||||
pub version: String,
|
||||
/// Bundle URL.
|
||||
pub bundle_url: String,
|
||||
/// Canonical host-local root of the installed plugin package.
|
||||
pub plugin_root: String,
|
||||
/// Icon URL.
|
||||
pub icon_url: Option<String>,
|
||||
/// Content hash.
|
||||
@ -414,6 +416,7 @@ impl From<application::PluginRuntimePlugin> for PluginRuntimePluginDto {
|
||||
publisher: value.publisher,
|
||||
version: value.version,
|
||||
bundle_url: value.bundle_url,
|
||||
plugin_root: value.plugin_root,
|
||||
icon_url: value.icon_url,
|
||||
content_hash: value.content_hash,
|
||||
capabilities: value.capabilities,
|
||||
|
||||
@ -440,6 +440,19 @@ pub trait PluginPackageStore: Send + Sync {
|
||||
/// Lists committed package roots.
|
||||
async fn list_installed(&self) -> Result<Vec<PluginPackageRef>, PluginStoreError>;
|
||||
|
||||
/// Resolves the host-local root of one committed plugin package.
|
||||
async fn installed_plugin_root(
|
||||
&self,
|
||||
plugin_id: &crate::PluginId,
|
||||
) -> Result<LocalPath, PluginStoreError> {
|
||||
self.list_installed()
|
||||
.await?
|
||||
.into_iter()
|
||||
.find(|package| package.plugin_id.as_ref() == Some(plugin_id))
|
||||
.map(|package| LocalPath::new(package.root))
|
||||
.ok_or(PluginStoreError::NotFound)
|
||||
}
|
||||
|
||||
/// Reads `idea-plugin.json` from a package root.
|
||||
async fn read_manifest(
|
||||
&self,
|
||||
|
||||
@ -141,6 +141,21 @@ impl PluginPackageStore for FsPluginPackageStore {
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
async fn installed_plugin_root(
|
||||
&self,
|
||||
plugin_id: &PluginId,
|
||||
) -> Result<LocalPath, PluginStoreError> {
|
||||
let root = self.installed_plugin_dir(plugin_id);
|
||||
let canonical = root.canonicalize().map_err(|error| {
|
||||
if error.kind() == io::ErrorKind::NotFound {
|
||||
PluginStoreError::NotFound
|
||||
} else {
|
||||
PluginStoreError::Io(error.to_string())
|
||||
}
|
||||
})?;
|
||||
Ok(LocalPath::new(canonical.to_string_lossy().into_owned()))
|
||||
}
|
||||
|
||||
async fn read_manifest(
|
||||
&self,
|
||||
package: &PluginPackageRef,
|
||||
|
||||
@ -134,6 +134,13 @@ async fn installs_reference_fixture_and_loads_runtime_catalog() {
|
||||
assert!(plugin
|
||||
.bundle_url
|
||||
.starts_with("idea-plugin://dev.idea.fixtures.reference-minimal/0.1.0/"));
|
||||
assert_eq!(
|
||||
PathBuf::from(&plugin.plugin_root),
|
||||
app_data
|
||||
.join("plugins/installed/dev.idea.fixtures.reference-minimal")
|
||||
.canonicalize()
|
||||
.unwrap()
|
||||
);
|
||||
assert_eq!(plugin.contributes.menus.len(), 1);
|
||||
assert_eq!(plugin.contributes.menu_items.len(), 1);
|
||||
|
||||
@ -183,6 +190,13 @@ async fn installs_sdk_hello_plugin_and_loads_runtime_catalog() {
|
||||
.bundle_url
|
||||
.starts_with("idea-plugin://com.example.hello-plugin/0.1.0/"));
|
||||
assert!(plugin.bundle_url.ends_with("/dist/index.js"));
|
||||
assert_eq!(
|
||||
PathBuf::from(&plugin.plugin_root),
|
||||
app_data
|
||||
.join("plugins/installed/com.example.hello-plugin")
|
||||
.canonicalize()
|
||||
.unwrap()
|
||||
);
|
||||
assert_eq!(plugin.contributes.menus[0].id, "hello-plugin.menu");
|
||||
assert_eq!(plugin.contributes.menus[0].label.as_str(), "Hello Plugin");
|
||||
assert_eq!(
|
||||
|
||||
Reference in New Issue
Block a user