From c0b531866d6b86aaeeb216ffa83dbbfa006b817b Mon Sep 17 00:00:00 2001 From: "claude[bot]" <209825114+claude[bot]@users.noreply.github.com> Date: Thu, 10 Jul 2025 01:24:01 +0000 Subject: [PATCH] fix: Allow BASIC_MEMORY_HOME env var to override file-based config MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The BASIC_MEMORY_HOME environment variable was not being respected when a config file existed, because the file-based configuration bypassed Pydantic''s environment variable parsing. This fix modifies ConfigManager.load_config() to explicitly check for BASIC_MEMORY_HOME after loading from file and override the main project path if the environment variable is set. Fixes #219 🤖 Generated with [Claude Code](https://claude.ai/code) Co-authored-by: Paul Hernandez --- src/basic_memory/config.py | 9 ++++- tests/test_config.py | 71 +++++++++++++++++++++++++++++++++++++- 2 files changed, 78 insertions(+), 2 deletions(-) diff --git a/src/basic_memory/config.py b/src/basic_memory/config.py index 998c7c6b..d8cfae0c 100644 --- a/src/basic_memory/config.py +++ b/src/basic_memory/config.py @@ -180,7 +180,14 @@ class ConfigManager: if self.config_file.exists(): try: data = json.loads(self.config_file.read_text(encoding="utf-8")) - return BasicMemoryConfig(**data) + config = BasicMemoryConfig(**data) + + # Allow BASIC_MEMORY_HOME environment variable to override the main project path + env_home = os.getenv("BASIC_MEMORY_HOME") + if env_home: + config.projects["main"] = str(Path(env_home)) + + return config except Exception as e: # pragma: no cover logger.error(f"Failed to load config: {e}") config = BasicMemoryConfig() diff --git a/tests/test_config.py b/tests/test_config.py index 6c03fbc0..f0e0cf1e 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -1,6 +1,7 @@ """Test configuration management.""" -from basic_memory.config import BasicMemoryConfig +import json +from basic_memory.config import BasicMemoryConfig, ConfigManager class TestBasicMemoryConfig: @@ -76,3 +77,71 @@ class TestBasicMemoryConfig: # The default_factory should override with BASIC_MEMORY_HOME value # Note: This tests the current behavior where default_factory takes precedence assert config.projects["main"] == original_path + + +class TestConfigManager: + """Test ConfigManager behavior with BASIC_MEMORY_HOME environment variable.""" + + def test_load_config_respects_basic_memory_home_with_existing_file(self, config_home, monkeypatch): + """Test that ConfigManager.load_config respects BASIC_MEMORY_HOME even when config file exists.""" + # Set up a custom path via environment variable + custom_path = str(config_home / "custom" / "env" / "path") + monkeypatch.setenv("BASIC_MEMORY_HOME", custom_path) + + # Create a config manager with a temporary config directory + config_dir = config_home / ".basic-memory" + config_dir.mkdir(parents=True, exist_ok=True) + config_file = config_dir / "config.json" + + # Create a config file with a different main project path + file_config = { + "projects": { + "main": str(config_home / "file" / "path") + }, + "default_project": "main" + } + config_file.write_text(json.dumps(file_config)) + + # Mock the ConfigManager to use our temporary config directory + monkeypatch.setattr("basic_memory.config.ConfigManager.config_dir", config_dir) + monkeypatch.setattr("basic_memory.config.ConfigManager.config_file", config_file) + + # Create ConfigManager and load config + config_manager = ConfigManager() + config_manager.config_dir = config_dir + config_manager.config_file = config_file + + loaded_config = config_manager.load_config() + + # The environment variable should override the file-based configuration + assert loaded_config.projects["main"] == custom_path + + def test_load_config_without_basic_memory_home_uses_file_config(self, config_home, monkeypatch): + """Test that ConfigManager.load_config uses file config when BASIC_MEMORY_HOME is not set.""" + # Ensure BASIC_MEMORY_HOME is not set + monkeypatch.delenv("BASIC_MEMORY_HOME", raising=False) + + # Create a config manager with a temporary config directory + config_dir = config_home / ".basic-memory" + config_dir.mkdir(parents=True, exist_ok=True) + config_file = config_dir / "config.json" + + # Create a config file with a specific main project path + file_path = str(config_home / "file" / "path") + file_config = { + "projects": { + "main": file_path + }, + "default_project": "main" + } + config_file.write_text(json.dumps(file_config)) + + # Create ConfigManager and load config + config_manager = ConfigManager() + config_manager.config_dir = config_dir + config_manager.config_file = config_file + + loaded_config = config_manager.load_config() + + # Should use the file-based configuration + assert loaded_config.projects["main"] == file_path