-
-
Notifications
You must be signed in to change notification settings - Fork 29
Dockerized the project #113
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| # Git | ||
| .git | ||
| .gitignore | ||
|
|
||
| # Node | ||
| node_modules | ||
| npm-debug.log | ||
| dist | ||
| coverage | ||
|
|
||
| # Python | ||
| __pycache__ | ||
| *.pyc | ||
| *.pyo | ||
| *.pyd | ||
| .Python | ||
| env | ||
| venv | ||
| pip-log.txt | ||
| pip-delete-this-directory.txt | ||
| .tox | ||
| .coverage | ||
| .coverage.* | ||
| .cache | ||
| nosetests.xml | ||
| coverage.xml | ||
| *.cover | ||
| *.log | ||
| .pytest_cache | ||
|
|
||
| # Docker | ||
| docker-compose.yml | ||
| Dockerfile | ||
| .dockerignore | ||
|
|
||
| # Environment | ||
| .env | ||
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -352,7 +352,17 @@ This section explains how to run Coderrr locally from source for development or | |
| git clone https://github.com/Akash-nath29/Coderrr.git | ||
| cd Coderrr | ||
| ``` | ||
| ### 2. Backend Setup (FastAPI) | ||
| ### 2. Backend Setup | ||
| You can run the backend using Docker (recommended) or set it up manually. | ||
|
|
||
| #### Option A: Docker (Recommended) | ||
|
|
||
| ```bash | ||
| docker compose up --build | ||
| ``` | ||
| The backend will be started at `http://localhost:5000` with hot-reloading enabled. | ||
|
Comment on lines
+358
to
+363
|
||
|
|
||
| #### Option B: Manual Setup (FastAPI) | ||
|
|
||
| ```bash | ||
| cd backend | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,27 @@ | ||||||||||
| # Use official Python 3.11 slim image | ||||||||||
| FROM python:3.11-slim | ||||||||||
|
Comment on lines
+1
to
+2
|
||||||||||
| # Use official Python 3.11 slim image | |
| FROM python:3.11-slim | |
| # Use official Python 3.11.x slim image (pinned patch version for reproducibility) | |
| FROM python:3.11.8-slim |
Copilot
AI
Jan 27, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The PR description extensively discusses Skills.md support, prompt construction changes, and unit tests (mentioning files like src/agent.js, Skills.md, and skills.test.js), but none of these changes are present in the actual diff. The diff only contains Docker-related files. This creates confusion about what is actually being changed in this PR. Please update the PR description to accurately reflect only the Docker-related changes, or include the missing Skills.md-related changes in the PR if they should be part of it.
Copilot
AI
Jan 27, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The commented line suggests that gcc might be needed for building some Python packages, but it's unclear if this is actually required. If any of the dependencies in requirements.txt need compilation (like some cryptography or data science libraries), the build will fail without these system dependencies. Either uncomment this line if build tools are needed, or remove the comment if all dependencies are pure Python packages. You can verify by testing the Docker build.
| # Install system dependencies (if any needed, e.g. for build tools) | |
| # RUN apt-get update && apt-get install -y --no-install-recommends gcc && rm -rf /var/lib/apt/lists/* | |
| # Install system dependencies required for building Python packages | |
| RUN apt-get update && apt-get install -y --no-install-recommends gcc && rm -rf /var/lib/apt/lists/* |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,21 @@ | ||||||||||||||||||
|
|
||||||||||||||||||
|
|
||||||||||||||||||
|
Comment on lines
+1
to
+2
|
||||||||||||||||||
Copilot
AI
Jan 27, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The docker-compose.yml file is missing a version specification. While Docker Compose v2+ doesn't strictly require a version field (it's optional), it's considered best practice to include it for clarity and backwards compatibility. Add version: '3.8' or version: '3.9' at the top of the file (after removing the empty lines) to explicitly declare the Compose file format version being used.
| version: '3.9' |
Copilot
AI
Jan 27, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The volume mount ./backend:/app overrides the entire /app directory in the container, including the Python dependencies installed by the Dockerfile. This defeats the purpose of the pip install step in the Dockerfile. For development with hot-reloading to work properly, you have two options:
- Use a named volume for Python packages: add
- backend-deps:/usr/local/lib/python3.11/site-packagesto preserve installed packages - Document that developers need to run
pip install -r requirements.txtin their local backend/ directory
Without this, the container will fail to start because the required Python packages won't be available at runtime.
Copilot
AI
Jan 27, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The docker-compose.yml file references a .env file that should be located at the repository root, but this .env file path is not documented in the README. Users following the Docker setup instructions will need to create this .env file with the appropriate backend environment variables (like GITHUB_TOKEN, GITHUB_MODELS_ENDPOINT, GITHUB_MODEL, etc.). Consider adding explicit instructions in the README about creating the root .env file from .env.example before running docker compose.
| # Load environment variables from .env file if it exists | |
| env_file: | |
| - .env |
Copilot
AI
Jan 27, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider adding a health check to monitor the backend service status. The backend has a /health endpoint (see backend/main.py line 267) that can be used. Add a healthcheck configuration like:
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:5000/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 10s
This will help Docker monitor the service and restart it if it becomes unhealthy. Note: you'll need to add curl to the Dockerfile if not already present.
| command: uvicorn main:app --host 0.0.0.0 --port 5000 --reload | |
| command: uvicorn main:app --host 0.0.0.0 --port 5000 --reload | |
| healthcheck: | |
| test: ["CMD", "curl", "-f", "http://localhost:5000/health"] | |
| interval: 30s | |
| timeout: 10s | |
| retries: 3 | |
| start_period: 10s |
Copilot
AI
Jan 27, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The restart policy unless-stopped is appropriate for development, but it means the container will automatically restart on system boot. For a development environment, restart: "no" or restart: on-failure might be more appropriate to avoid unnecessary resource usage when not actively developing. The current setting is more suited for production deployments.
| restart: unless-stopped | |
| restart: "no" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,194 @@ | ||
| const fs = require('fs'); | ||
| const path = require('path'); | ||
| const Agent = require('../../src/agent'); | ||
|
|
||
| // Mock dependencies | ||
| jest.mock('../../src/ui', () => ({ | ||
| info: jest.fn(), | ||
| warning: jest.fn(), | ||
| success: jest.fn(), | ||
| error: jest.fn(), | ||
| spinner: jest.fn(() => ({ start: jest.fn(), stop: jest.fn() })), | ||
| section: jest.fn(), | ||
| space: jest.fn(), | ||
| confirm: jest.fn(), | ||
| displayFileOp: jest.fn(), | ||
| displayDiff: jest.fn() | ||
| })); | ||
|
|
||
| jest.mock('../../src/configManager', () => ({ | ||
| initializeProjectStorage: jest.fn(), | ||
| loadProjectMemory: jest.fn(() => []), | ||
| saveProjectMemory: jest.fn(), | ||
| clearProjectMemory: jest.fn(), | ||
| getConfig: jest.fn() | ||
| })); | ||
|
|
||
| jest.mock('axios'); | ||
|
|
||
| describe('Skills.md Loading', () => { | ||
| let tempDir; | ||
| const originalCwd = process.cwd(); | ||
|
|
||
| beforeEach(() => { | ||
| jest.clearAllMocks(); | ||
| // Create a temporary directory for testing | ||
| tempDir = path.join(originalCwd, 'test-temp-skills'); | ||
| if (!fs.existsSync(tempDir)) { | ||
| fs.mkdirSync(tempDir, { recursive: true }); | ||
| } | ||
| }); | ||
|
|
||
| afterEach(() => { | ||
| // Clean up temporary directory | ||
| if (fs.existsSync(tempDir)) { | ||
| const files = fs.readdirSync(tempDir); | ||
| for (const file of files) { | ||
| fs.unlinkSync(path.join(tempDir, file)); | ||
| } | ||
| fs.rmdirSync(tempDir); | ||
| } | ||
| }); | ||
|
|
||
| describe('loadSkillsPrompt', () => { | ||
| it('should load Skills.md when it exists', () => { | ||
| const skillsContent = '# Frontend Skills\n- Use modern design patterns'; | ||
| fs.writeFileSync(path.join(tempDir, 'Skills.md'), skillsContent); | ||
|
|
||
| const agent = new Agent({ workingDir: tempDir }); | ||
| agent.loadSkillsPrompt(); | ||
|
|
||
| expect(agent.skillsPrompt).toBe(skillsContent); | ||
| }); | ||
|
|
||
| it('should not set skillsPrompt when Skills.md does not exist', () => { | ||
| const agent = new Agent({ workingDir: tempDir }); | ||
| agent.loadSkillsPrompt(); | ||
|
|
||
| expect(agent.skillsPrompt).toBeNull(); | ||
| }); | ||
|
|
||
| it('should handle empty Skills.md gracefully', () => { | ||
| fs.writeFileSync(path.join(tempDir, 'Skills.md'), ' '); | ||
|
|
||
| const agent = new Agent({ workingDir: tempDir }); | ||
| agent.loadSkillsPrompt(); | ||
|
|
||
| expect(agent.skillsPrompt).toBe(''); | ||
| }); | ||
| }); | ||
|
|
||
| describe('loadCustomPrompt', () => { | ||
| it('should load Coderrr.md when it exists', () => { | ||
| const taskContent = '# Task: Build landing page'; | ||
| fs.writeFileSync(path.join(tempDir, 'Coderrr.md'), taskContent); | ||
|
|
||
| const agent = new Agent({ workingDir: tempDir }); | ||
| agent.loadCustomPrompt(); | ||
|
|
||
| expect(agent.customPrompt).toBe(taskContent); | ||
| }); | ||
|
|
||
| it('should not set customPrompt when Coderrr.md does not exist', () => { | ||
| const agent = new Agent({ workingDir: tempDir }); | ||
| agent.loadCustomPrompt(); | ||
|
|
||
| expect(agent.customPrompt).toBeNull(); | ||
| }); | ||
| }); | ||
|
|
||
| describe('Prompt Priority Order', () => { | ||
| it('should construct prompt with Skills.md before Coderrr.md', () => { | ||
| const skillsContent = '# Skills\n- Be modern'; | ||
| const taskContent = '# Task\n- Build a form'; | ||
| fs.writeFileSync(path.join(tempDir, 'Skills.md'), skillsContent); | ||
| fs.writeFileSync(path.join(tempDir, 'Coderrr.md'), taskContent); | ||
|
|
||
| const agent = new Agent({ workingDir: tempDir, scanOnFirstRequest: false }); | ||
| agent.loadSkillsPrompt(); | ||
| agent.loadCustomPrompt(); | ||
|
|
||
| // Simulate prompt construction (from chat method logic) | ||
| let enhancedPrompt = 'User request'; | ||
|
|
||
| // Task guidance first (will be wrapped by skills) | ||
| if (agent.customPrompt) { | ||
| enhancedPrompt = `[TASK GUIDANCE]\n${agent.customPrompt}\n\n[USER REQUEST]\n${enhancedPrompt}`; | ||
| } | ||
|
|
||
| // Skills prepended (comes before everything else) | ||
| if (agent.skillsPrompt) { | ||
| enhancedPrompt = `[SKILLS]\n${agent.skillsPrompt}\n\n${enhancedPrompt}`; | ||
| } | ||
|
|
||
| // Verify priority order: Skills comes first | ||
| expect(enhancedPrompt.startsWith('[SKILLS]')).toBe(true); | ||
| expect(enhancedPrompt.indexOf('[SKILLS]')).toBeLessThan(enhancedPrompt.indexOf('[TASK GUIDANCE]')); | ||
| expect(enhancedPrompt.indexOf('[TASK GUIDANCE]')).toBeLessThan(enhancedPrompt.indexOf('[USER REQUEST]')); | ||
| }); | ||
|
|
||
| it('should work with only Skills.md (no Coderrr.md)', () => { | ||
| const skillsContent = '# Skills\n- Be creative'; | ||
| fs.writeFileSync(path.join(tempDir, 'Skills.md'), skillsContent); | ||
|
|
||
| const agent = new Agent({ workingDir: tempDir, scanOnFirstRequest: false }); | ||
| agent.loadSkillsPrompt(); | ||
| agent.loadCustomPrompt(); | ||
|
|
||
| let enhancedPrompt = 'User request'; | ||
|
|
||
| if (agent.customPrompt) { | ||
| enhancedPrompt = `[TASK GUIDANCE]\n${agent.customPrompt}\n\n[USER REQUEST]\n${enhancedPrompt}`; | ||
| } | ||
|
|
||
| if (agent.skillsPrompt) { | ||
| enhancedPrompt = `[SKILLS]\n${agent.skillsPrompt}\n\n${enhancedPrompt}`; | ||
| } | ||
|
|
||
| expect(enhancedPrompt).toContain('[SKILLS]'); | ||
| expect(enhancedPrompt).not.toContain('[TASK GUIDANCE]'); | ||
| expect(enhancedPrompt).toContain('User request'); | ||
| }); | ||
|
|
||
| it('should work with only Coderrr.md (no Skills.md)', () => { | ||
| const taskContent = '# Task\n- Do something'; | ||
| fs.writeFileSync(path.join(tempDir, 'Coderrr.md'), taskContent); | ||
|
|
||
| const agent = new Agent({ workingDir: tempDir, scanOnFirstRequest: false }); | ||
| agent.loadSkillsPrompt(); | ||
| agent.loadCustomPrompt(); | ||
|
|
||
| let enhancedPrompt = 'User request'; | ||
|
|
||
| if (agent.customPrompt) { | ||
| enhancedPrompt = `[TASK GUIDANCE]\n${agent.customPrompt}\n\n[USER REQUEST]\n${enhancedPrompt}`; | ||
| } | ||
|
|
||
| if (agent.skillsPrompt) { | ||
| enhancedPrompt = `[SKILLS]\n${agent.skillsPrompt}\n\n${enhancedPrompt}`; | ||
| } | ||
|
|
||
| expect(enhancedPrompt).not.toContain('[SKILLS]'); | ||
| expect(enhancedPrompt).toContain('[TASK GUIDANCE]'); | ||
| expect(enhancedPrompt).toContain('User request'); | ||
| }); | ||
|
|
||
| it('should work with neither Skills.md nor Coderrr.md', () => { | ||
| const agent = new Agent({ workingDir: tempDir, scanOnFirstRequest: false }); | ||
| agent.loadSkillsPrompt(); | ||
| agent.loadCustomPrompt(); | ||
|
|
||
| let enhancedPrompt = 'User request'; | ||
|
|
||
| if (agent.customPrompt) { | ||
| enhancedPrompt = `[TASK GUIDANCE]\n${agent.customPrompt}\n\n[USER REQUEST]\n${enhancedPrompt}`; | ||
| } | ||
|
|
||
| if (agent.skillsPrompt) { | ||
| enhancedPrompt = `[SKILLS]\n${agent.skillsPrompt}\n\n${enhancedPrompt}`; | ||
| } | ||
|
|
||
| expect(enhancedPrompt).toBe('User request'); | ||
| }); | ||
| }); | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The .dockerignore file is placed in the repository root, but the Docker build context in docker-compose.yml is set to
./backend. Docker will look for .dockerignore in the build context directory (backend/), not in the repository root. This .dockerignore file will not be used during the Docker build. Either move this file to the backend/ directory, or change the build context to the repository root and adjust the Dockerfile accordingly.