aboutsummaryrefslogtreecommitdiff
path: root/src/integration
diff options
context:
space:
mode:
authorJJ Liebig <jonathan.liebig@gmail.com>2026-09-04 01:05:35 +0200
committerGitHub <noreply@github.com>2026-09-04 01:05:35 +0200
commit3150bd92d6162ba248bc28fd4d40bd0d6238e1af (patch)
treebeab751f2fc61d49a9f771091b9132cd1357b070 /src/integration
parent73825652244edaf11d3fddc89b5b00266edcae0d (diff)
fix(integration): resolve Devin config on Windows (#3595)
refs #2724
Diffstat (limited to 'src/integration')
-rw-r--r--src/integration/env.rs33
-rw-r--r--src/integration/tests.rs32
2 files changed, 63 insertions, 2 deletions
diff --git a/src/integration/env.rs b/src/integration/env.rs
index 8f3a9e65..c4ee1c34 100644
--- a/src/integration/env.rs
+++ b/src/integration/env.rs
@@ -76,6 +76,11 @@ pub(crate) fn devin_dir() -> io::Result<PathBuf> {
return expand_tilde_path(PathBuf::from(value)).map(|path| path.join("devin"));
}
+ #[cfg(windows)]
+ if let Some(value) = std::env::var_os("APPDATA").filter(|value| !value.is_empty()) {
+ return Ok(PathBuf::from(value).join("devin"));
+ }
+
Ok(home_dir()?.join(".config").join("devin"))
}
@@ -215,7 +220,31 @@ pub(crate) fn home_dir() -> io::Result<PathBuf> {
}
#[cfg(test)]
-pub(crate) fn integration_env_lock() -> MutexGuard<'static, ()> {
+pub(crate) struct IntegrationEnvLock {
+ _guard: MutexGuard<'static, ()>,
+ #[cfg(windows)]
+ appdata: Option<std::ffi::OsString>,
+}
+
+#[cfg(test)]
+impl Drop for IntegrationEnvLock {
+ fn drop(&mut self) {
+ #[cfg(windows)]
+ if let Some(appdata) = self.appdata.take() {
+ std::env::set_var("APPDATA", appdata);
+ } else {
+ std::env::remove_var("APPDATA");
+ }
+ }
+}
+
+#[cfg(test)]
+pub(crate) fn integration_env_lock() -> IntegrationEnvLock {
static LOCK: OnceLock<Mutex<()>> = OnceLock::new();
- LOCK.get_or_init(|| Mutex::new(())).lock().unwrap()
+ let guard = LOCK.get_or_init(|| Mutex::new(())).lock().unwrap();
+ IntegrationEnvLock {
+ _guard: guard,
+ #[cfg(windows)]
+ appdata: std::env::var_os("APPDATA"),
+ }
}
diff --git a/src/integration/tests.rs b/src/integration/tests.rs
index c2d94a6e..a02a549e 100644
--- a/src/integration/tests.rs
+++ b/src/integration/tests.rs
@@ -134,6 +134,8 @@ fn clear_integration_path_env() {
std::env::remove_var(COPILOT_HOME_ENV_VAR);
std::env::remove_var(KIMI_CODE_HOME_ENV_VAR);
std::env::remove_var("XDG_CONFIG_HOME");
+ #[cfg(windows)]
+ std::env::remove_var("APPDATA");
std::env::remove_var(QODERCLI_CONFIG_DIR_ENV_VAR);
std::env::remove_var(QWEN_HOME_ENV_VAR);
std::env::remove_var(CURSOR_CONFIG_DIR_ENV_VAR);
@@ -211,6 +213,36 @@ fn home_dir_uses_userprofile_when_home_is_missing() {
#[cfg(windows)]
#[test]
+fn windows_devin_dir_uses_appdata_without_xdg_override() {
+ let previous_appdata;
+ {
+ let _lock = integration_env_lock();
+ previous_appdata = std::env::var_os("APPDATA");
+ let previous_xdg = std::env::var_os("XDG_CONFIG_HOME");
+ let base = unique_base();
+ let appdata = base.join("appdata");
+ let xdg = base.join("xdg");
+ std::env::set_var("APPDATA", &appdata);
+
+ assert_eq!(devin_dir().unwrap(), appdata.join("devin"));
+
+ std::env::set_var("XDG_CONFIG_HOME", &xdg);
+ assert_eq!(devin_dir().unwrap(), xdg.join("devin"));
+
+ if let Some(value) = previous_xdg {
+ std::env::set_var("XDG_CONFIG_HOME", value);
+ } else {
+ std::env::remove_var("XDG_CONFIG_HOME");
+ }
+ }
+ {
+ let _lock = integration_env_lock();
+ assert_eq!(std::env::var_os("APPDATA"), previous_appdata);
+ }
+}
+
+#[cfg(windows)]
+#[test]
fn windows_supports_portable_integrations() {
use crate::api::schema::IntegrationTarget;