fix: Add explicit workspace cleanup on project creation failure - #77
Conversation
WalkthroughThis change enhances workspace cleanup in the API endpoint's exception handler for failed project creation. When workspace initialization fails, the API now explicitly removes the orphaned workspace directory from the filesystem in addition to deleting the database record, using Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes
Poem
Pre-merge checks and finishing touches✅ Passed checks (5 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Code Review - PR #77: Workspace Cleanup on Project Creation FailureSummary✅ APPROVED - This is a well-implemented defense-in-depth fix that addresses a legitimate edge case in workspace cleanup. Strengths1. Excellent Problem Identification 🎯
2. High-Quality Tests ✅The test suite is exemplary:
3. Code Quality 💎
4. Proper Exception Handlingtry:
shutil.rmtree(workspace_path)
logger.info(f"Cleaned up orphaned workspace: {workspace_path}")
except Exception as cleanup_error:
logger.error(f"Failed to clean up workspace {workspace_path}: {cleanup_error}")
Minor Observations1. Variable Name Shadowing
|
Resolves #7 Changes: - Add explicit workspace cleanup in API endpoint exception handler - Import shutil for directory removal - Add logging for cleanup attempts - Defense-in-depth: API endpoint removes orphaned workspaces even if WorkspaceManager cleanup fails Tests: - Add test_workspace_cleanup.py with 3 comprehensive tests - All 46 workspace tests pass - All 11 project creation API tests pass Implementation follows proposed solution from issue #7
4a2c8ae to
bf53491
Compare
Code Review: Workspace Cleanup on Project Creation FailureSummaryThis PR successfully addresses issue #7 by adding defense-in-depth workspace cleanup to the API endpoint. The implementation is solid, well-tested, and follows the proposed solution exactly. ✅ Strengths1. Excellent Test Coverage
2. Defense-in-Depth Pattern
3. Proper Error Handling
4. Code Quality
🔍 Observations & Suggestions1. Path Traversal Security (Low Priority)The current implementation constructs paths as: workspace_path = app.state.workspace_manager.workspace_root / str(project_id)Observation: Suggestion: Consider adding a path validation assertion: workspace_path = app.state.workspace_manager.workspace_root / str(project_id)
# Sanity check: ensure workspace_path is actually a child of workspace_root
if not workspace_path.resolve().is_relative_to(app.state.workspace_manager.workspace_root.resolve()):
logger.error(f"Path traversal attempt detected: {workspace_path}")
raise HTTPException(status_code=500, detail="Invalid workspace path")Rationale: Although not exploitable currently (project_id is integer-controlled), this adds another layer of security if the codebase evolves. 2. Duplicate Cleanup Logic (Minor)Both Trade-off Analysis:
Suggestion (optional): Document this pattern in a comment or consider extracting to a shared utility if more endpoints need workspace cleanup in the future. 3. Test Mocking Complexity (Minor)The test Observation: The mock directly manipulates filesystem state. Consider adding a comment explaining why this approach was chosen over simpler mocking. # Simulate workspace creation failure with incomplete cleanup
# We create the directory manually (not via WorkspaceManager) to test
# that the API endpoint can handle orphaned directories left by failed cleanup
def mock_create_with_partial_workspace(**kwargs):
...4. Logging ConsistencyCurrent logging: logger.info(f"Cleaned up orphaned workspace: {workspace_path}")
logger.error(f"Failed to clean up workspace {workspace_path}: {cleanup_error}")Suggestion: Consider adding a log entry at the start of cleanup attempt for easier debugging: if workspace_path.exists():
logger.warning(f"Orphaned workspace detected, attempting cleanup: {workspace_path}")
try:
shutil.rmtree(workspace_path)
logger.info(f"Successfully cleaned up orphaned workspace: {workspace_path}")
except Exception as cleanup_error:
logger.error(f"Failed to clean up workspace {workspace_path}: {cleanup_error}")This makes it easier to distinguish between:
🎯 VerdictRecommendation: ✅ APPROVE with minor suggestions This PR:
The suggestions above are non-blocking and can be addressed in future PRs if desired. The current implementation is production-ready. 📊 Test Results VerificationAccording to PR description:
Great job on comprehensive testing! 🎉 Reviewed by: Claude (Code Review Agent) |
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
tests/api/test_workspace_cleanup.py (1)
12-51: Consider marking as integration test due to network dependency.This test makes an actual network call to GitHub (
https://github.com/nonexistent/invalid-repo-12345.git), which could cause flakiness if:
- Network is unavailable
- GitHub rate-limits the request
- DNS resolution fails
Since it's marked
@pytest.mark.unit, consider either:
- Changing to
@pytest.mark.integrationto reflect the external dependency- Mocking the git clone at the
WorkspaceManagerlevel (similar to test 2)The test logic itself is correct and valuable for verifying the real end-to-end cleanup path.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
codeframe/ui/server.py(2 hunks)tests/api/test_workspace_cleanup.py(1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.py: Use Python 3.11+ with async/await syntax, asyncio, and AsyncAnthropic for AI integrations in backend code
Lint code with ruff check . before committing; fix all style violations to maintain consistent code quality across the Python codebase
Use type hints with Python 3.11+ throughout all Python code; run type checking as part of quality gates to catch type errors before task completion
Files:
tests/api/test_workspace_cleanup.pycodeframe/ui/server.py
🧬 Code graph analysis (2)
tests/api/test_workspace_cleanup.py (3)
tests/api/conftest.py (1)
api_client(42-89)codeframe/core/config.py (1)
get(290-302)codeframe/workspace/manager.py (1)
create_workspace(26-71)
codeframe/ui/server.py (6)
tests/agents/test_review_worker_agent.py (2)
db(19-23)project_id(26-32)tests/lib/test_metrics_tracker.py (1)
db(14-34)tests/integration/test_quality_gates_integration.py (2)
db(24-29)project_id(72-78)tests/agents/test_review_agent.py (1)
db(19-39)tests/planning/test_prd_generation.py (2)
db(16-21)project_id(25-34)codeframe/persistence/database.py (1)
delete_project(1191-1199)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: Frontend Unit Tests
- GitHub Check: Backend Unit Tests
- GitHub Check: claude-review
🔇 Additional comments (4)
codeframe/ui/server.py (2)
15-15: LGTM!The
shutilimport is appropriately added to support the explicitshutil.rmtree()cleanup in the exception handler.
359-374: Well-implemented defense-in-depth cleanup.The explicit workspace cleanup logic correctly addresses Issue #7:
- Path derivation matches
WorkspaceManager's pattern (workspace_root / str(project_id))- Existence check prevents unnecessary operations
- Try/except ensures cleanup failures don't mask the original error
- Logging provides visibility for debugging orphaned workspace issues
- The original exception detail is preserved in the HTTP 500 response
The
project_idis an integer from the database, so path traversal is not a concern here.tests/api/test_workspace_cleanup.py (2)
53-115: Well-designed test for the critical failure scenario.This test correctly validates the core Issue #7 fix by:
- Simulating a partial workspace creation (directory + file created)
- Simulating
WorkspaceManager's cleanup failure (raising without removing the directory)- Verifying that the API endpoint's explicit cleanup removes the orphaned workspace
The mock accurately reproduces the failure mode described in the issue. The inline
patchimport is acceptable, though moving it to module-level would align with typical Python conventions.
117-145: Good sanity check for successful path.This test correctly validates that the cleanup logic doesn't interfere with successful project creation.
The test is properly isolated. The conftest.py includes an autouse fixture
clean_database_between_teststhat runs after each test, clearing all database records and the workspace directory. This ensures tests are independent regardless of execution order.
Description
Fixes #7 - Improves workspace cleanup when project creation fails during workspace initialization.
Problem
When project creation fails during workspace initialization, the API endpoint only cleaned up the database record but didn't explicitly verify that the workspace directory was removed. If WorkspaceManager's internal cleanup failed or was interrupted, orphaned workspace directories could accumulate.
Solution
Added explicit workspace cleanup in the API endpoint's exception handler as a defense-in-depth measure:
Changes
codeframe/ui/server.py:
import shutilfor directory removaltests/api/test_workspace_cleanup.py (NEW):
test_workspace_cleanup_after_git_clone_failure: Verifies cleanup on git failuretest_workspace_cleanup_when_manager_cleanup_fails: Critical test simulating WorkspaceManager cleanup failuretest_no_cleanup_on_successful_creation: Sanity check for normal operationTest Results
✅ 3 new tests - All passing
✅ 46 workspace tests - All passing
✅ 11 project creation API tests - All passing
✅ Total: 60 tests passing, 0 failures
TDD Approach
Implementation Details
The fix implements the proposed solution from issue #7:
Related
Summary by CodeRabbit
Bug Fixes
Tests
✏️ Tip: You can customize this high-level summary in your review settings.