From 1b603eaded20ff8f742e14bb8f29b792d504503e Mon Sep 17 00:00:00 2001 From: mikemikimike <13286568797@163.com> Date: Fri, 28 Aug 2026 14:32:18 +0800 Subject: [PATCH] fix: preserve shell environment over dotenv files --- aider/main.py | 24 +++++++++++++++--------- tests/basic/test_main.py | 13 +++++++++++++ 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/aider/main.py b/aider/main.py index afb3f836624..bebf1e76fc1 100644 --- a/aider/main.py +++ b/aider/main.py @@ -374,16 +374,22 @@ def load_dotenv_files(git_root, dotenv_fname, encoding="utf-8"): # Remove duplicates if it somehow got included by generate_search_path_list dotenv_files = list(dict.fromkeys(dotenv_files)) + existing_env = os.environ.copy() loaded = [] - for fname in dotenv_files: - try: - if Path(fname).exists(): - load_dotenv(fname, override=True, encoding=encoding) - loaded.append(fname) - except OSError as e: - print(f"OSError loading {fname}: {e}") - except Exception as e: - print(f"Error loading {fname}: {e}") + try: + for fname in dotenv_files: + try: + if Path(fname).exists(): + load_dotenv(fname, override=True, encoding=encoding) + loaded.append(fname) + except OSError as e: + print(f"OSError loading {fname}: {e}") + except Exception as e: + print(f"Error loading {fname}: {e}") + finally: + # Keep values explicitly provided by the user ahead of dotenv files. + for key, value in existing_env.items(): + os.environ[key] = value return loaded diff --git a/tests/basic/test_main.py b/tests/basic/test_main.py index c8966a53671..f45c47aa4a5 100644 --- a/tests/basic/test_main.py +++ b/tests/basic/test_main.py @@ -1470,6 +1470,19 @@ def test_load_dotenv_files_override(self): # Restore CWD os.chdir(original_cwd) + def test_load_dotenv_files_does_not_override_existing_env(self): + with GitTemporaryDirectory() as git_dir: + git_dir = Path(git_dir) + env_file = git_dir / ".env" + env_file.write_text("EXISTING_VAR=file_value\nFILE_ONLY_VAR=file_value\n") + + with patch.dict(os.environ, {"EXISTING_VAR": "shell_value"}, clear=False): + with patch("pathlib.Path.home", return_value=git_dir / "fake_home"): + load_dotenv_files(str(git_dir), None) + + self.assertEqual(os.environ.get("EXISTING_VAR"), "shell_value") + self.assertEqual(os.environ.get("FILE_ONLY_VAR"), "file_value") + @patch("aider.main.InputOutput") def test_cache_without_stream_no_warning(self, MockInputOutput): mock_io_instance = MockInputOutput.return_value