aboutsummaryrefslogtreecommitdiff
path: root/src/app/api
diff options
context:
space:
mode:
authorOgulcan Celik <ogulcancelik@gmail.com>2026-06-14 23:01:47 +0300
committerOgulcan Celik <ogulcancelik@gmail.com>2026-06-14 23:01:47 +0300
commit3015ce0f9d120df7583f838d1a47cb469fe5a727 (patch)
tree94db93e9fa6f7b83e4eb3ab09581ce101d536cfd /src/app/api
parent001a6b2fc4218d232967f285f945b313ce9ca19a (diff)
fix: stabilize plugin config directories
Diffstat (limited to 'src/app/api')
-rw-r--r--src/app/api/plugins/env.rs17
-rw-r--r--src/app/api/plugins/mod.rs86
-rw-r--r--src/app/api/plugins/panes.rs2
-rw-r--r--src/app/api/plugins/runtime.rs2
4 files changed, 92 insertions, 15 deletions
diff --git a/src/app/api/plugins/env.rs b/src/app/api/plugins/env.rs
index 13644a19..fcfac62f 100644
--- a/src/app/api/plugins/env.rs
+++ b/src/app/api/plugins/env.rs
@@ -1,9 +1,20 @@
use crate::api::schema::InstalledPluginInfo;
+pub(super) fn plugin_config_dir(plugin_id: &str) -> std::path::PathBuf {
+ crate::plugin_paths::plugin_config_dir(plugin_id)
+}
+
+pub(super) fn plugin_state_dir(plugin_id: &str) -> std::path::PathBuf {
+ crate::plugin_paths::plugin_state_dir(plugin_id)
+}
+
+pub(super) fn ensure_plugin_user_dirs(plugin: &InstalledPluginInfo) -> std::io::Result<()> {
+ crate::plugin_paths::ensure_plugin_user_dirs(&plugin.plugin_id)
+}
+
pub(super) fn plugin_path_env(plugin: &InstalledPluginInfo) -> Vec<(String, String)> {
- let component = crate::api::schema::plugin_managed_path_component(&plugin.plugin_id);
- let config_dir = crate::config::config_dir().join("plugins").join(&component);
- let state_dir = crate::config::state_dir().join("plugins").join(component);
+ let config_dir = plugin_config_dir(&plugin.plugin_id);
+ let state_dir = plugin_state_dir(&plugin.plugin_id);
vec![
("HERDR_PLUGIN_ROOT".to_string(), plugin.plugin_root.clone()),
diff --git a/src/app/api/plugins/mod.rs b/src/app/api/plugins/mod.rs
index d00ad20f..89f7a23d 100644
--- a/src/app/api/plugins/mod.rs
+++ b/src/app/api/plugins/mod.rs
@@ -36,6 +36,9 @@ impl App {
Err((code, message)) => return encode_error(id, code, message),
}
}
+ if let Err(err) = env::ensure_plugin_user_dirs(&plugin) {
+ return encode_error(id, "plugin_user_dir_create_failed", err.to_string());
+ }
let previous = self.state.installed_plugins.get(&plugin.plugin_id).cloned();
self.state
.installed_plugins
@@ -756,6 +759,71 @@ action = "bootstrap"
}
#[test]
+ fn plugin_link_creates_stable_config_and_state_dirs() {
+ let mut app = test_app();
+ let root = unique_temp_path("plugin-link-dirs");
+ let config_dir = super::env::plugin_config_dir("example.config-dirs");
+ let state_dir = super::env::plugin_state_dir("example.config-dirs");
+ let _ = std::fs::remove_dir_all(&config_dir);
+ let _ = std::fs::remove_dir_all(&state_dir);
+ write_manifest_content(
+ &root,
+ r#"
+id = "example.config-dirs"
+name = "Config Dirs"
+version = "0.1.0"
+platforms = ["linux", "macos", "windows"]
+"#,
+ );
+
+ link_manifest(&mut app, &root);
+
+ assert!(config_dir.is_dir());
+ assert!(state_dir.is_dir());
+
+ let _ = std::fs::remove_dir_all(root);
+ let _ = std::fs::remove_dir_all(config_dir);
+ let _ = std::fs::remove_dir_all(state_dir);
+ }
+
+ #[test]
+ fn plugin_link_seeds_stable_config_dir_from_legacy_unhashed_dir() {
+ let mut app = test_app();
+ let root = unique_temp_path("plugin-link-legacy-config");
+ let config_dir = super::env::plugin_config_dir("example.legacy-config");
+ let state_dir = super::env::plugin_state_dir("example.legacy-config");
+ let legacy_dir = crate::config::config_dir()
+ .join("plugins")
+ .join("example.legacy-config");
+ let _ = std::fs::remove_dir_all(&config_dir);
+ let _ = std::fs::remove_dir_all(&state_dir);
+ let _ = std::fs::remove_dir_all(&legacy_dir);
+ std::fs::create_dir_all(&legacy_dir).unwrap();
+ std::fs::write(legacy_dir.join(".env"), "TELEGRAM_BOT_TOKEN=test\n").unwrap();
+ write_manifest_content(
+ &root,
+ r#"
+id = "example.legacy-config"
+name = "Legacy Config"
+version = "0.1.0"
+platforms = ["linux", "macos", "windows"]
+"#,
+ );
+
+ link_manifest(&mut app, &root);
+
+ assert_eq!(
+ std::fs::read_to_string(config_dir.join(".env")).unwrap(),
+ "TELEGRAM_BOT_TOKEN=test\n"
+ );
+
+ let _ = std::fs::remove_dir_all(root);
+ let _ = std::fs::remove_dir_all(config_dir);
+ let _ = std::fs::remove_dir_all(state_dir);
+ let _ = std::fs::remove_dir_all(legacy_dir);
+ }
+
+ #[test]
fn plugin_link_lists_and_unlinks_manifest() {
let mut app = test_app();
let root = unique_temp_path("plugin-link");
@@ -1192,9 +1260,8 @@ command = ["sh", "-c", "printf '%s\n%s\n%s\n' \"$HERDR_PLUGIN_ROOT\" \"$HERDR_PL
Some(
crate::config::config_dir()
.join("plugins")
- .join(crate::api::schema::plugin_managed_path_component(
- "example.path-env"
- ))
+ .join("config")
+ .join("example.path-env")
.display()
.to_string()
.as_str()
@@ -1205,9 +1272,7 @@ command = ["sh", "-c", "printf '%s\n%s\n%s\n' \"$HERDR_PLUGIN_ROOT\" \"$HERDR_PL
Some(
crate::config::state_dir()
.join("plugins")
- .join(crate::api::schema::plugin_managed_path_component(
- "example.path-env"
- ))
+ .join("example.path-env")
.display()
.to_string()
.as_str()
@@ -1570,9 +1635,8 @@ command = ["sh", "-c", "printf '%s\n%s\n%s' \"$HERDR_PLUGIN_ROOT\" \"$HERDR_PLUG
Some(
crate::config::config_dir()
.join("plugins")
- .join(crate::api::schema::plugin_managed_path_component(
- "example.action-paths"
- ))
+ .join("config")
+ .join("example.action-paths")
.display()
.to_string()
.as_str()
@@ -1583,9 +1647,7 @@ command = ["sh", "-c", "printf '%s\n%s\n%s' \"$HERDR_PLUGIN_ROOT\" \"$HERDR_PLUG
Some(
crate::config::state_dir()
.join("plugins")
- .join(crate::api::schema::plugin_managed_path_component(
- "example.action-paths"
- ))
+ .join("example.action-paths")
.display()
.to_string()
.as_str()
diff --git a/src/app/api/plugins/panes.rs b/src/app/api/plugins/panes.rs
index e6fe7b44..5a935a8c 100644
--- a/src/app/api/plugins/panes.rs
+++ b/src/app/api/plugins/panes.rs
@@ -183,6 +183,8 @@ impl App {
let mut env = super::super::env::normalize_launch_env(env)?;
let context_json = serde_json::to_string(&context)
.map_err(|err| ("invalid_plugin_context".to_string(), err.to_string()))?;
+ super::env::ensure_plugin_user_dirs(plugin)
+ .map_err(|err| ("plugin_user_dir_create_failed".to_string(), err.to_string()))?;
env.retain(|(key, _)| !plugin_pane_protected_env_key(key));
env.extend(super::env::plugin_path_env(plugin));
env.push((
diff --git a/src/app/api/plugins/runtime.rs b/src/app/api/plugins/runtime.rs
index 94a49dd4..0008def2 100644
--- a/src/app/api/plugins/runtime.rs
+++ b/src/app/api/plugins/runtime.rs
@@ -31,6 +31,8 @@ impl App {
let args = command.iter().skip(1).cloned().collect::<Vec<_>>();
let context_json = serde_json::to_string(context)
.map_err(|err| ("invalid_plugin_context", err.to_string()))?;
+ super::env::ensure_plugin_user_dirs(plugin)
+ .map_err(|err| ("plugin_user_dir_create_failed", err.to_string()))?;
let log_id = format!("plugin-log-{}", self.state.next_plugin_command_log_id);
self.state.next_plugin_command_log_id += 1;
let started_unix_ms = current_unix_ms();