aboutsummaryrefslogtreecommitdiff
path: root/src/app/api/plugins/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/app/api/plugins/mod.rs')
-rw-r--r--src/app/api/plugins/mod.rs96
1 files changed, 94 insertions, 2 deletions
diff --git a/src/app/api/plugins/mod.rs b/src/app/api/plugins/mod.rs
index 83a6065e..e48a1506 100644
--- a/src/app/api/plugins/mod.rs
+++ b/src/app/api/plugins/mod.rs
@@ -13,9 +13,9 @@ use crate::api::schema::{
ResponseResult,
};
use crate::app::App;
+pub(super) use manifest::normalize_plugin_id;
use manifest::{
- effective_platforms, ensure_platform_supported, normalize_action_id, normalize_plugin_id,
- normalize_plugin_source,
+ effective_platforms, ensure_platform_supported, normalize_action_id, normalize_plugin_source,
};
#[cfg(test)]
@@ -110,6 +110,7 @@ impl App {
}
return encode_error(id, "plugin_registry_save_failed", err.to_string());
}
+ self.clear_agent_view_for_source(&format!("plugin:{plugin_id}"));
}
encode_success(id, ResponseResult::PluginUnlinked { plugin_id, removed })
}
@@ -593,6 +594,9 @@ impl App {
let Some(plugin) = self.state.installed_plugins.get(&plugin_id).cloned() else {
return encode_error(id, "plugin_not_found", "plugin not found");
};
+ if !enabled {
+ self.clear_agent_view_for_source(&format!("plugin:{plugin_id}"));
+ }
if enabled {
encode_success(id, ResponseResult::PluginEnabled { plugin })
} else {
@@ -1212,6 +1216,38 @@ command = ["echo", "b"]
}
#[test]
+ fn startup_hook_manifest_loads() {
+ let root = unique_temp_path("plugin-startup-manifest");
+ write_manifest_content(
+ &root,
+ r#"
+id = "example.startup-manifest"
+name = "Startup Manifest"
+version = "0.1.0"
+min_herdr_version = "0.6.10"
+platforms = ["linux", "macos", "windows"]
+
+[[startup]]
+command = ["node", "restore.js"]
+platforms = ["linux", "macos"]
+"#,
+ );
+
+ let plugin = load_plugin_manifest(&root.display().to_string(), true).unwrap();
+
+ assert_eq!(plugin.startup.len(), 1);
+ assert_eq!(plugin.startup[0].command, ["node", "restore.js"]);
+ assert_eq!(
+ plugin.startup[0].platforms,
+ Some(vec![
+ crate::api::schema::PluginPlatform::Linux,
+ crate::api::schema::PluginPlatform::Macos,
+ ])
+ );
+ let _ = std::fs::remove_dir_all(root);
+ }
+
+ #[test]
fn smoke_fixture_manifest_loads() {
let plugin = load_plugin_manifest("tests/fixtures/plugin-smoke", true)
.expect("smoke fixture should load");
@@ -1235,6 +1271,12 @@ command = ["echo", "b"]
let root = unique_temp_path("plugin-enable-disable");
write_manifest(&root);
link_manifest(&mut app, &root);
+ app.state.agent_view_override = Some(crate::api::schema::AgentViewSetParams {
+ source: "plugin:example.worktree-bootstrap".into(),
+ label: None,
+ filter: None,
+ sort: Vec::new(),
+ });
let disabled = app.handle_api_request(Request {
id: "disable".into(),
@@ -1246,6 +1288,19 @@ command = ["echo", "b"]
panic!("expected disabled response: {disabled}");
};
assert!(!plugin.enabled);
+ assert!(app.state.agent_view_override.is_none());
+ let delayed_restore = app.handle_api_request(Request {
+ id: "delayed-startup-restore".into(),
+ method: Method::AgentViewSet(crate::api::schema::AgentViewSetParams {
+ source: "plugin:example.worktree-bootstrap".into(),
+ label: None,
+ filter: None,
+ sort: Vec::new(),
+ }),
+ });
+ let delayed_restore: crate::api::schema::ErrorResponse =
+ serde_json::from_str(&delayed_restore).unwrap();
+ assert_eq!(delayed_restore.error.code, "plugin_disabled");
let enabled = app.handle_api_request(Request {
id: "enable".into(),
@@ -2266,6 +2321,43 @@ command = ["sh", "-c", "printf '%s\n%s\n%s' \"$HERDR_PLUGIN_ROOT\" \"$HERDR_PLUG
#[cfg(unix)]
#[test]
+ fn startup_hooks_run_once_with_plugin_environment() {
+ let mut app = test_app();
+ let root = unique_temp_path("plugin-startup-hook");
+ let capture = root.join("startup.txt");
+ write_manifest_content(
+ &root,
+ &format!(
+ r#"
+id = "example.startup"
+name = "Startup"
+version = "0.1.0"
+min_herdr_version = "0.6.10"
+platforms = ["linux", "macos"]
+
+[[startup]]
+command = ["sh", "-c", "printf '%s:%s' \"$HERDR_PLUGIN_ID\" \"$HERDR_PLUGIN_EVENT\" > {}"]
+"#,
+ capture.display()
+ ),
+ );
+ link_manifest(&mut app, &root);
+
+ app.run_plugin_startup_hooks();
+
+ assert_eq!(
+ read_capture_when_ready(&capture, || {
+ app.drain_all_internal_events();
+ }),
+ "example.startup:startup"
+ );
+ let plugin = app.state.installed_plugins.get("example.startup").unwrap();
+ assert_eq!(plugin.startup.len(), 1);
+ let _ = std::fs::remove_dir_all(root);
+ }
+
+ #[cfg(unix)]
+ #[test]
fn event_hooks_use_event_target_context() {
let mut app = test_app();
app.state.workspaces = vec![