Skip to content

Commit 0738138

Browse files
jsbattigclaude
andcommitted
fix: resolve config_fixer import error and bump version to 6.3.0
Fixed critical import error in cidx fix-config command caused by outdated references after refactoring. Updated FileManager import to FileIdentifier with correct method calls. Added comprehensive test coverage and updated all version references. Changes: - Fix config_fixer.py import from file_manager to file_identifier - Update method calls: get_indexable_files() -> get_current_files() - Add FileIdentifier constructor with codebase_dir parameter - Create test_config_fixer_file_analyzer.py with 5 comprehensive tests - Bump version 6.2.0 -> 6.3.0 in __init__.py, README.md, RELEASE_NOTES.md - Add detailed v6.3.0 release notes documenting the bug fix Testing: 2053 tests passing, manual E2E validation in /tmp directories 🤖 Generated with [Claude Code](https://claude.ai/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 0e512a8 commit 0738138

File tree

5 files changed

+247
-7
lines changed

5 files changed

+247
-7
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ cidx setup-global-registry
6565
```bash
6666
python3 -m venv code-indexer-env
6767
source code-indexer-env/bin/activate
68-
pip install git+https://github.com/jsbattig/code-indexer.git@v6.2.0
68+
pip install git+https://github.com/jsbattig/code-indexer.git@v6.3.0
6969

7070
# Setup global registry (standalone command - requires sudo)
7171
cidx setup-global-registry

RELEASE_NOTES.md

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,84 @@
11
# Release Notes
22

3+
## Version 6.3.0 - Config Fixer Import Bug Fix
4+
5+
**Release Date**: October 16, 2025
6+
7+
### 🐛 Bug Fix
8+
9+
This release fixes a critical import error in the `cidx fix-config` command caused by outdated code references after a refactoring.
10+
11+
#### Problem
12+
13+
The `config_fixer.py` module attempted to import a non-existent `FileManager` class from a non-existent `file_manager` module:
14+
15+
```python
16+
from code_indexer.services.file_manager import FileManager
17+
```
18+
19+
This caused the following error when running `cidx fix-config`:
20+
21+
```
22+
Warning: Could not analyze project files: No module named 'code_indexer.services.file_manager'
23+
```
24+
25+
#### Solution
26+
27+
**Updated Import and Method Calls**: Fixed the import to use the correct `FileIdentifier` class from the `file_identifier` module:
28+
29+
```python
30+
# Before (broken):
31+
from code_indexer.services.file_manager import FileManager
32+
file_manager = FileManager(config)
33+
actual_files = file_manager.get_indexable_files()
34+
35+
# After (fixed):
36+
from code_indexer.services.file_identifier import FileIdentifier
37+
file_identifier = FileIdentifier(codebase_dir, config)
38+
actual_files = file_identifier.get_current_files()
39+
```
40+
41+
#### Impact
42+
43+
**Before Fix**:
44+
- `cidx fix-config` displayed import error warning
45+
- File analysis section of config validation failed silently
46+
- Configuration repair operations incomplete
47+
48+
**After Fix**:
49+
- `cidx fix-config` runs without import errors
50+
- File analysis works correctly
51+
- Configuration validation completes successfully
52+
53+
#### Testing Verification
54+
55+
**Unit Tests**:
56+
- Added comprehensive test suite: `tests/unit/services/test_config_fixer_file_analyzer.py`
57+
- 5 new tests validating correct `FileIdentifier` usage
58+
- All 2053 tests passing (fast-automation.sh)
59+
60+
**Manual Testing**:
61+
- Tested in fresh project directories (/tmp)
62+
- Validated with git repositories
63+
- Tested with empty directories
64+
- Confirmed no import errors in all scenarios
65+
66+
**Quality Checks**:
67+
- ✅ Ruff linting: Clean
68+
- ✅ MyPy type checking: Clean
69+
- ✅ Black formatting: Applied
70+
- ✅ Code review: Approved for production
71+
72+
#### Files Modified
73+
74+
**Production Code**:
75+
- `src/code_indexer/services/config_fixer.py` - Fixed import and method calls (lines 199-202)
76+
77+
**Tests**:
78+
- `tests/unit/services/test_config_fixer_file_analyzer.py` - New comprehensive test suite (5 tests)
79+
80+
---
81+
382
## Version 6.2.0 - Alpine Linux Container Support
483

584
**Release Date**: October 15, 2025

src/code_indexer/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,5 @@
55
to provide code search capabilities.
66
"""
77

8-
__version__ = "6.2.0"
8+
__version__ = "6.3.0"
99
__author__ = "Seba Battig"

src/code_indexer/services/config_fixer.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -196,16 +196,20 @@ class FileSystemAnalyzer:
196196
def analyze_project_files(codebase_dir: Path, config: Config) -> Dict[str, Any]:
197197
"""Analyze actual project files to determine what should be indexed."""
198198
try:
199-
from code_indexer.services.file_manager import FileManager
199+
from code_indexer.services.file_identifier import FileIdentifier
200200

201-
file_manager = FileManager(config)
202-
actual_files = file_manager.get_indexable_files()
201+
file_identifier = FileIdentifier(codebase_dir, config)
202+
actual_files = file_identifier.get_current_files()
203203

204204
return {
205205
"total_files_to_index": len(actual_files),
206-
"discovered_files": [str(f) for f in actual_files],
206+
"discovered_files": list(actual_files.keys()),
207207
"file_extensions_found": list(
208-
set(f.suffix.lstrip(".") for f in actual_files if f.suffix)
208+
set(
209+
Path(f).suffix.lstrip(".")
210+
for f in actual_files.keys()
211+
if Path(f).suffix
212+
)
209213
),
210214
}
211215

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
"""
2+
Unit tests for ConfigFixer FileSystemAnalyzer.
3+
4+
Tests verify correct usage of FileIdentifier for analyzing project files.
5+
"""
6+
7+
from unittest.mock import Mock, patch, MagicMock
8+
from code_indexer.services.config_fixer import FileSystemAnalyzer
9+
from code_indexer.config import Config
10+
11+
12+
class TestFileSystemAnalyzer:
13+
"""Test FileSystemAnalyzer.analyze_project_files() method."""
14+
15+
def test_analyze_project_files_uses_file_identifier(self, tmp_path):
16+
"""Verify that analyze_project_files uses FileIdentifier correctly."""
17+
# Create a mock config
18+
config = Mock(spec=Config)
19+
codebase_dir = tmp_path / "test_project"
20+
codebase_dir.mkdir()
21+
22+
# Create some test files
23+
(codebase_dir / "test.py").write_text("print('hello')")
24+
(codebase_dir / "test2.py").write_text("print('world')")
25+
26+
# Mock FileIdentifier to verify it's called correctly
27+
# Note: Import is inside method, so we mock it at the file_identifier module level
28+
with patch(
29+
"code_indexer.services.file_identifier.FileIdentifier"
30+
) as mock_file_identifier:
31+
# Setup mock to return expected data structure
32+
mock_instance = MagicMock()
33+
mock_file_identifier.return_value = mock_instance
34+
mock_instance.get_current_files.return_value = {
35+
"test.py": {"file_path": "test.py", "file_hash": "abc123"},
36+
"test2.py": {"file_path": "test2.py", "file_hash": "def456"},
37+
}
38+
39+
# Call the method under test
40+
result = FileSystemAnalyzer.analyze_project_files(codebase_dir, config)
41+
42+
# Verify FileIdentifier was instantiated with correct parameters
43+
mock_file_identifier.assert_called_once_with(codebase_dir, config)
44+
45+
# Verify get_current_files() was called (not get_indexable_files())
46+
mock_instance.get_current_files.assert_called_once()
47+
48+
# Verify the result has the expected structure
49+
assert "total_files_to_index" in result
50+
assert result["total_files_to_index"] == 2
51+
assert "discovered_files" in result
52+
assert len(result["discovered_files"]) == 2
53+
54+
def test_analyze_project_files_returns_correct_structure(self, tmp_path):
55+
"""Verify analyze_project_files returns the expected dictionary structure."""
56+
config = Mock(spec=Config)
57+
codebase_dir = tmp_path / "test_project"
58+
codebase_dir.mkdir()
59+
60+
# Create test files with different extensions
61+
(codebase_dir / "file1.py").write_text("# python")
62+
(codebase_dir / "file2.js").write_text("// javascript")
63+
(codebase_dir / "file3.md").write_text("# markdown")
64+
65+
with patch(
66+
"code_indexer.services.file_identifier.FileIdentifier"
67+
) as mock_file_identifier:
68+
mock_instance = MagicMock()
69+
mock_file_identifier.return_value = mock_instance
70+
mock_instance.get_current_files.return_value = {
71+
"file1.py": {"file_path": "file1.py"},
72+
"file2.js": {"file_path": "file2.js"},
73+
"file3.md": {"file_path": "file3.md"},
74+
}
75+
76+
result = FileSystemAnalyzer.analyze_project_files(codebase_dir, config)
77+
78+
# Verify all required keys exist
79+
assert "total_files_to_index" in result
80+
assert "discovered_files" in result
81+
assert "file_extensions_found" in result
82+
83+
# Verify correct counts
84+
assert result["total_files_to_index"] == 3
85+
assert len(result["discovered_files"]) == 3
86+
87+
# Verify file extensions are extracted
88+
extensions = result["file_extensions_found"]
89+
assert isinstance(extensions, list)
90+
assert set(extensions) == {"py", "js", "md"}
91+
92+
def test_analyze_project_files_handles_empty_directory(self, tmp_path):
93+
"""Verify analyze_project_files handles directory with no indexable files."""
94+
config = Mock(spec=Config)
95+
codebase_dir = tmp_path / "empty_project"
96+
codebase_dir.mkdir()
97+
98+
with patch(
99+
"code_indexer.services.file_identifier.FileIdentifier"
100+
) as mock_file_identifier:
101+
mock_instance = MagicMock()
102+
mock_file_identifier.return_value = mock_instance
103+
mock_instance.get_current_files.return_value = {}
104+
105+
result = FileSystemAnalyzer.analyze_project_files(codebase_dir, config)
106+
107+
assert result["total_files_to_index"] == 0
108+
assert result["discovered_files"] == []
109+
assert result["file_extensions_found"] == []
110+
111+
def test_analyze_project_files_handles_exceptions_gracefully(self, tmp_path):
112+
"""Verify analyze_project_files returns safe defaults on exceptions."""
113+
config = Mock(spec=Config)
114+
codebase_dir = tmp_path / "test_project"
115+
codebase_dir.mkdir()
116+
117+
with patch(
118+
"code_indexer.services.file_identifier.FileIdentifier"
119+
) as mock_file_identifier:
120+
# Simulate an exception during file identification
121+
mock_file_identifier.side_effect = Exception("File system error")
122+
123+
result = FileSystemAnalyzer.analyze_project_files(codebase_dir, config)
124+
125+
# Should return safe defaults on error
126+
assert result["total_files_to_index"] == 0
127+
assert result["discovered_files"] == []
128+
assert result["file_extensions_found"] == []
129+
130+
def test_analyze_project_files_extracts_file_paths_correctly(self, tmp_path):
131+
"""Verify analyze_project_files extracts file paths from get_current_files() result."""
132+
config = Mock(spec=Config)
133+
codebase_dir = tmp_path / "test_project"
134+
codebase_dir.mkdir()
135+
136+
with patch(
137+
"code_indexer.services.file_identifier.FileIdentifier"
138+
) as mock_file_identifier:
139+
mock_instance = MagicMock()
140+
mock_file_identifier.return_value = mock_instance
141+
142+
# get_current_files() returns Dict[str, Dict[str, Any]]
143+
# Keys are file paths (strings), values are metadata dicts
144+
mock_instance.get_current_files.return_value = {
145+
"src/main.py": {"file_hash": "hash1"},
146+
"tests/test_main.py": {"file_hash": "hash2"},
147+
"README.md": {"file_hash": "hash3"},
148+
}
149+
150+
result = FileSystemAnalyzer.analyze_project_files(codebase_dir, config)
151+
152+
# Verify file paths are extracted from dictionary keys
153+
discovered = result["discovered_files"]
154+
assert len(discovered) == 3
155+
assert "src/main.py" in discovered
156+
assert "tests/test_main.py" in discovered
157+
assert "README.md" in discovered

0 commit comments

Comments
 (0)