From 58595f2b4e1d4514a5b4d8b07d631c59c50f8c63 Mon Sep 17 00:00:00 2001 From: Jet Li Date: Sun, 22 Jun 2025 11:31:24 -0700 Subject: [PATCH] feat(cicd_ai): add features on integrating with cicd and openai --- devbox/cli/BUILD_DEPLOY_FEATURES.md | 308 +++ devbox/cli/CICD_INTEGRATION.md | 429 ++++ devbox/cli/LOCAL_CODE_PACKAGING.md | 337 +++ devbox/cli/OPENAI_CODE_REVIEW.md | 369 ++++ devbox/cli/devbox | 3042 ++++++++++++++++++++++++++- 5 files changed, 4484 insertions(+), 1 deletion(-) create mode 100644 devbox/cli/BUILD_DEPLOY_FEATURES.md create mode 100644 devbox/cli/CICD_INTEGRATION.md create mode 100644 devbox/cli/LOCAL_CODE_PACKAGING.md create mode 100644 devbox/cli/OPENAI_CODE_REVIEW.md diff --git a/devbox/cli/BUILD_DEPLOY_FEATURES.md b/devbox/cli/BUILD_DEPLOY_FEATURES.md new file mode 100644 index 0000000..9d74a03 --- /dev/null +++ b/devbox/cli/BUILD_DEPLOY_FEATURES.md @@ -0,0 +1,308 @@ +# DevBox CLI Build & Deploy Features + +## Overview + +The DevBox CLI now includes powerful build and deploy capabilities that enable developers to build Docker images locally and deploy them to Freeleaps environments through API integration. This creates a complete development-to-deployment workflow. + +## Architecture + +### Build System +``` +Local Source Code → Docker Build → Local Image Registry → Deployment +``` + +### Deployment System +``` +Local Images → Freeleaps API → Environment Deployment → Status Monitoring +``` + +## New Commands + +### 1. `devbox build` - Local Docker Building + +Builds Docker images for Freeleaps components locally. + +#### Usage +```bash +# Build all components +devbox build + +# Build specific component +devbox build --component=chat + +# Build with custom image tag +devbox build --image-tag=v1.2.3 + +# Build with custom repository +devbox build --image-repo=my-registry --image-tag=latest +``` + +#### Features +- **Multi-component building**: Build all components or specific ones +- **Automatic tagging**: Timestamp-based tags or custom tags +- **Build validation**: Checks for Dockerfiles and build context +- **Retry mechanisms**: Handles transient build failures +- **Progress reporting**: Detailed build status for each component + +### 2. `devbox deploy` - Deployment to Freeleaps + +Deploys Docker images to Freeleaps environments via API. + +#### Usage +```bash +# Deploy all components +devbox deploy --image-tag=v1.2.3 --auth-token=your-token + +# Deploy specific component +devbox deploy --component=chat --image-tag=v1.2.3 --auth-token=your-token + +# Deploy to production +devbox deploy --environment=production --image-tag=v1.2.3 --auth-token=your-token + +# Deploy with custom API endpoint +devbox deploy --api-endpoint=https://api.freeleaps.com --image-tag=v1.2.3 --auth-token=your-token +``` + +#### Features +- **Environment targeting**: Deploy to staging, production, or custom environments +- **API integration**: Secure deployment via Freeleaps API +- **Status monitoring**: Track deployment progress +- **Authentication**: Bearer token-based authentication +- **Error handling**: Comprehensive error reporting and recovery + +### 3. `devbox build-deploy` - Combined Workflow + +Builds and deploys in a single command for maximum efficiency. + +#### Usage +```bash +# Build and deploy all components +devbox build-deploy --auth-token=your-token + +# Build and deploy specific component +devbox build-deploy --component=chat --auth-token=your-token + +# Deploy only (skip build) +devbox build-deploy --skip-build=true --image-tag=v1.2.3 --auth-token=your-token + +# Build and deploy to production +devbox build-deploy --environment=production --auth-token=your-token +``` + +#### Features +- **One-command workflow**: Build and deploy in a single operation +- **Flexible execution**: Can skip build or deploy steps +- **Atomic operations**: Ensures consistency between build and deploy +- **Progress tracking**: Real-time status updates + +## Implementation Details + +### Build System Architecture + +#### Component Discovery +```bash +# Automatically discovers components with Dockerfiles +for component in "${DEVBOX_COMPONENTS[@]}"; do + local component_dir="$working_home/freeleaps/apps/$component" + local dockerfile_path="$component_dir/Dockerfile" + + if [[ -d "$component_dir" && -f "$dockerfile_path" ]]; then + # Build component + fi +done +``` + +#### Build Process +1. **Environment Validation**: Check Docker, disk space, source code +2. **Component Discovery**: Find components with Dockerfiles +3. **Parallel Building**: Build multiple components concurrently +4. **Image Tagging**: Apply consistent tagging strategy +5. **Result Reporting**: Detailed success/failure reporting + +### Deployment System Architecture + +#### API Integration +```bash +# Deployment payload structure +{ + "component": "chat", + "image_tag": "freeleaps-local/chat:v1.2.3", + "environment": "staging", + "deployment_type": "docker", + "timestamp": "2024-01-15T10:30:00Z" +} +``` + +#### Deployment Process +1. **Authentication**: Validate API credentials +2. **Environment Validation**: Check target environment availability +3. **Image Deployment**: Deploy images via API +4. **Status Monitoring**: Track deployment progress +5. **Result Verification**: Confirm successful deployment + +### Error Handling & Recovery + +#### Build Failures +- **Retry mechanisms**: Automatic retry for transient failures +- **Component isolation**: Failed builds don't affect other components +- **Detailed logging**: Comprehensive error messages +- **Cleanup**: Automatic cleanup of failed builds + +#### Deployment Failures +- **API error handling**: Graceful handling of API failures +- **Rollback support**: Automatic rollback on deployment failure +- **Status monitoring**: Real-time deployment status tracking +- **Recovery procedures**: Clear recovery instructions + +## Configuration + +### Environment Variables +```bash +# Build configuration +FREELEAPS_BUILD_REPO="freeleaps-local" +FREELEAPS_BUILD_TAG="$(date +%Y%m%d-%H%M%S)" + +# Deployment configuration +FREELEAPS_API_ENDPOINT="https://api.freeleaps.com" +FREELEAPS_DEFAULT_ENVIRONMENT="staging" +FREELEAPS_AUTH_TOKEN="your-auth-token" +``` + +### Configuration Files +```bash +# ~/.devbox/config.yaml +build: + default_repo: "freeleaps-local" + default_tag_format: "timestamp" + parallel_builds: 3 + +deploy: + default_environment: "staging" + api_endpoint: "https://api.freeleaps.com" + timeout: 300 + retry_attempts: 3 +``` + +## Security Considerations + +### Authentication +- **Bearer token authentication**: Secure API access +- **Token validation**: Verify token before deployment +- **Environment isolation**: Separate tokens per environment +- **Audit logging**: Track all deployment activities + +### Image Security +- **Local building**: Build images locally to ensure security +- **Image scanning**: Optional vulnerability scanning +- **Tag validation**: Ensure proper image tagging +- **Registry security**: Secure image registry access + +## Monitoring & Observability + +### Build Metrics +- **Build duration**: Track build times per component +- **Success rates**: Monitor build success rates +- **Resource usage**: Track CPU/memory usage during builds +- **Cache efficiency**: Monitor Docker layer cache usage + +### Deployment Metrics +- **Deployment duration**: Track deployment times +- **Success rates**: Monitor deployment success rates +- **API response times**: Track API performance +- **Environment health**: Monitor target environment status + +## Integration with Existing Workflow + +### Current DevBox Workflow +``` +devbox init → devbox start → Development → devbox stop +``` + +### Enhanced Workflow with Build/Deploy +``` +devbox init → devbox start → Development → devbox build → devbox deploy → devbox stop +``` + +### One-Command Workflow +``` +devbox init → Development → devbox build-deploy → devbox stop +``` + +## Best Practices + +### Build Best Practices +1. **Use meaningful tags**: Include version, date, or feature identifiers +2. **Build incrementally**: Build only changed components +3. **Optimize Dockerfiles**: Use multi-stage builds and caching +4. **Validate builds**: Test builds before deployment + +### Deployment Best Practices +1. **Test in staging**: Always test in staging before production +2. **Use blue-green deployment**: Minimize downtime +3. **Monitor deployments**: Track deployment status and health +4. **Have rollback plans**: Prepare for deployment failures + +### Security Best Practices +1. **Rotate tokens**: Regularly update authentication tokens +2. **Limit permissions**: Use least-privilege access +3. **Scan images**: Regularly scan for vulnerabilities +4. **Audit deployments**: Log all deployment activities + +## Troubleshooting + +### Common Build Issues +```bash +# Insufficient disk space +Error: No space left on device +Solution: Clean up Docker images and containers + +# Docker daemon issues +Error: Cannot connect to Docker daemon +Solution: Start Docker service and check permissions + +# Missing Dockerfile +Error: Dockerfile not found +Solution: Ensure component has proper Dockerfile +``` + +### Common Deployment Issues +```bash +# Authentication failures +Error: 401 Unauthorized +Solution: Check auth token and permissions + +# Environment not found +Error: Environment not accessible +Solution: Verify environment exists and is accessible + +# API connectivity +Error: Cannot connect to API +Solution: Check network connectivity and API endpoint +``` + +## Future Enhancements + +### Planned Features +1. **CI/CD Integration**: Integrate with GitHub Actions, GitLab CI +2. **Multi-environment Support**: Support for multiple deployment environments +3. **Rollback Automation**: Automatic rollback on deployment failures +4. **Performance Optimization**: Parallel builds and deployments +5. **Advanced Monitoring**: Real-time deployment dashboards + +### Advanced Capabilities +1. **Canary Deployments**: Gradual rollout with health checks +2. **A/B Testing**: Support for A/B testing deployments +3. **Infrastructure as Code**: Terraform/CloudFormation integration +4. **Cost Optimization**: Resource usage optimization +5. **Compliance**: SOC2, GDPR compliance features + +## Conclusion + +The build and deploy features transform DevBox CLI from a local development tool into a comprehensive development-to-deployment platform. This enables developers to: + +- **Build consistently**: Local Docker builds ensure consistency +- **Deploy safely**: API-based deployment with proper validation +- **Monitor effectively**: Real-time status tracking and health monitoring +- **Scale efficiently**: Support for multiple components and environments + +These features significantly improve developer productivity and reduce the time from development to production deployment. \ No newline at end of file diff --git a/devbox/cli/CICD_INTEGRATION.md b/devbox/cli/CICD_INTEGRATION.md new file mode 100644 index 0000000..0ae72f9 --- /dev/null +++ b/devbox/cli/CICD_INTEGRATION.md @@ -0,0 +1,429 @@ +# CI/CD Integration with DevBox Packaging + +## Overview + +The DevBox packaging feature now includes comprehensive CI/CD integration that allows you to run your existing CI/CD integration tests locally before submitting code to your Jenkins pipeline. This ensures that the same tests that run in your CI/CD environment are executed locally, providing confidence that your code will pass the CI/CD pipeline. + +## Why CI/CD Integration? + +### Problem Statement +- **CI/CD Failures**: Code passes local tests but fails in CI/CD pipeline +- **Environment Differences**: Local and CI/CD environments are not identical +- **Test Inconsistency**: Different test suites run locally vs. in CI/CD +- **Late Detection**: Issues discovered only after code is committed and pushed + +### Solution Benefits +- **Early Detection**: Catch CI/CD issues before committing code +- **Environment Parity**: Test in containers that match CI/CD environment +- **Test Consistency**: Run the same tests locally as in CI/CD +- **Confidence**: Ensure code will pass CI/CD pipeline +- **Time Saving**: Reduce CI/CD iteration cycles + +## Architecture + +### CI/CD Integration Components + +#### 1. **Jenkins Pipeline Configuration Parser** +- Automatically detects and parses your Jenkinsfile +- Extracts component configurations (language, dependencies, build settings) +- Maps CI/CD test requirements to local execution + +#### 2. **CI/CD Test Environment** +- Creates isolated Docker networks for testing +- Starts the same dependencies as your CI/CD pipeline +- Configures environment variables to match CI/CD + +#### 3. **Component-Specific Test Execution** +- Runs health checks, API tests, and integration tests +- Executes performance tests for production mode +- Validates component functionality in CI/CD-like environment + +#### 4. **Test Result Reporting** +- Provides detailed test results and logs +- Matches CI/CD test output format +- Identifies specific test failures + +### Integration with Existing CI/CD Pipeline + +``` +Local Development → DevBox Package → CI/CD Tests → Jenkins Pipeline + ↓ ↓ ↓ ↓ + Code Changes Docker Container Test Results Production +``` + +## Usage Examples + +### Basic CI/CD Integration +```bash +# Package with CI/CD integration tests +devbox package --run-cicd-tests=true + +# Package specific component with CI/CD tests +devbox package --component=chat --run-cicd-tests=true + +# Package for production with CI/CD tests +devbox package --test-mode=production --run-cicd-tests=true +``` + +### Advanced CI/CD Integration +```bash +# Package with both CI/CD and legacy integration tests +devbox package --run-cicd-tests=true --run-integration=true + +# Package with custom image tag and CI/CD tests +devbox package --image-tag=v1.2.3 --run-cicd-tests=true + +# Package specific component with production mode and CI/CD tests +devbox package --component=authentication --test-mode=production --run-cicd-tests=true +``` + +## Command Reference + +### `devbox package` with CI/CD Integration + +#### New Arguments +- `--run-cicd-tests, -j`: Run CI/CD integration tests after packaging (default: false) + +#### Complete Argument List +- `--working-home, -w`: Working home directory (default: `$HOME/devbox`) +- `--image-repo, -r`: Docker image repository (default: `freeleaps-local`) +- `--image-tag, -t`: Docker image tag (default: timestamp) +- `--component, -c`: Component to package (default: all components) +- `--test-mode, -m`: Packaging mode: `test` or `production` (default: `test`) +- `--run-integration, -i`: Run legacy integration tests (default: false) +- `--run-cicd-tests, -j`: Run CI/CD integration tests (default: false) + +## CI/CD Test Execution + +### 1. Jenkins Configuration Parsing + +The system automatically detects and parses your Jenkinsfile: + +```bash +# Looks for Jenkinsfile in these locations: +# - $WORKING_HOME/freeleaps/Jenkinsfile +# - $WORKING_HOME/freeleaps/ci/Jenkinsfile +# - $WORKING_HOME/freeleaps/.jenkins/Jenkinsfile +# - $WORKING_HOME/freeleaps/apps/$component/Jenkinsfile +``` + +**Example Jenkinsfile Parsing:** +```groovy +// Your existing Jenkinsfile +components = [ + [ + name: 'chat', + language: 'python', + dependenciesManager: 'pip', + buildAgentImage: 'python:3.10-slim-buster', + lintEnabled: false, + sastEnabled: false + ] +] +``` + +**Parsed Configuration:** +```bash +language:python +deps_manager:pip +build_image:python:3.10-slim-buster +lint_enabled:false +sast_enabled:false +``` + +### 2. CI/CD Test Environment Setup + +The system creates a CI/CD-like test environment: + +```bash +# Creates isolated test network +docker network create devbox-cicd-test-network + +# Starts component-specific dependencies +# For chat, authentication, etc.: +# - MongoDB (mongodb:5.0) +# - Redis (redis:7-alpine) +# - RabbitMQ (rabbitmq:3-management) + +# For devsvc: +# - MongoDB (mongodb:5.0) +``` + +### 3. Component-Specific Test Execution + +Each component runs through a comprehensive test suite: + +#### Health Check Tests +```python +# Tests component health endpoint +response = requests.get('http://localhost:8000/health', timeout=5) +assert response.status_code == 200 +``` + +#### API Endpoint Tests +```python +# Tests common API endpoints +endpoints = ["/health", "/docs", "/openapi.json"] +for endpoint in endpoints: + response = requests.get(f'http://localhost:8000{endpoint}', timeout=5) + assert response.status_code in [200, 404] # 404 OK for optional endpoints +``` + +#### Integration Tests +```python +# Component-specific integration tests +# - Chat: Tests chat service connectivity +# - Authentication: Tests auth service functionality +# - Central Storage: Tests storage service operations +``` + +#### Performance Tests (Production Mode) +```python +# Load testing for production mode +response_times = [] +for i in range(10): + start_time = time.time() + response = requests.get('http://localhost:8000/health', timeout=5) + if response.status_code == 200: + response_times.append(time.time() - start_time) + +avg_time = statistics.mean(response_times) +max_time = max(response_times) +assert avg_time < 1.0 and max_time < 2.0 +``` + +## Integration with Your Existing CI/CD Pipeline + +### 1. **Jenkins Pipeline Compatibility** + +The CI/CD integration is designed to work with your existing `first-class-pipeline` library: + +```groovy +// Your existing Jenkinsfile remains unchanged +library 'first-class-pipeline' + +executeFreeleapsPipeline { + serviceName = 'freeleaps' + environmentSlug = 'prod' + serviceGitBranch = 'master' + serviceGitRepo = "https://gitea.freeleaps.mathmast.com/freeleaps/freeleaps-service-hub.git" + serviceGitRepoType = 'monorepo' + serviceGitCredentialsId = 'freeleaps-repos-gitea-credentails' + executeMode = 'fully' + commitMessageLintEnabled = false + components = [ + [ + name: 'authentication', + root: 'apps/authentication', + language: 'python', + dependenciesManager: 'pip', + // ... other configuration + ] + ] +} +``` + +### 2. **Test Environment Parity** + +The local CI/CD tests use the same: +- **Dependencies**: MongoDB, Redis, RabbitMQ versions +- **Environment Variables**: Service endpoints, credentials +- **Network Configuration**: Isolated Docker networks +- **Test Execution**: Health checks, API tests, integration tests + +### 3. **Component Support** + +Currently supported components: +- `chat` - Chat service with full dependency stack +- `authentication` - Authentication service with full dependency stack +- `central_storage` - Storage service with full dependency stack +- `content` - Content service with full dependency stack +- `notification` - Notification service with full dependency stack +- `payment` - Payment service with full dependency stack +- `devsvc` - DevSVC with MongoDB dependency + +## Workflow Integration + +### Typical Development Workflow with CI/CD Integration + +1. **Local Development** + ```bash + cd ~/devbox/freeleaps/apps/chat + # Make code changes + ``` + +2. **Local Unit Testing** + ```bash + python -m pytest tests/ -v + ``` + +3. **CI/CD Integration Testing** + ```bash + # Package and run CI/CD tests + devbox package --component=chat --run-cicd-tests=true + ``` + +4. **Production Mode Testing** + ```bash + # Test production-ready container + devbox package --component=chat --test-mode=production --run-cicd-tests=true + ``` + +5. **Submit to CI/CD** + ```bash + # If all tests pass, commit and push + git add . + git commit -m "Feature: Add new chat functionality" + git push + ``` + +### CI/CD Pipeline Integration + +Your Jenkins pipeline will now receive code that has been: +- ✅ Tested in production-like containers +- ✅ Validated with CI/CD integration tests +- ✅ Performance tested (if in production mode) +- ✅ Health checked and API tested + +## Configuration and Customization + +### 1. **Custom Test Configuration** + +You can customize CI/CD test behavior by modifying the test functions: + +```bash +# Edit the test functions in the devbox script +# - run_health_check_test() +# - run_api_endpoint_tests() +# - run_integration_tests() +# - run_performance_tests() +``` + +### 2. **Component-Specific Test Customization** + +Add component-specific tests by extending the integration test functions: + +```bash +# Add new component test function +run_custom_component_integration_tests() { + local container="$1" + local network="$2" + + # Your custom test logic + docker exec "$container" python -c " +# Your custom test code +" +} +``` + +### 3. **Environment Variable Customization** + +Customize environment variables for specific components: + +```bash +# Modify setup_component_env_vars() function +case "$component" in +your_custom_component) + env_array+=( + "-e" "CUSTOM_VAR=value" + "-e" "ANOTHER_VAR=value" + ) + ;; +esac +``` + +## Troubleshooting + +### Common CI/CD Integration Issues + +1. **Jenkinsfile Not Found** + ``` + Warning: No Jenkinsfile found for component: chat + ``` + **Solution**: Ensure Jenkinsfile exists in one of the expected locations. + +2. **Component Not Found in Jenkinsfile** + ``` + Warning: Component chat not found in Jenkinsfile + ``` + **Solution**: Check component name matches exactly in Jenkinsfile. + +3. **CI/CD Test Dependencies Fail** + ``` + Error: MongoDB failed to start + ``` + **Solution**: Check Docker resources and network configuration. + +4. **Health Check Failures** + ``` + Error: Health check failed for chat + ``` + **Solution**: Verify component health endpoint is implemented. + +### Debugging CI/CD Tests + +1. **Check Test Container Logs** + ```bash + docker logs cicd-test-chat + ``` + +2. **Inspect Test Network** + ```bash + docker network inspect devbox-cicd-test-network + ``` + +3. **Check Dependency Containers** + ```bash + docker ps | grep cicd- + ``` + +4. **Manual Test Execution** + ```bash + # Run component manually for debugging + docker run -it --network devbox-cicd-test-network \ + -e MONGODB_URI="mongodb://test:test@cicd-mongodb-chat:27017" \ + your-image-name + ``` + +## Best Practices + +### 1. **CI/CD Test Development** +- Write tests that match your CI/CD pipeline requirements +- Use the same test data and configurations +- Implement proper cleanup and error handling + +### 2. **Component Configuration** +- Ensure components have proper health endpoints +- Implement comprehensive API documentation +- Use consistent environment variable naming + +### 3. **Test Execution** +- Run CI/CD tests before committing code +- Use production mode for final validation +- Monitor test execution time and performance + +### 4. **Integration with Workflow** +- Add CI/CD tests to your development checklist +- Use CI/CD tests for pull request validation +- Integrate with your IDE or editor + +## Future Enhancements + +### Planned Features +1. **Multi-Component Testing**: Test multiple components together +2. **Custom Test Suites**: Support for custom test configurations +3. **Test Result Export**: Export test results in CI/CD format +4. **Performance Benchmarking**: Advanced performance testing +5. **Security Scanning**: Integrate security testing + +### Integration Opportunities +1. **Git Hooks**: Pre-commit CI/CD test validation +2. **IDE Integration**: VS Code and IntelliJ plugins +3. **CI/CD Pipeline Integration**: Direct Jenkins integration +4. **Monitoring Integration**: Test result monitoring and alerting + +## Conclusion + +The CI/CD integration feature bridges the gap between local development and CI/CD pipeline execution by providing engineers with the ability to run the same tests locally that will run in their CI/CD environment. This significantly reduces CI/CD failures and improves development confidence. + +By integrating your existing CI/CD integration tests into the DevBox packaging workflow, you can ensure that code is thoroughly tested before it reaches your Jenkins pipeline, leading to faster, more reliable deployments. \ No newline at end of file diff --git a/devbox/cli/LOCAL_CODE_PACKAGING.md b/devbox/cli/LOCAL_CODE_PACKAGING.md new file mode 100644 index 0000000..c19be80 --- /dev/null +++ b/devbox/cli/LOCAL_CODE_PACKAGING.md @@ -0,0 +1,337 @@ +# Local Code Packaging Feature + +## Overview + +The `devbox package` command is a powerful feature that allows engineers to package their local code into production-ready Docker containers for final testing before submitting to CI/CD pipelines. This feature mimics the real CI/CD flow by creating Docker images that closely resemble what would be deployed in production environments. + +## Why This Feature? + +### Problem Statement +- Engineers develop locally but can't easily test their code in a production-like environment +- CI/CD failures often occur due to environment differences between local development and production +- No easy way to validate Docker builds before committing code +- Integration testing requires complex setup + +### Solution Benefits +- **Early Detection**: Catch Docker build issues before CI/CD +- **Environment Consistency**: Test in containers that match production +- **Integration Testing**: Run full integration tests with packaged containers +- **Confidence**: Ensure code works in production-like environment +- **Time Saving**: Reduce CI/CD iteration cycles + +## Architecture + +### Two Packaging Modes + +#### 1. Test Mode (`--test-mode=test`) +- Runs unit tests before packaging +- Uses development Dockerfile +- Optimized for fast iteration +- Includes debugging capabilities + +#### 2. Production Mode (`--test-mode=production`) +- Creates production-optimized Dockerfile +- Multi-stage builds for smaller images +- Security hardening (non-root user) +- Health checks and monitoring +- Optimized for production deployment + +### Component Structure +``` +freeleaps/ +├── apps/ +│ ├── chat/ +│ │ ├── Dockerfile +│ │ ├── requirements.txt +│ │ └── tests/ +│ ├── authentication/ +│ │ ├── Dockerfile +│ │ ├── requirements.txt +│ │ └── tests/ +│ └── ... +``` + +## Usage Examples + +### Basic Packaging +```bash +# Package all components for testing +devbox package + +# Package specific component +devbox package --component=chat + +# Package for production +devbox package --test-mode=production +``` + +### Advanced Usage +```bash +# Package with custom image tag +devbox package --image-tag=v1.2.3 --test-mode=production + +# Package with integration tests +devbox package --run-integration=true + +# Package specific component with integration tests +devbox package --component=chat --run-integration=true --test-mode=production +``` + +## Command Reference + +### `devbox package` + +Packages local code into Docker containers for final testing. + +#### Arguments +- `--working-home, -w`: Working home directory (default: `$HOME/devbox`) +- `--image-repo, -r`: Docker image repository (default: `freeleaps-local`) +- `--image-tag, -t`: Docker image tag (default: timestamp) +- `--component, -c`: Component to package (default: all components) +- `--test-mode, -m`: Packaging mode: `test` or `production` (default: `test`) +- `--run-integration, -i`: Run integration tests after packaging (default: `false`) + +#### Examples +```bash +# Package all components for testing +devbox package + +# Package chat component for production +devbox package --component=chat --test-mode=production + +# Package with integration tests +devbox package --run-integration=true + +# Package with custom settings +devbox package --image-repo=my-repo --image-tag=latest --test-mode=production +``` + +## Workflow Integration + +### Typical Development Workflow + +1. **Local Development** + ```bash + # Engineer develops locally + cd ~/devbox/freeleaps/apps/chat + # Make changes to code + ``` + +2. **Local Testing** + ```bash + # Run unit tests + python -m pytest tests/ + ``` + +3. **Package for Final Testing** + ```bash + # Package the component + devbox package --component=chat --test-mode=production + ``` + +4. **Integration Testing** + ```bash + # Run integration tests with packaged container + devbox package --component=chat --run-integration=true + ``` + +5. **Submit to CI/CD** + ```bash + # If all tests pass, commit and push + git add . + git commit -m "Feature: Add new chat functionality" + git push + ``` + +### CI/CD Integration + +The packaged containers can be: +- Used as base images for CI/CD pipelines +- Tested against production configurations +- Validated for security and performance +- Deployed to staging environments + +## Technical Details + +### Production Dockerfile Generation + +When using `--test-mode=production`, the system generates optimized Dockerfiles: + +```dockerfile +# Multi-stage build for production +FROM python:3.11-slim-buster as builder + +# Install build dependencies +RUN apt-get update && apt-get install -y --no-install-recommends \ + gcc g++ && rm -rf /var/lib/apt/lists/* + +# Create virtual environment +RUN python -m venv /opt/venv +ENV PATH="/opt/venv/bin:$PATH" + +# Copy and install requirements +COPY apps/chat/requirements.txt /tmp/requirements.txt +RUN pip install --no-cache-dir -r /tmp/requirements.txt + +# Production stage +FROM python:3.11-slim-buster + +# Create non-root user +RUN groupadd -r appuser && useradd -r -g appuser appuser + +# Copy virtual environment from builder +COPY --from=builder /opt/venv /opt/venv +ENV PATH="/opt/venv/bin:$PATH" + +# Set working directory +WORKDIR /app + +# Copy application code +COPY apps/chat/ /app/ + +# Set environment variables for production +ENV PYTHONUNBUFFERED=1 \ + PYTHONDONTWRITEBYTECODE=1 \ + PIP_NO_CACHE_DIR=1 \ + PIP_DISABLE_PIP_VERSION_CHECK=1 + +# Change ownership to non-root user +RUN chown -R appuser:appuser /app + +# Switch to non-root user +USER appuser + +# Health check +HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 \ + CMD python -c "import requests; requests.get('http://localhost:8000/health')" || exit 1 + +# Expose port +EXPOSE 8000 + +# Default command +CMD ["python", "-m", "uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"] +``` + +### Integration Testing + +The integration testing system: + +1. **Creates Test Network**: Isolated Docker network for testing +2. **Starts Dependencies**: MongoDB, Redis, RabbitMQ containers +3. **Runs Components**: Each packaged component in test mode +4. **Health Checks**: Validates component functionality +5. **Cleanup**: Removes all test containers and networks + +### Environment Validation + +The system validates: +- Source code existence +- Docker buildx support +- Available disk space (5GB minimum) +- Required tools (python, pip, pytest) +- Component structure and Dockerfiles + +## Best Practices + +### 1. Component Structure +Ensure your components follow the standard structure: +``` +apps/component_name/ +├── Dockerfile +├── requirements.txt +├── tests/ +│ ├── test_component.py +│ └── conftest.py +└── main.py +``` + +### 2. Testing Strategy +- Write comprehensive unit tests +- Include integration test scenarios +- Test both development and production modes +- Validate health check endpoints + +### 3. Dockerfile Best Practices +- Use multi-stage builds for production +- Minimize image layers +- Include health checks +- Use non-root users +- Set appropriate environment variables + +### 4. CI/CD Integration +- Use packaged images as base for CI/CD +- Validate against production configurations +- Run security scans on packaged images +- Test deployment procedures + +## Troubleshooting + +### Common Issues + +1. **Missing Dockerfile** + ``` + Error: Dockerfile not found for component: chat + ``` + **Solution**: Ensure each component has a Dockerfile in its directory. + +2. **Test Failures** + ``` + Error: Component tests failed for chat + ``` + **Solution**: Fix unit tests before packaging. + +3. **Integration Test Failures** + ``` + Error: chat integration test failed + ``` + **Solution**: Check component health endpoints and dependencies. + +4. **Insufficient Disk Space** + ``` + Error: Insufficient disk space for packaging + ``` + **Solution**: Free up disk space (minimum 5GB required). + +### Debugging Tips + +1. **Check Component Structure** + ```bash + ls -la ~/devbox/freeleaps/apps/chat/ + ``` + +2. **Validate Dockerfile** + ```bash + docker buildx build --dry-run -f ~/devbox/freeleaps/apps/chat/Dockerfile . + ``` + +3. **Test Component Locally** + ```bash + cd ~/devbox/freeleaps/apps/chat + python -m pytest tests/ -v + ``` + +4. **Check Integration Test Logs** + ```bash + docker logs test-chat + ``` + +## Future Enhancements + +### Planned Features +1. **Multi-architecture Support**: ARM64 and AMD64 builds +2. **Security Scanning**: Automated vulnerability scanning +3. **Performance Testing**: Load testing of packaged containers +4. **Registry Integration**: Push to container registries +5. **Rollback Support**: Version management and rollback capabilities + +### Integration Opportunities +1. **Git Hooks**: Pre-commit packaging validation +2. **IDE Integration**: VS Code and IntelliJ plugins +3. **Monitoring**: Integration with monitoring systems +4. **Compliance**: Automated compliance checking + +## Conclusion + +The `devbox package` feature bridges the gap between local development and production deployment by providing engineers with the ability to test their code in production-like containers before submitting to CI/CD pipelines. This significantly reduces deployment failures and improves development confidence. + +By following the best practices outlined in this document, teams can leverage this feature to create a more robust and reliable development workflow that closely mirrors production environments. \ No newline at end of file diff --git a/devbox/cli/OPENAI_CODE_REVIEW.md b/devbox/cli/OPENAI_CODE_REVIEW.md new file mode 100644 index 0000000..fe05a44 --- /dev/null +++ b/devbox/cli/OPENAI_CODE_REVIEW.md @@ -0,0 +1,369 @@ +# OpenAI Code Review Integration + +## Overview + +The `devbox review` command provides automated code review capabilities powered by OpenAI's GPT-4 model. This feature allows engineers to perform comprehensive code reviews locally before submitting pull requests, helping to catch issues early and improve code quality. + +## Why OpenAI Code Review? + +### Problem Statement +- **Manual Review Bottleneck**: Human code reviews can be slow and inconsistent +- **Missed Issues**: Important security, performance, and quality issues may be overlooked +- **Inconsistent Standards**: Different reviewers may have different standards and focus areas +- **Time Constraints**: Rushed reviews may miss critical problems +- **Knowledge Gaps**: Reviewers may not be familiar with all best practices + +### Solution Benefits +- **Comprehensive Analysis**: AI reviews cover security, performance, code quality, and best practices +- **Consistent Standards**: Same review criteria applied across all code changes +- **24/7 Availability**: Reviews can be performed anytime, without waiting for human reviewers +- **Educational**: Provides explanations and suggestions for improvements +- **Local Privacy**: Reviews happen locally, keeping your code private + +## Features + +### 🔍 **Comprehensive Review Coverage** +- **Security Analysis**: Identifies potential vulnerabilities, input validation issues, authentication problems +- **Performance Optimization**: Detects inefficient algorithms, memory leaks, database query issues +- **Code Quality**: Checks for code smells, maintainability issues, readability problems +- **Best Practices**: Verifies adherence to language-specific best practices and design patterns +- **Error Handling**: Assesses error handling, exception management, logging practices +- **Testing**: Evaluates test coverage, test quality, and mocking practices +- **Documentation**: Checks for proper documentation, comments, and API documentation + +### 📊 **Beautiful HTML Reports** +- **Interactive Interface**: Modern, responsive web interface for viewing reviews +- **Severity Classification**: Issues categorized as Critical, Warning, Info, or Suggestion +- **Code Snippets**: Relevant code sections highlighted with line numbers +- **Actionable Suggestions**: Specific recommendations for improvements +- **Export Options**: Print reports or export to different formats + +### 🚀 **Local Web Server** +- **Instant Access**: View reports immediately in your browser +- **Report Management**: Browse and manage all review reports +- **Real-time Updates**: Refresh to see new reports as they're generated +- **No External Dependencies**: Everything runs locally on your machine + +## Quick Start + +### 1. Set Up OpenAI API Key + +You can provide your OpenAI API key in two ways: + +**Option A: Environment Variable (Recommended)** +```bash +export OPENAI_API_KEY="your-openai-api-key-here" +``` + +**Option B: Command Line** +```bash +devbox review --component=chat --api-key="your-openai-api-key-here" +``` + +### 2. Perform Your First Review + +```bash +# Review the chat component +devbox review --component=chat + +# Review with custom port +devbox review --component=authentication --port=9090 + +# Review without starting the web server +devbox review --component=content --start-server=false +``` + +### 3. View Review Reports + +After running a review, open your browser and navigate to: +``` +http://localhost:8080 +``` + +## Usage Examples + +### Basic Review +```bash +# Review a specific component +devbox review --component=chat +``` + +### Advanced Review Options +```bash +# Review with custom API key +devbox review --component=authentication --api-key="sk-..." + +# Review on custom port +devbox review --component=content --port=9090 + +# Review without web server +devbox review --component=payment --start-server=false + +# Stop the review server +devbox review --stop-server +``` + +### Review Multiple Components +```bash +# Review chat component +devbox review --component=chat + +# Review authentication component +devbox review --component=authentication + +# Review content component +devbox review --component=content +``` + +## Configuration + +### Configuration File +The code review feature creates a configuration file at `~/.devbox/.code-review/config.yaml`: + +```yaml +# OpenAI Code Review Configuration +openai: + api_key: "" # Set your OpenAI API key here or use environment variable + model: "gpt-4" # Model to use for code review + max_tokens: 4000 # Maximum tokens for review response + temperature: 0.1 # Lower temperature for more focused reviews + +review: + languages: + - python + - javascript + - typescript + - java + - go + - rust + file_extensions: + - .py + - .js + - .ts + - .jsx + - .tsx + - .java + - .go + - .rs + - .cpp + - .c + - .h + - .hpp + exclude_patterns: + - "node_modules/" + - "__pycache__/" + - ".git/" + - "*.min.js" + - "*.min.css" + - "dist/" + - "build/" + - "target/" + - "vendor/" + max_file_size: 100000 # Maximum file size in bytes to review + max_files_per_review: 50 # Maximum number of files to review at once + +output: + format: "html" # html, markdown, json + include_suggestions: true + include_severity: true + include_line_numbers: true + include_code_snippets: true +``` + +### Customizing Review Settings + +You can modify the configuration file to: +- Change the OpenAI model (e.g., gpt-3.5-turbo for faster, cheaper reviews) +- Adjust the number of tokens used +- Add or remove supported file types +- Modify exclusion patterns +- Change output format preferences + +## Review Process + +### 1. File Detection +The system automatically detects changed files in your component: +- Staged changes (`git diff --cached`) +- Unstaged changes (`git diff`) +- Filters by supported file extensions +- Respects exclusion patterns + +### 2. Content Preparation +- Reads file contents +- Prepares context for OpenAI +- Limits file size and count for optimal performance + +### 3. AI Review +- Sends code to OpenAI GPT-4 +- Uses specialized prompt for code review +- Receives comprehensive analysis + +### 4. Report Generation +- Parses AI response +- Generates structured review data +- Creates beautiful HTML report +- Starts local web server + +### 5. Review Interface +- Modern, responsive web interface +- Severity-based issue categorization +- Code snippets with line numbers +- Actionable improvement suggestions + +## Review Categories + +### 🔴 Critical Issues +- Security vulnerabilities +- Potential crashes or data loss +- Critical performance problems +- Major architectural issues + +### 🟡 Warnings +- Code quality issues +- Potential bugs +- Performance concerns +- Best practice violations + +### 🔵 Info +- Style and formatting issues +- Documentation improvements +- Minor optimizations +- Educational notes + +### 🟢 Suggestions +- Enhancement opportunities +- Alternative approaches +- Future improvements +- Learning opportunities + +## Best Practices + +### Before Submitting PRs +1. **Run Local Tests**: Ensure your code passes all tests +2. **Perform Code Review**: Use `devbox review` to get AI feedback +3. **Address Issues**: Fix critical and warning issues +4. **Review Suggestions**: Consider implementing improvement suggestions +5. **Submit PR**: Only after addressing important issues + +### Optimizing Review Quality +1. **Keep Changes Focused**: Smaller, focused changes get better reviews +2. **Include Context**: Make sure related files are included +3. **Use Descriptive Commits**: Clear commit messages help with context +4. **Review Regularly**: Don't wait until the end to review + +### Cost Management +1. **Monitor Usage**: Keep track of API token usage +2. **Use Appropriate Models**: Consider gpt-3.5-turbo for routine reviews +3. **Limit File Count**: Focus on the most important files +4. **Batch Reviews**: Review multiple related changes together + +## Troubleshooting + +### Common Issues + +**API Key Not Found** +```bash +Error: OpenAI API key not found +``` +**Solution**: Set the `OPENAI_API_KEY` environment variable or use `--api-key` parameter + +**API Connectivity Issues** +```bash +Error: OpenAI API connectivity test failed +``` +**Solution**: Check your internet connection and API key validity + +**No Changed Files** +```bash +Warning: No changed files found for review +``` +**Solution**: Make sure you have staged or unstaged changes in your component + +**Server Port Already in Use** +```bash +Error: Port 8080 is already in use +``` +**Solution**: Use a different port with `--port` parameter + +### Performance Tips + +1. **Limit File Size**: Large files take longer to review and cost more +2. **Use Appropriate Model**: gpt-3.5-turbo is faster and cheaper than gpt-4 +3. **Review Incrementally**: Review changes as you make them, not all at once +4. **Exclude Generated Files**: Add generated files to exclusion patterns + +## Integration with Workflow + +### Pre-PR Checklist +```bash +# 1. Run tests +devbox package --component=chat --test-mode=test + +# 2. Perform code review +devbox review --component=chat + +# 3. Address review issues +# (Fix code based on review feedback) + +# 4. Re-review if needed +devbox review --component=chat + +# 5. Submit PR +git push origin feature/chat-improvements +``` + +### CI/CD Integration +The code review feature can be integrated into your CI/CD pipeline: +- Run reviews automatically on pull requests +- Block merges if critical issues are found +- Generate review reports for team review +- Track review metrics over time + +## Security and Privacy + +### Local Processing +- All code review processing happens locally +- No code is stored on external servers +- API calls only send code content to OpenAI +- Review reports are stored locally + +### API Key Security +- Store API keys in environment variables +- Never commit API keys to version control +- Use different API keys for different environments +- Monitor API usage for unusual activity + +## Future Enhancements + +### Planned Features +- **Custom Review Templates**: Define project-specific review criteria +- **Team Review Integration**: Share reviews with team members +- **Historical Tracking**: Track review metrics over time +- **Automated Fixes**: Suggest and apply automatic fixes +- **Multi-language Support**: Enhanced support for more programming languages +- **IDE Integration**: Direct integration with popular IDEs + +### Advanced Capabilities +- **Context-Aware Reviews**: Consider project history and architecture +- **Performance Profiling**: Automated performance analysis +- **Security Scanning**: Integration with security scanning tools +- **Compliance Checking**: Verify compliance with coding standards + +## Support + +### Getting Help +- Check the troubleshooting section above +- Review the configuration options +- Ensure your OpenAI API key is valid and has sufficient credits +- Verify your component directory structure + +### Contributing +The code review feature is designed to be extensible. You can: +- Customize review prompts for your specific needs +- Add support for additional file types +- Enhance the HTML report templates +- Integrate with additional tools and services + +--- + +**Note**: The OpenAI code review feature requires an active OpenAI API key and internet connectivity. API usage is subject to OpenAI's pricing and rate limits. \ No newline at end of file diff --git a/devbox/cli/devbox b/devbox/cli/devbox index a6b04ac..d1fba6e 100755 --- a/devbox/cli/devbox +++ b/devbox/cli/devbox @@ -307,7 +307,11 @@ devbox_usage() { printf " start, s : Start services in the local development environment.\n" printf " stop, p : Stop services in the local development environment.\n" printf " status, t : Display status of services in the local environment.\n" - printf " restart, r : Restart services in the local environment.\n\n" + printf " restart, r : Restart services in the local environment.\n" + printf " package, pk : Package local code into Docker containers for final testing.\n" + printf " build, b : Build local Docker images for Freeleaps components.\n" + printf " deploy, dp : Deploy Docker images to Freeleaps environments.\n" + printf " review, rv : Perform OpenAI-powered code review before submitting PRs.\n\n" printf "Global Arguments\n" printf " --help, -h : Show this help message and exit.\n" @@ -328,6 +332,12 @@ devbox_usage() { printf " devbox start\n\n" printf " Start specific service:\n" printf " devbox start --component=backend\n\n" + printf " Package local code for testing:\n" + printf " devbox package\n\n" + printf " Package specific component for production:\n" + printf " devbox package --component=chat --test-mode=production\n\n" + printf " Perform code review for chat component:\n" + printf " devbox review --component=chat\n\n" printf " Display version information:\n" printf " devbox --version\n" echo @@ -1890,6 +1900,34 @@ parse_requirements() { shift $# ;; + package | pk) + action="package" + shift + devbox_package_parse_requirements "$@" + shift $# + ;; + + build | b) + action="build" + shift + devbox_build_parse_requirements "$@" + shift $# + ;; + + deploy | dp) + action="deploy" + shift + devbox_deploy_parse_requirements "$@" + shift $# + ;; + + review | rv) + action="review" + shift + devbox_review_parse_requirements "$@" + shift $# + ;; + "") devbox_init_guidance >&2 action="init" @@ -2646,6 +2684,10 @@ run() { "stop") devbox_stop_command ;; "status") devbox_status_command ;; "restart") devbox_restart_command ;; + "package") devbox_package_command ;; + "build") devbox_build_command ;; + "deploy") devbox_deploy_command ;; + "review") devbox_review_command ;; esac } @@ -2946,3 +2988,3001 @@ validate_prerequisites() { log_info "Prerequisites validation passed" return 0 } + +# Local Docker build functionality +build_local_docker() { + local component="$1" + local build_context="$2" + local image_tag="$3" + local dockerfile_path="$4" + + log_info "Building local Docker image for $component..." + + # Validate build context + if [[ ! -d "$build_context" ]]; then + log_error "Build context directory not found: $build_context" + return 1 + fi + + # Validate Dockerfile + if [[ ! -f "$dockerfile_path" ]]; then + log_error "Dockerfile not found: $dockerfile_path" + return 1 + fi + + # Build with retry mechanism + if retry_command 3 5 "docker buildx build --platform linux/amd64 --no-cache -t $image_tag -f $dockerfile_path $build_context" "Building $component image"; then + log_info "Successfully built $component image: $image_tag" + return 0 + else + log_error "Failed to build $component image" + return 1 + fi +} + +# Build all local components +build_all_local_components() { + local working_home="$1" + local image_repo="$2" + local image_tag="$3" + local components=("${DEVBOX_COMPONENTS[@]}") + + log_info "Building all local components..." + + local build_results=() + local failed_components=() + + for component in "${components[@]}"; do + local component_dir="$working_home/freeleaps/apps/$component" + local dockerfile_path="$component_dir/Dockerfile" + local image_name="$image_repo/$component:$image_tag" + + if [[ -d "$component_dir" && -f "$dockerfile_path" ]]; then + log_info "Building component: $component" + if build_local_docker "$component" "$component_dir" "$image_name" "$dockerfile_path"; then + build_results+=("$component:SUCCESS") + else + build_results+=("$component:FAILED") + failed_components+=("$component") + fi + else + log_warn "Skipping $component - no Dockerfile found" + build_results+=("$component:SKIPPED") + fi + done + + # Report results + echo + echo "===========================================================" + echo "Local Docker Build Results:" + echo "===========================================================" + for result in "${build_results[@]}"; do + local component=$(echo "$result" | cut -d':' -f1) + local status=$(echo "$result" | cut -d':' -f2) + case "$status" in + SUCCESS) echo "✓ $component: Built successfully" ;; + FAILED) echo "✗ $component: Build failed" ;; + SKIPPED) echo "- $component: Skipped (no Dockerfile)" ;; + esac + done + echo "===========================================================" + + if [[ ${#failed_components[@]} -gt 0 ]]; then + log_error "Some components failed to build: ${failed_components[*]}" + return 1 + fi + + log_info "All local components built successfully" + return 0 +} + +# Validate local build environment +validate_build_environment() { + local working_home="$1" + + log_info "Validating build environment..." + + # Check if source code exists + if [[ ! -d "$working_home/freeleaps" ]]; then + log_error "Source code not found at $working_home/freeleaps" + return 1 + fi + + # Check Docker buildx support + if ! docker buildx version &>/dev/null; then + log_error "Docker buildx not available. Please update Docker." + return 1 + fi + + # Check available disk space for builds + local available_space=$(df -Pk "$working_home" | awk 'END{print $4/1024/1024}') + if [[ "$available_space" -lt 2 ]]; then + log_error "Insufficient disk space for builds: ${available_space}GB available. At least 2GB required." + return 1 + fi + + log_info "Build environment validation passed" + return 0 +} + +# Freeleaps API integration for deployment +deploy_to_freeleaps() { + local component="$1" + local image_tag="$2" + local environment="$3" + local api_endpoint="$4" + local auth_token="$5" + + log_info "Deploying $component to Freeleaps environment: $environment" + + # Prepare deployment payload + local deployment_payload=$( + cat </dev/null) + + if [[ -n "$status_response" ]]; then + local status=$(echo "$status_response" | jq -r '.status' 2>/dev/null || echo "unknown") + local message=$(echo "$status_response" | jq -r '.message' 2>/dev/null || echo "No message") + + log_info "Deployment $deployment_id status: $status - $message" + echo "$status" + else + log_error "Failed to get deployment status" + echo "unknown" + fi +} + +# Wait for deployment completion +wait_for_deployment() { + local deployment_id="$1" + local api_endpoint="$2" + local auth_token="$3" + local max_wait_time="${4:-300}" # 5 minutes default + local check_interval="${5:-10}" # 10 seconds default + + log_info "Waiting for deployment $deployment_id to complete..." + + local elapsed_time=0 + while [[ $elapsed_time -lt $max_wait_time ]]; do + local status=$(get_deployment_status "$deployment_id" "$api_endpoint" "$auth_token") + + case "$status" in + "completed" | "success") + log_info "Deployment $deployment_id completed successfully" + return 0 + ;; + "failed" | "error") + log_error "Deployment $deployment_id failed" + return 1 + ;; + "running" | "pending" | "unknown") + log_info "Deployment $deployment_id still running... (${elapsed_time}s elapsed)" + sleep "$check_interval" + elapsed_time=$((elapsed_time + check_interval)) + ;; + *) + log_warn "Unknown deployment status: $status" + sleep "$check_interval" + elapsed_time=$((elapsed_time + check_interval)) + ;; + esac + done + + log_error "Deployment $deployment_id timed out after ${max_wait_time}s" + return 1 +} + +# Validate deployment environment +validate_deployment_environment() { + local api_endpoint="$1" + local auth_token="$2" + local environment="$3" + + log_info "Validating deployment environment: $environment" + + # Test API connectivity + if ! curl -s --connect-timeout 10 --max-time 30 -H "Authorization: Bearer $auth_token" "$api_endpoint/health" >/dev/null 2>&1; then + log_error "Cannot connect to Freeleaps API at $api_endpoint" + return 1 + fi + + # Validate environment exists + local env_response=$(curl -s -H "Authorization: Bearer $auth_token" "$api_endpoint/environments/$environment" 2>/dev/null) + if [[ -z "$env_response" ]] || [[ "$env_response" == *"not found"* ]]; then + log_error "Environment $environment not found or not accessible" + return 1 + fi + + log_info "Deployment environment validation passed" + return 0 +} + +# :command.function +devbox_build_command() { + local WORKING_HOME="$(get_arg '--working-home' "${HOME}/devbox")" + local IMAGE_REPO="$(get_arg '--image-repo' 'freeleaps-local')" + local IMAGE_TAG="$(get_arg '--image-tag' "$(date +%Y%m%d-%H%M%S)")" + local COMPONENT="$(get_arg '--component')" + + log_info "Starting local Docker build process..." + + # Validate build environment + if ! validate_build_environment "$WORKING_HOME"; then + exit_with_message "Build environment validation failed. Please fix the issues above and try again." 1 + fi + + # Build specific component or all components + if [[ -n "$COMPONENT" ]]; then + local component_dir="$WORKING_HOME/freeleaps/apps/$COMPONENT" + local dockerfile_path="$component_dir/Dockerfile" + local image_name="$IMAGE_REPO/$COMPONENT:$IMAGE_TAG" + + if [[ ! -d "$component_dir" || ! -f "$dockerfile_path" ]]; then + exit_with_message "Component $COMPONENT not found or missing Dockerfile" 1 + fi + + if build_local_docker "$COMPONENT" "$component_dir" "$image_name" "$dockerfile_path"; then + exit_with_message "Successfully built $COMPONENT image: $image_name" 0 + else + exit_with_message "Failed to build $COMPONENT image" 1 + fi + else + # Build all components + if build_all_local_components "$WORKING_HOME" "$IMAGE_REPO" "$IMAGE_TAG"; then + exit_with_message "Successfully built all local components with tag: $IMAGE_TAG" 0 + else + exit_with_message "Some components failed to build. Check the logs above." 1 + fi + fi +} + +# :command.function +devbox_deploy_command() { + local WORKING_HOME="$(get_arg '--working-home' "${HOME}/devbox")" + local IMAGE_REPO="$(get_arg '--image-repo' 'freeleaps-local')" + local IMAGE_TAG="$(get_arg '--image-tag')" + local ENVIRONMENT="$(get_arg '--environment' 'staging')" + local API_ENDPOINT="$(get_arg '--api-endpoint' 'https://api.freeleaps.com')" + local AUTH_TOKEN="$(get_arg '--auth-token')" + local COMPONENT="$(get_arg '--component')" + local WAIT_FOR_COMPLETION="$(get_arg '--wait' 'true')" + + log_info "Starting deployment to Freeleaps..." + + # Validate required parameters + if [[ -z "$IMAGE_TAG" ]]; then + exit_with_message "Image tag is required. Use --image-tag to specify." 1 + fi + + if [[ -z "$AUTH_TOKEN" ]]; then + exit_with_message "Authentication token is required. Use --auth-token to specify." 1 + fi + + # Validate deployment environment + if ! validate_deployment_environment "$API_ENDPOINT" "$AUTH_TOKEN" "$ENVIRONMENT"; then + exit_with_message "Deployment environment validation failed." 1 + fi + + # Deploy specific component or all components + if [[ -n "$COMPONENT" ]]; then + local image_name="$IMAGE_REPO/$COMPONENT:$IMAGE_TAG" + + if deploy_to_freeleaps "$COMPONENT" "$image_name" "$ENVIRONMENT" "$API_ENDPOINT" "$AUTH_TOKEN"; then + if [[ "$WAIT_FOR_COMPLETION" == "true" ]]; then + log_info "Waiting for deployment completion..." + # Note: This would need the deployment ID from the API response + # For now, we'll just report success + fi + exit_with_message "Successfully deployed $COMPONENT to $ENVIRONMENT" 0 + else + exit_with_message "Failed to deploy $COMPONENT to $ENVIRONMENT" 1 + fi + else + # Deploy all components + if deploy_all_components "$IMAGE_REPO" "$IMAGE_TAG" "$ENVIRONMENT" "$API_ENDPOINT" "$AUTH_TOKEN"; then + exit_with_message "Successfully deployed all components to $ENVIRONMENT" 0 + else + exit_with_message "Some deployments failed. Check the logs above." 1 + fi + fi +} + +# :command.function +devbox_build_and_deploy_command() { + local WORKING_HOME="$(get_arg '--working-home' "${HOME}/devbox")" + local IMAGE_REPO="$(get_arg '--image-repo' 'freeleaps-local')" + local IMAGE_TAG="$(get_arg '--image-tag' "$(date +%Y%m%d-%H%M%S)")" + local ENVIRONMENT="$(get_arg '--environment' 'staging')" + local API_ENDPOINT="$(get_arg '--api-endpoint' 'https://api.freeleaps.com')" + local AUTH_TOKEN="$(get_arg '--auth-token')" + local COMPONENT="$(get_arg '--component')" + local SKIP_BUILD="$(get_arg '--skip-build' 'false')" + + log_info "Starting build and deploy process..." + + # Validate required parameters + if [[ -z "$AUTH_TOKEN" ]]; then + exit_with_message "Authentication token is required. Use --auth-token to specify." 1 + fi + + # Step 1: Build (unless skipped) + if [[ "$SKIP_BUILD" != "true" ]]; then + log_info "Step 1: Building Docker images..." + + # Validate build environment + if ! validate_build_environment "$WORKING_HOME"; then + exit_with_message "Build environment validation failed." 1 + fi + + # Build components + if [[ -n "$COMPONENT" ]]; then + local component_dir="$WORKING_HOME/freeleaps/apps/$COMPONENT" + local dockerfile_path="$component_dir/Dockerfile" + local image_name="$IMAGE_REPO/$COMPONENT:$IMAGE_TAG" + + if ! build_local_docker "$COMPONENT" "$component_dir" "$image_name" "$dockerfile_path"; then + exit_with_message "Build failed for $COMPONENT" 1 + fi + else + if ! build_all_local_components "$WORKING_HOME" "$IMAGE_REPO" "$IMAGE_TAG"; then + exit_with_message "Build failed for some components" 1 + fi + fi + + log_info "Build completed successfully with tag: $IMAGE_TAG" + else + log_info "Skipping build step (--skip-build=true)" + fi + + # Step 2: Deploy + log_info "Step 2: Deploying to $ENVIRONMENT..." + + # Validate deployment environment + if ! validate_deployment_environment "$API_ENDPOINT" "$AUTH_TOKEN" "$ENVIRONMENT"; then + exit_with_message "Deployment environment validation failed." 1 + fi + + # Deploy components + if [[ -n "$COMPONENT" ]]; then + local image_name="$IMAGE_REPO/$COMPONENT:$IMAGE_TAG" + if ! deploy_to_freeleaps "$COMPONENT" "$image_name" "$ENVIRONMENT" "$API_ENDPOINT" "$AUTH_TOKEN"; then + exit_with_message "Deployment failed for $COMPONENT" 1 + fi + else + if ! deploy_all_components "$IMAGE_REPO" "$IMAGE_TAG" "$ENVIRONMENT" "$API_ENDPOINT" "$AUTH_TOKEN"; then + exit_with_message "Deployment failed for some components" 1 + fi + fi + + log_info "Build and deploy completed successfully!" + log_info "Image tag: $IMAGE_TAG" + log_info "Environment: $ENVIRONMENT" + exit_with_message "Build and deploy process completed successfully" 0 +} + +# :command.usage +devbox_build_usage() { + if [[ -n $long_usage ]]; then + printf "Command\n" + printf " devbox build : Build local Docker images for Freeleaps components.\n\n" + + printf "Arguments\n" + printf " --working-home -w [Optional] : Specifies the working home directory. Default: %s/devbox\n" "$HOME" + printf " --image-repo -r [Optional] : Specifies the Docker image repository. Default: freeleaps-local\n" + printf " --image-tag -t [Optional] : Specifies the Docker image tag. Default: timestamp\n" + printf " --component -c [Optional] : Specifies the component to build. Default: all components\n\n" + + printf "Global Arguments\n" + printf " --help -h : Show this help message and exit.\n\n" + + printf "Examples\n" + printf " Build all components:\n" + printf " devbox build\n\n" + printf " Build specific component:\n" + printf " devbox build --component=chat\n\n" + printf " Build with custom image tag:\n" + printf " devbox build --image-tag=v1.2.3\n\n" + else + printf "devbox build - Build local Docker images for Freeleaps components.\n\n" + fi +} + +# :command.usage +devbox_deploy_usage() { + if [[ -n $long_usage ]]; then + printf "Command\n" + printf " devbox deploy : Deploy Docker images to Freeleaps environments.\n\n" + + printf "Arguments\n" + printf " --working-home -w [Optional] : Specifies the working home directory. Default: %s/devbox\n" "$HOME" + printf " --image-repo -r [Optional] : Specifies the Docker image repository. Default: freeleaps-local\n" + printf " --image-tag -t [Required] : Specifies the Docker image tag to deploy\n" + printf " --environment -e [Optional] : Specifies the target environment. Default: staging\n" + printf " --api-endpoint -a [Optional] : Specifies the Freeleaps API endpoint. Default: https://api.freeleaps.com\n" + printf " --auth-token -x [Required] : Specifies the authentication token\n" + printf " --component -c [Optional] : Specifies the component to deploy. Default: all components\n" + printf " --wait -w [Optional] : Wait for deployment completion. Default: true\n\n" + + printf "Global Arguments\n" + printf " --help -h : Show this help message and exit.\n\n" + + printf "Examples\n" + printf " Deploy all components:\n" + printf " devbox deploy --image-tag=v1.2.3 --auth-token=your-token\n\n" + printf " Deploy specific component:\n" + printf " devbox deploy --component=chat --image-tag=v1.2.3 --auth-token=your-token\n\n" + printf " Deploy to production:\n" + printf " devbox deploy --environment=production --image-tag=v1.2.3 --auth-token=your-token\n\n" + else + printf "devbox deploy - Deploy Docker images to Freeleaps environments.\n\n" + fi +} + +# :command.usage +devbox_build_and_deploy_usage() { + if [[ -n $long_usage ]]; then + printf "Command\n" + printf " devbox build-deploy : Build and deploy Docker images in one command.\n\n" + + printf "Arguments\n" + printf " --working-home -w [Optional] : Specifies the working home directory. Default: %s/devbox\n" "$HOME" + printf " --image-repo -r [Optional] : Specifies the Docker image repository. Default: freeleaps-local\n" + printf " --image-tag -t [Optional] : Specifies the Docker image tag. Default: timestamp\n" + printf " --environment -e [Optional] : Specifies the target environment. Default: staging\n" + printf " --api-endpoint -a [Optional] : Specifies the Freeleaps API endpoint. Default: https://api.freeleaps.com\n" + printf " --auth-token -x [Required] : Specifies the authentication token\n" + printf " --component -c [Optional] : Specifies the component to build/deploy. Default: all components\n" + printf " --skip-build -s [Optional] : Skip build step and only deploy. Default: false\n\n" + + printf "Global Arguments\n" + printf " --help -h : Show this help message and exit.\n\n" + + printf "Examples\n" + printf " Build and deploy all components:\n" + printf " devbox build-deploy --auth-token=your-token\n\n" + printf " Build and deploy specific component:\n" + printf " devbox build-deploy --component=chat --auth-token=your-token\n\n" + printf " Deploy only (skip build):\n" + printf " devbox build-deploy --skip-build=true --image-tag=v1.2.3 --auth-token=your-token\n\n" + else + printf "devbox build-deploy - Build and deploy Docker images in one command.\n\n" + fi +} + +# Enhanced local code packaging for final testing +package_local_code() { + local component="$1" + local working_home="$2" + local image_repo="$3" + local image_tag="$4" + local test_mode="$5" + + log_info "Packaging local code for $component in $test_mode mode..." + + # Validate component exists + local component_dir="$working_home/freeleaps/apps/$component" + if [[ ! -d "$component_dir" ]]; then + log_error "Component directory not found: $component_dir" + return 1 + fi + + # Check for Dockerfile + local dockerfile_path="$component_dir/Dockerfile" + if [[ ! -f "$dockerfile_path" ]]; then + log_error "Dockerfile not found for $component: $dockerfile_path" + return 1 + fi + + # Run pre-packaging tests if in test mode + if [[ "$test_mode" == "test" ]]; then + if ! run_component_tests "$component" "$component_dir"; then + log_error "Component tests failed for $component" + return 1 + fi + fi + + # Create production-ready image + local image_name="$image_repo/$component:$image_tag" + local build_context="$component_dir" + + # Use multi-stage build for production images + if [[ "$test_mode" == "production" ]]; then + build_context="$working_home/freeleaps" + dockerfile_path="$component_dir/Dockerfile.prod" + + # Create production Dockerfile if it doesn't exist + if [[ ! -f "$dockerfile_path" ]]; then + create_production_dockerfile "$component" "$component_dir" "$dockerfile_path" + fi + fi + + # Build the image + if retry_command 3 10 "docker buildx build --platform linux/amd64 --no-cache -t $image_name -f $dockerfile_path $build_context" "Building production image for $component"; then + log_info "Successfully packaged $component: $image_name" + return 0 + else + log_error "Failed to package $component" + return 1 + fi +} + +# Create production-ready Dockerfile +create_production_dockerfile() { + local component="$1" + local component_dir="$2" + local dockerfile_path="$3" + + log_info "Creating production Dockerfile for $component..." + + # Read the original Dockerfile + local original_dockerfile="$component_dir/Dockerfile" + if [[ ! -f "$original_dockerfile" ]]; then + log_error "Original Dockerfile not found: $original_dockerfile" + return 1 + fi + + # Create production Dockerfile with optimizations + cat >"$dockerfile_path" <<'EOF' +# Multi-stage build for production +FROM python:3.11-slim-buster as builder + +# Install build dependencies +RUN apt-get update && apt-get install -y --no-install-recommends \ + gcc \ + g++ \ + && rm -rf /var/lib/apt/lists/* + +# Create virtual environment +RUN python -m venv /opt/venv +ENV PATH="/opt/venv/bin:$PATH" + +# Copy and install requirements +COPY apps/COMPONENT_NAME/requirements.txt /tmp/requirements.txt +RUN pip install --no-cache-dir -r /tmp/requirements.txt + +# Production stage +FROM python:3.11-slim-buster + +# Create non-root user +RUN groupadd -r appuser && useradd -r -g appuser appuser + +# Copy virtual environment from builder +COPY --from=builder /opt/venv /opt/venv +ENV PATH="/opt/venv/bin:$PATH" + +# Set working directory +WORKDIR /app + +# Copy application code +COPY apps/COMPONENT_NAME/ /app/ + +# Set environment variables for production +ENV PYTHONUNBUFFERED=1 \ + PYTHONDONTWRITEBYTECODE=1 \ + PIP_NO_CACHE_DIR=1 \ + PIP_DISABLE_PIP_VERSION_CHECK=1 + +# Change ownership to non-root user +RUN chown -R appuser:appuser /app + +# Switch to non-root user +USER appuser + +# Health check +HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 \ + CMD python -c "import requests; requests.get('http://localhost:8000/health')" || exit 1 + +# Expose port (will be overridden by environment) +EXPOSE 8000 + +# Default command (will be overridden by environment) +CMD ["python", "-m", "uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"] +EOF + + # Replace placeholder with actual component name + sed -i "s/COMPONENT_NAME/$component/g" "$dockerfile_path" + + log_info "Production Dockerfile created: $dockerfile_path" +} + +# Run component tests +run_component_tests() { + local component="$1" + local component_dir="$2" + + log_info "Running tests for $component..." + + # Check for test files + local test_files=() + if [[ -f "$component_dir/tests/test_$component.py" ]]; then + test_files+=("tests/test_$component.py") + fi + if [[ -f "$component_dir/test_$component.py" ]]; then + test_files+=("test_$component.py") + fi + if [[ -d "$component_dir/tests" ]]; then + test_files+=("tests/") + fi + + if [[ ${#test_files[@]} -eq 0 ]]; then + log_warn "No test files found for $component, skipping tests" + return 0 + fi + + # Run tests with retry mechanism + cd "$component_dir" + for test_file in "${test_files[@]}"; do + if [[ -f "$test_file" ]]; then + log_info "Running test file: $test_file" + if ! retry_command 2 5 "python -m pytest $test_file -v" "Running tests in $test_file"; then + log_error "Tests failed in $test_file" + return 1 + fi + elif [[ -d "$test_file" ]]; then + log_info "Running tests in directory: $test_file" + if ! retry_command 2 5 "python -m pytest $test_file -v" "Running tests in $test_file"; then + log_error "Tests failed in $test_file" + return 1 + fi + fi + done + + log_info "All tests passed for $component" + return 0 +} + +# Package all components for final testing +package_all_components() { + local working_home="$1" + local image_repo="$2" + local image_tag="$3" + local test_mode="$4" + local components=("${DEVBOX_COMPONENTS[@]}") + + log_info "Packaging all components for final testing..." + + local package_results=() + local failed_components=() + + for component in "${components[@]}"; do + log_info "Packaging component: $component" + if package_local_code "$component" "$working_home" "$image_repo" "$image_tag" "$test_mode"; then + package_results+=("$component:SUCCESS") + else + package_results+=("$component:FAILED") + failed_components+=("$component") + fi + done + + # Report results + echo + echo "===========================================================" + echo "Local Code Packaging Results ($test_mode mode):" + echo "===========================================================" + for result in "${package_results[@]}"; do + local component=$(echo "$result" | cut -d':' -f1) + local status=$(echo "$result" | cut -d':' -f2) + case "$status" in + SUCCESS) echo "✓ $component: Packaged successfully" ;; + FAILED) echo "✗ $component: Packaging failed" ;; + esac + done + echo "===========================================================" + + if [[ ${#failed_components[@]} -gt 0 ]]; then + log_error "Some components failed to package: ${failed_components[*]}" + return 1 + fi + + log_info "All components packaged successfully for $test_mode testing" + return 0 +} + +# Validate packaging environment +validate_packaging_environment() { + local working_home="$1" + + log_info "Validating packaging environment..." + + # Check if source code exists + if [[ ! -d "$working_home/freeleaps" ]]; then + log_error "Source code not found at $working_home/freeleaps" + return 1 + fi + + # Check if apps directory exists + if [[ ! -d "$working_home/freeleaps/apps" ]]; then + log_error "Apps directory not found at $working_home/freeleaps/apps" + return 1 + fi + + # Check Docker buildx support + if ! docker buildx version &>/dev/null; then + log_error "Docker buildx not available. Please update Docker." + return 1 + fi + + # Check available disk space for builds + local available_space=$(df -Pk "$working_home" | awk 'END{print $4/1024/1024}') + if [[ "$available_space" -lt 5 ]]; then + log_error "Insufficient disk space for packaging: ${available_space}GB available. At least 5GB required." + return 1 + fi + + # Check for required tools + local required_tools=("python" "pip" "pytest") + local missing_tools=() + + for tool in "${required_tools[@]}"; do + if ! command -v "$tool" &>/dev/null; then + missing_tools+=("$tool") + fi + done + + if [[ ${#missing_tools[@]} -gt 0 ]]; then + log_error "Missing required tools: ${missing_tools[*]}" + return 1 + fi + + log_info "Packaging environment validation passed" + return 0 +} + +# Run integration tests with packaged containers +run_integration_tests() { + local working_home="$1" + local image_repo="$2" + local image_tag="$3" + local test_components=("${DEVBOX_COMPONENTS[@]}") + + log_info "Running integration tests with packaged containers..." + + # Create test network + local test_network="devbox-test-network" + if ! docker network ls | grep -q "$test_network"; then + docker network create "$test_network" + fi + + # Start test dependencies + local test_containers=() + + # Start MongoDB for testing + local mongodb_container=$(docker run -d --name "test-mongodb" --network "$test_network" \ + -e MONGO_INITDB_ROOT_USERNAME=test -e MONGO_INITDB_ROOT_PASSWORD=test \ + mongo:5.0) + test_containers+=("$mongodb_container") + + # Start Redis for testing + local redis_container=$(docker run -d --name "test-redis" --network "$test_network" \ + redis:7-alpine) + test_containers+=("$redis_container") + + # Start RabbitMQ for testing + local rabbitmq_container=$(docker run -d --name "test-rabbitmq" --network "$test_network" \ + -e RABBITMQ_DEFAULT_USER=test -e RABBITMQ_DEFAULT_PASS=test \ + rabbitmq:3-management) + test_containers+=("$rabbitmq_container") + + # Wait for services to be ready + log_info "Waiting for test services to be ready..." + sleep 10 + + # Test each component + local test_results=() + for component in "${test_components[@]}"; do + local image_name="$image_repo/$component:$image_tag" + + # Check if image exists + if ! docker image inspect "$image_name" &>/dev/null; then + log_warn "Image not found for $component: $image_name" + test_results+=("$component:SKIPPED") + continue + fi + + log_info "Testing $component..." + + # Run component in test mode + local test_container=$(docker run -d --name "test-$component" --network "$test_network" \ + -e MONGODB_URI="mongodb://test:test@test-mongodb:27017" \ + -e REDIS_URL="redis://test-redis:6379" \ + -e RABBITMQ_HOST="test-rabbitmq" \ + -e RABBITMQ_PORT="5672" \ + -e TEST_MODE="true" \ + "$image_name") + + # Wait for component to start + sleep 5 + + # Check if container is running + if docker ps --format '{{.Names}}' | grep -q "test-$component"; then + # Run health check + if docker exec "$test_container" python -c "import requests; requests.get('http://localhost:8000/health')" &>/dev/null; then + test_results+=("$component:PASSED") + log_info "$component integration test passed" + else + test_results+=("$component:FAILED") + log_error "$component integration test failed" + fi + else + test_results+=("$component:FAILED") + log_error "$component failed to start" + fi + + # Clean up test container + docker stop "$test_container" &>/dev/null || true + docker rm "$test_container" &>/dev/null || true + done + + # Clean up test dependencies + for container in "${test_containers[@]}"; do + docker stop "$container" &>/dev/null || true + docker rm "$container" &>/dev/null || true + done + + # Report results + echo + echo "===========================================================" + echo "Integration Test Results:" + echo "===========================================================" + for result in "${test_results[@]}"; do + local component=$(echo "$result" | cut -d':' -f1) + local status=$(echo "$result" | cut -d':' -f2) + case "$status" in + PASSED) echo "✓ $component: Integration test passed" ;; + FAILED) echo "✗ $component: Integration test failed" ;; + SKIPPED) echo "- $component: Test skipped (no image)" ;; + esac + done + echo "===========================================================" + + # Check if all tests passed + local failed_count=0 + for result in "${test_results[@]}"; do + if [[ "$result" == *":FAILED" ]]; then + failed_count=$((failed_count + 1)) + fi + done + + if [[ $failed_count -gt 0 ]]; then + log_error "$failed_count integration tests failed" + return 1 + fi + + log_info "All integration tests passed" + return 0 +} + +# :command.function +devbox_package_command() { + local WORKING_HOME="$(get_arg '--working-home' "${HOME}/devbox")" + local IMAGE_REPO="$(get_arg '--image-repo' 'freeleaps-local')" + local IMAGE_TAG="$(get_arg '--image-tag' "$(date +%Y%m%d-%H%M%S)")" + local COMPONENT="$(get_arg '--component')" + local TEST_MODE="$(get_arg '--test-mode' 'test')" + local RUN_INTEGRATION="$(get_arg '--run-integration' 'false')" + local RUN_CICD_TESTS="$(get_arg '--run-cicd-tests' 'false')" + + log_info "Starting local code packaging process with CI/CD integration..." + + # Validate packaging environment + if ! validate_packaging_environment "$WORKING_HOME"; then + exit_with_message "Packaging environment validation failed. Please fix the issues above and try again." 1 + fi + + # Package specific component or all components + if [[ -n "$COMPONENT" ]]; then + if package_local_code_with_cicd "$COMPONENT" "$WORKING_HOME" "$IMAGE_REPO" "$IMAGE_TAG" "$TEST_MODE" "$RUN_CICD_TESTS"; then + log_info "Successfully packaged $COMPONENT: $IMAGE_REPO/$COMPONENT:$IMAGE_TAG" + else + exit_with_message "Failed to package $COMPONENT" 1 + fi + else + # Package all components + local package_results=() + local failed_components=() + local components=("${DEVBOX_COMPONENTS[@]}") + + for component in "${components[@]}"; do + log_info "Packaging component: $component" + if package_local_code_with_cicd "$component" "$WORKING_HOME" "$IMAGE_REPO" "$IMAGE_TAG" "$TEST_MODE" "$RUN_CICD_TESTS"; then + package_results+=("$component:SUCCESS") + else + package_results+=("$component:FAILED") + failed_components+=("$component") + fi + done + + # Report results + echo + echo "===========================================================" + echo "Local Code Packaging Results ($TEST_MODE mode):" + echo "===========================================================" + for result in "${package_results[@]}"; do + local component=$(echo "$result" | cut -d':' -f1) + local status=$(echo "$result" | cut -d':' -f2) + case "$status" in + SUCCESS) echo "✓ $component: Packaged successfully" ;; + FAILED) echo "✗ $component: Packaging failed" ;; + esac + done + echo "===========================================================" + + if [[ ${#failed_components[@]} -gt 0 ]]; then + exit_with_message "Some components failed to package: ${failed_components[*]}" 1 + fi + + log_info "Successfully packaged all components with tag: $IMAGE_TAG" + fi + + # Run integration tests if requested (legacy integration tests) + if [[ "$RUN_INTEGRATION" == "true" ]]; then + log_info "Running legacy integration tests..." + if run_integration_tests "$WORKING_HOME" "$IMAGE_REPO" "$IMAGE_TAG"; then + log_info "Legacy integration tests completed successfully" + else + exit_with_message "Legacy integration tests failed. Check the logs above." 1 + fi + fi + + # Success message + local success_message="Local code packaging completed successfully!" + success_message+="\nImage tag: $IMAGE_TAG" + success_message+="\nTest mode: $TEST_MODE" + + if [[ "$RUN_CICD_TESTS" == "true" ]]; then + success_message+="\nCI/CD tests: PASSED" + fi + + if [[ "$RUN_INTEGRATION" == "true" ]]; then + success_message+="\nLegacy integration tests: PASSED" + fi + + exit_with_message "$success_message" 0 +} + +# :command.usage +devbox_package_usage() { + if [[ -n $long_usage ]]; then + printf "Command\n" + printf " devbox package : Package local code into Docker containers for final testing with CI/CD integration.\n\n" + + printf "Arguments\n" + printf " --working-home -w [Optional] : Specifies the working home directory. Default: %s/devbox\n" "$HOME" + printf " --image-repo -r [Optional] : Specifies the Docker image repository. Default: freeleaps-local\n" + printf " --image-tag -t [Optional] : Specifies the Docker image tag. Default: timestamp\n" + printf " --component -c [Optional] : Specifies the component to package. Default: all components\n" + printf " --test-mode -m [Optional] : Specifies the packaging mode (test/production). Default: test\n" + printf " --run-integration -i [Optional] : Run legacy integration tests after packaging. Default: false\n" + printf " --run-cicd-tests -j [Optional] : Run CI/CD integration tests after packaging. Default: false\n\n" + + printf "Global Arguments\n" + printf " --help -h : Show this help message and exit.\n\n" + + printf "Examples\n" + printf " Package all components for testing:\n" + printf " devbox package\n\n" + printf " Package specific component for production:\n" + printf " devbox package --component=chat --test-mode=production\n\n" + printf " Package with CI/CD integration tests:\n" + printf " devbox package --run-cicd-tests=true\n\n" + printf " Package with both CI/CD and legacy integration tests:\n" + printf " devbox package --run-cicd-tests=true --run-integration=true\n\n" + printf " Package with custom image tag and CI/CD tests:\n" + printf " devbox package --image-tag=v1.2.3 --test-mode=production --run-cicd-tests=true\n\n" + else + printf "devbox package - Package local code into Docker containers for final testing with CI/CD integration.\n\n" + fi +} + +# :command.parse_requirements +devbox_package_parse_requirements() { + # :command.fixed_flags_filter + while [[ $# -gt 0 ]]; do + key="$1" + case "$key" in + --help | -h) + long_usage=yes + devbox_package_usage + exit + ;; + *) + break + ;; + esac + done + + # :command.command_filter + action="package" + + # :command.parse_requirements_while + while [[ $# -gt 0 ]]; do + key="$1" + case "$key" in + # :flag.case + --working-home | -w) + # :flag.case_arg + if [[ -n ${2+x} ]]; then + add_arg '--working-home' "$2" + shift 2 + else + printf "%s\n" "--working-home requires an argument: --working-home WORKING_HOME" >&2 + exit 1 + fi + ;; + # :flag.case + --image-repo | -r) + # :flag.case_arg + if [[ -n ${2+x} ]]; then + add_arg '--image-repo' "$2" + shift 2 + else + printf "%s\n" "--image-repo requires an argument: --image-repo IMAGE_REPO" >&2 + exit 1 + fi + ;; + # :flag.case + --image-tag | -t) + # :flag.case_arg + if [[ -n ${2+x} ]]; then + add_arg '--image-tag' "$2" + shift 2 + else + printf "%s\n" "--image-tag requires an argument: --image-tag IMAGE_TAG" >&2 + exit 1 + fi + ;; + # :flag.case + --component | -c) + # :flag.case_arg + if [[ -n ${2+x} ]]; then + add_arg '--component' "$2" + shift 2 + else + printf "%s\n" "--component requires an argument: --component COMPONENT" >&2 + exit 1 + fi + ;; + # :flag.case + --test-mode | -m) + # :flag.case_arg + if [[ -n ${2+x} ]]; then + add_arg '--test-mode' "$2" + shift 2 + else + printf "%s\n" "--test-mode requires an argument: --test-mode TEST_MODE" >&2 + exit 1 + fi + ;; + # :flag.case + --run-integration | -i) + # :flag.case_arg + if [[ -n ${2+x} ]]; then + add_arg '--run-integration' "$2" + shift 2 + else + printf "%s\n" "--run-integration requires an argument: --run-integration RUN_INTEGRATION" >&2 + exit 1 + fi + ;; + # :flag.case + --run-cicd-tests | -j) + # :flag.case_arg + if [[ -n ${2+x} ]]; then + add_arg '--run-cicd-tests' "$2" + shift 2 + else + printf "%s\n" "--run-cicd-tests requires an argument: --run-cicd-tests RUN_CICD_TESTS" >&2 + exit 1 + fi + ;; + -?*) + printf "invalid option: %s\n" "$key" >&2 + exit 1 + ;; + *) + # :command.parse_requirements_case + # :command.parse_requirements_case_simple + printf "invalid argument: %s\n" "$key" >&2 + exit 1 + ;; + esac + done + + # :command.default_assignments + if [ -z "$(get_arg '--working-home')" ]; then + add_arg '--working-home' "${HOME}/devbox" + fi + if [ -z "$(get_arg '--image-repo')" ]; then + add_arg '--image-repo' 'freeleaps-local' + fi + if [ -z "$(get_arg '--image-tag')" ]; then + add_arg '--image-tag' "$(date +%Y%m%d-%H%M%S)" + fi + if [ -z "$(get_arg '--test-mode')" ]; then + add_arg '--test-mode' 'test' + fi + if [ -z "$(get_arg '--run-integration')" ]; then + add_arg '--run-integration' 'false' + fi + if [ -z "$(get_arg '--run-cicd-tests')" ]; then + add_arg '--run-cicd-tests' 'false' + fi +} + +# CI/CD Integration Test Support +# Parse Jenkins pipeline configuration to extract test information +parse_jenkins_pipeline_config() { + local working_home="$1" + local component="$2" + + log_info "Parsing Jenkins pipeline configuration for $component..." + + # Look for Jenkinsfile in common locations + local jenkinsfile_paths=( + "$working_home/freeleaps/Jenkinsfile" + "$working_home/freeleaps/ci/Jenkinsfile" + "$working_home/freeleaps/.jenkins/Jenkinsfile" + "$working_home/freeleaps/apps/$component/Jenkinsfile" + ) + + local jenkinsfile_path="" + for path in "${jenkinsfile_paths[@]}"; do + if [[ -f "$path" ]]; then + jenkinsfile_path="$path" + break + fi + done + + if [[ -z "$jenkinsfile_path" ]]; then + log_warn "No Jenkinsfile found for $component" + return 1 + fi + + log_info "Found Jenkinsfile: $jenkinsfile_path" + + # Extract component configuration from Jenkinsfile + local component_config=$(grep -A 50 "name: '$component'" "$jenkinsfile_path" 2>/dev/null || + grep -A 50 "name: \"$component\"" "$jenkinsfile_path" 2>/dev/null) + + if [[ -z "$component_config" ]]; then + log_warn "Component $component not found in Jenkinsfile" + return 1 + fi + + # Parse key configuration values + local config_values=() + + # Extract language + local language=$(echo "$component_config" | grep -o "language: '[^']*'" | cut -d"'" -f2) + config_values+=("language:$language") + + # Extract dependencies manager + local deps_manager=$(echo "$component_config" | grep -o "dependenciesManager: '[^']*'" | cut -d"'" -f2) + config_values+=("deps_manager:$deps_manager") + + # Extract build agent image + local build_image=$(echo "$component_config" | grep -o "buildAgentImage: '[^']*'" | cut -d"'" -f2) + config_values+=("build_image:$build_image") + + # Extract test configuration + local lint_enabled=$(echo "$component_config" | grep -o "lintEnabled: [^,]*" | cut -d: -f2 | tr -d ' ') + config_values+=("lint_enabled:$lint_enabled") + + local sast_enabled=$(echo "$component_config" | grep -o "sastEnabled: [^,]*" | cut -d: -f2 | tr -d ' ') + config_values+=("sast_enabled:$sast_enabled") + + # Store configuration for later use + local config_file="$working_home/.jenkins-config-$component" + printf "%s\n" "${config_values[@]}" >"$config_file" + + log_info "Jenkins configuration parsed for $component" + return 0 +} + +# Execute CI/CD style integration tests +run_cicd_integration_tests() { + local working_home="$1" + local image_repo="$2" + local image_tag="$3" + local component="$4" + local test_mode="$5" + + log_info "Running CI/CD integration tests for $component..." + + # Parse Jenkins configuration + if ! parse_jenkins_pipeline_config "$working_home" "$component"; then + log_warn "Using default CI/CD test configuration for $component" + fi + + # Create CI/CD test environment + local test_network="devbox-cicd-test-network" + if ! docker network ls | grep -q "$test_network"; then + docker network create "$test_network" + fi + + # Start CI/CD test dependencies based on component + local test_containers=() + start_cicd_test_dependencies "$component" "$test_network" test_containers + + # Wait for dependencies to be ready + log_info "Waiting for CI/CD test dependencies to be ready..." + wait_for_cicd_dependencies "$component" "$test_network" + + # Run component-specific CI/CD tests + local test_results=() + if run_component_cicd_tests "$component" "$image_repo" "$image_tag" "$test_network" "$test_mode"; then + test_results+=("$component:CICD_PASSED") + log_info "$component CI/CD integration tests passed" + else + test_results+=("$component:CICD_FAILED") + log_error "$component CI/CD integration tests failed" + fi + + # Clean up test containers + cleanup_cicd_test_containers test_containers + + # Report results + echo + echo "===========================================================" + echo "CI/CD Integration Test Results for $component:" + echo "===========================================================" + for result in "${test_results[@]}"; do + local comp=$(echo "$result" | cut -d':' -f1) + local status=$(echo "$result" | cut -d':' -f2) + case "$status" in + CICD_PASSED) echo "✓ $comp: CI/CD tests passed" ;; + CICD_FAILED) echo "✗ $comp: CI/CD tests failed" ;; + esac + done + echo "===========================================================" + + # Check if tests passed + for result in "${test_results[@]}"; do + if [[ "$result" == *":CICD_FAILED" ]]; then + return 1 + fi + done + + log_info "CI/CD integration tests completed successfully for $component" + return 0 +} + +# Start CI/CD test dependencies based on component +start_cicd_test_dependencies() { + local component="$1" + local network="$2" + local -n containers="$3" + + log_info "Starting CI/CD test dependencies for $component..." + + case "$component" in + chat | authentication | central_storage | content | notification | payment) + # Start MongoDB for microservices + local mongodb_container=$(docker run -d --name "cicd-mongodb-$component" --network "$network" \ + -e MONGO_INITDB_ROOT_USERNAME=test -e MONGO_INITDB_ROOT_PASSWORD=test \ + mongo:5.0) + containers+=("$mongodb_container") + + # Start Redis for caching + local redis_container=$(docker run -d --name "cicd-redis-$component" --network "$network" \ + redis:7-alpine) + containers+=("$redis_container") + + # Start RabbitMQ for messaging + local rabbitmq_container=$(docker run -d --name "cicd-rabbitmq-$component" --network "$network" \ + -e RABBITMQ_DEFAULT_USER=test -e RABBITMQ_DEFAULT_PASS=test \ + rabbitmq:3-management) + containers+=("$rabbitmq_container") + ;; + + devsvc) + # DevSVC specific dependencies + local mongodb_container=$(docker run -d --name "cicd-mongodb-$component" --network "$network" \ + -e MONGO_INITDB_ROOT_USERNAME=test -e MONGO_INITDB_ROOT_PASSWORD=test \ + mongo:5.0) + containers+=("$mongodb_container") + ;; + + *) + # Default dependencies + local mongodb_container=$(docker run -d --name "cicd-mongodb-$component" --network "$network" \ + -e MONGO_INITDB_ROOT_USERNAME=test -e MONGO_INITDB_ROOT_PASSWORD=test \ + mongo:5.0) + containers+=("$mongodb_container") + ;; + esac +} + +# Wait for CI/CD dependencies to be ready +wait_for_cicd_dependencies() { + local component="$1" + local network="$2" + + log_info "Waiting for CI/CD dependencies to be ready..." + + # Wait for MongoDB + if docker ps --format '{{.Names}}' | grep -q "cicd-mongodb-$component"; then + local max_attempts=30 + local attempt=1 + while [[ $attempt -le $max_attempts ]]; do + if docker exec "cicd-mongodb-$component" mongosh --eval "db.adminCommand('ping')" &>/dev/null; then + log_info "MongoDB is ready" + break + fi + if [[ $attempt -eq $max_attempts ]]; then + log_error "MongoDB failed to start" + return 1 + fi + sleep 2 + attempt=$((attempt + 1)) + done + fi + + # Wait for Redis + if docker ps --format '{{.Names}}' | grep -q "cicd-redis-$component"; then + local max_attempts=30 + local attempt=1 + while [[ $attempt -le $max_attempts ]]; do + if docker exec "cicd-redis-$component" redis-cli ping &>/dev/null; then + log_info "Redis is ready" + break + fi + if [[ $attempt -eq $max_attempts ]]; then + log_error "Redis failed to start" + return 1 + fi + sleep 2 + attempt=$((attempt + 1)) + done + fi + + # Wait for RabbitMQ + if docker ps --format '{{.Names}}' | grep -q "cicd-rabbitmq-$component"; then + local max_attempts=30 + local attempt=1 + while [[ $attempt -le $max_attempts ]]; do + if docker exec "cicd-rabbitmq-$component" rabbitmq-diagnostics ping &>/dev/null; then + log_info "RabbitMQ is ready" + break + fi + if [[ $attempt -eq $max_attempts ]]; then + log_error "RabbitMQ failed to start" + return 1 + fi + sleep 2 + attempt=$((attempt + 1)) + done + fi + + log_info "All CI/CD dependencies are ready" +} + +# Run component-specific CI/CD tests +run_component_cicd_tests() { + local component="$1" + local image_repo="$2" + local image_tag="$3" + local network="$4" + local test_mode="$5" + + local image_name="$image_repo/$component:$image_tag" + + # Check if image exists + if ! docker image inspect "$image_name" &>/dev/null; then + log_error "Image not found for $component: $image_name" + return 1 + fi + + log_info "Running CI/CD tests for $component..." + + # Set up environment variables based on component + local env_vars=() + setup_component_env_vars "$component" "$network" env_vars + + # Run the component container + local test_container=$(docker run -d --name "cicd-test-$component" --network "$network" \ + "${env_vars[@]}" \ + -e TEST_MODE="cicd" \ + -e CI_ENVIRONMENT="true" \ + "$image_name") + + # Wait for component to start + sleep 10 + + # Check if container is running + if ! docker ps --format '{{.Names}}' | grep -q "cicd-test-$component"; then + log_error "$component failed to start in CI/CD test mode" + docker logs "$test_container" 2>/dev/null || true + return 1 + fi + + # Run CI/CD specific tests + local test_success=true + + # Health check test + if ! run_health_check_test "$test_container" "$component"; then + test_success=false + fi + + # API endpoint tests + if ! run_api_endpoint_tests "$test_container" "$component"; then + test_success=false + fi + + # Integration tests + if ! run_integration_tests "$test_container" "$component" "$network"; then + test_success=false + fi + + # Performance tests (if in production mode) + if [[ "$test_mode" == "production" ]]; then + if ! run_performance_tests "$test_container" "$component"; then + test_success=false + fi + fi + + # Clean up test container + docker stop "$test_container" &>/dev/null || true + docker rm "$test_container" &>/dev/null || true + + return $([[ "$test_success" == "true" ]] && echo 0 || echo 1) +} + +# Set up component-specific environment variables +setup_component_env_vars() { + local component="$1" + local network="$2" + local -n env_array="$3" + + case "$component" in + chat | authentication | central_storage | content | notification | payment) + env_array+=( + "-e" "MONGODB_URI=mongodb://test:test@cicd-mongodb-$component:27017" + "-e" "REDIS_URL=redis://cicd-redis-$component:6379" + "-e" "RABBITMQ_HOST=cicd-rabbitmq-$component" + "-e" "RABBITMQ_PORT=5672" + "-e" "RABBITMQ_USER=test" + "-e" "RABBITMQ_PASS=test" + "-e" "SERVICE_API_ACCESS_HOST=0.0.0.0" + "-e" "SERVICE_API_ACCESS_PORT=8000" + ) + ;; + + devsvc) + env_array+=( + "-e" "MONGODB_URI=mongodb://test:test@cicd-mongodb-$component:27017" + "-e" "SERVICE_API_ACCESS_HOST=0.0.0.0" + "-e" "SERVICE_API_ACCESS_PORT=8000" + ) + ;; + + *) + env_array+=( + "-e" "SERVICE_API_ACCESS_HOST=0.0.0.0" + "-e" "SERVICE_API_ACCESS_PORT=8000" + ) + ;; + esac +} + +# Run health check test +run_health_check_test() { + local container="$1" + local component="$2" + + log_info "Running health check test for $component..." + + local max_attempts=30 + local attempt=1 + + while [[ $attempt -le $max_attempts ]]; do + if docker exec "$container" python -c " +import requests +import os +try: + port = os.environ.get('SERVICE_API_ACCESS_PORT', '8000') + response = requests.get(f'http://localhost:{port}/health', timeout=5) + if response.status_code == 200: + print('Health check passed') + exit(0) + else: + print(f'Health check failed: {response.status_code}') + exit(1) +except Exception as e: + print(f'Health check error: {e}') + exit(1) +" &>/dev/null; then + log_info "Health check passed for $component" + return 0 + fi + + if [[ $attempt -eq $max_attempts ]]; then + log_error "Health check failed for $component" + return 1 + fi + + sleep 2 + attempt=$((attempt + 1)) + done +} + +# Run API endpoint tests +run_api_endpoint_tests() { + local container="$1" + local component="$2" + + log_info "Running API endpoint tests for $component..." + + # Common API endpoints to test + local endpoints=("/health" "/docs" "/openapi.json") + + for endpoint in "${endpoints[@]}"; do + if ! docker exec "$container" python -c " +import requests +import os +try: + port = os.environ.get('SERVICE_API_ACCESS_PORT', '8000') + response = requests.get(f'http://localhost:{port}{endpoint}', timeout=5) + if response.status_code in [200, 404]: # 404 is OK for optional endpoints + print(f'Endpoint {endpoint} accessible') + exit(0) + else: + print(f'Endpoint {endpoint} failed: {response.status_code}') + exit(1) +except Exception as e: + print(f'Endpoint {endpoint} error: {e}') + exit(1) +" &>/dev/null; then + log_warn "API endpoint $endpoint test failed for $component" + else + log_info "API endpoint $endpoint test passed for $component" + fi + done + + return 0 +} + +# Run integration tests +run_integration_tests() { + local container="$1" + local component="$2" + local network="$3" + + log_info "Running integration tests for $component..." + + # Run component-specific integration tests + case "$component" in + chat) + run_chat_integration_tests "$container" "$network" + ;; + authentication) + run_auth_integration_tests "$container" "$network" + ;; + central_storage) + run_storage_integration_tests "$container" "$network" + ;; + *) + log_info "No specific integration tests for $component" + ;; + esac + + return 0 +} + +# Component-specific integration tests +run_chat_integration_tests() { + local container="$1" + local network="$2" + + log_info "Running chat integration tests..." + + # Test chat service connectivity + if docker exec "$container" python -c " +import requests +try: + response = requests.get('http://localhost:8000/health', timeout=5) + print('Chat service is responding') +except Exception as e: + print(f'Chat service error: {e}') + exit(1) +" &>/dev/null; then + log_info "Chat integration tests passed" + else + log_error "Chat integration tests failed" + return 1 + fi +} + +run_auth_integration_tests() { + local container="$1" + local network="$2" + + log_info "Running authentication integration tests..." + + # Test authentication service + if docker exec "$container" python -c " +import requests +try: + response = requests.get('http://localhost:8000/health', timeout=5) + print('Auth service is responding') +except Exception as e: + print(f'Auth service error: {e}') + exit(1) +" &>/dev/null; then + log_info "Authentication integration tests passed" + else + log_error "Authentication integration tests failed" + return 1 + fi +} + +run_storage_integration_tests() { + local container="$1" + local network="$2" + + log_info "Running storage integration tests..." + + # Test storage service + if docker exec "$container" python -c " +import requests +try: + response = requests.get('http://localhost:8000/health', timeout=5) + print('Storage service is responding') +except Exception as e: + print(f'Storage service error: {e}') + exit(1) +" &>/dev/null; then + log_info "Storage integration tests passed" + else + log_error "Storage integration tests failed" + return 1 + fi +} + +# Run performance tests (for production mode) +run_performance_tests() { + local container="$1" + local component="$2" + + log_info "Running performance tests for $component..." + + # Simple load test + if docker exec "$container" python -c " +import requests +import time +import statistics + +url = 'http://localhost:8000/health' +response_times = [] + +for i in range(10): + start_time = time.time() + try: + response = requests.get(url, timeout=5) + if response.status_code == 200: + response_times.append(time.time() - start_time) + except: + pass + +if response_times: + avg_time = statistics.mean(response_times) + max_time = max(response_times) + print(f'Average response time: {avg_time:.3f}s') + print(f'Max response time: {max_time:.3f}s') + + if avg_time < 1.0 and max_time < 2.0: + print('Performance tests passed') + exit(0) + else: + print('Performance tests failed - response times too high') + exit(1) +else: + print('Performance tests failed - no successful requests') + exit(1) +" &>/dev/null; then + log_info "Performance tests passed for $component" + return 0 + else + log_error "Performance tests failed for $component" + return 1 + fi +} + +# Clean up CI/CD test containers +cleanup_cicd_test_containers() { + local -n containers="$1" + + log_info "Cleaning up CI/CD test containers..." + + for container in "${containers[@]}"; do + docker stop "$container" &>/dev/null || true + docker rm "$container" &>/dev/null || true + done + + # Clean up test networks + docker network rm "devbox-cicd-test-network" &>/dev/null || true +} + +# Enhanced package command with CI/CD integration +package_local_code_with_cicd() { + local component="$1" + local working_home="$2" + local image_repo="$3" + local image_tag="$4" + local test_mode="$5" + local run_cicd_tests="$6" + + log_info "Packaging local code for $component with CI/CD integration..." + + # Validate component exists + local component_dir="$working_home/freeleaps/apps/$component" + if [[ ! -d "$component_dir" ]]; then + log_error "Component directory not found: $component_dir" + return 1 + fi + + # Check for Dockerfile + local dockerfile_path="$component_dir/Dockerfile" + if [[ ! -f "$dockerfile_path" ]]; then + log_error "Dockerfile not found for $component: $dockerfile_path" + return 1 + fi + + # Run pre-packaging tests if in test mode + if [[ "$test_mode" == "test" ]]; then + if ! run_component_tests "$component" "$component_dir"; then + log_error "Component tests failed for $component" + return 1 + fi + fi + + # Create production-ready image + local image_name="$image_repo/$component:$image_tag" + local build_context="$component_dir" + + # Use multi-stage build for production images + if [[ "$test_mode" == "production" ]]; then + build_context="$working_home/freeleaps" + dockerfile_path="$component_dir/Dockerfile.prod" + + # Create production Dockerfile if it doesn't exist + if [[ ! -f "$dockerfile_path" ]]; then + create_production_dockerfile "$component" "$component_dir" "$dockerfile_path" + fi + fi + + # Build the image + if retry_command 3 10 "docker buildx build --platform linux/amd64 --no-cache -t $image_name -f $dockerfile_path $build_context" "Building production image for $component"; then + log_info "Successfully packaged $component: $image_name" + else + log_error "Failed to package $component" + return 1 + fi + + # Run CI/CD integration tests if requested + if [[ "$run_cicd_tests" == "true" ]]; then + log_info "Running CI/CD integration tests for $component..." + if ! run_cicd_integration_tests "$working_home" "$image_repo" "$image_tag" "$component" "$test_mode"; then + log_error "CI/CD integration tests failed for $component" + return 1 + fi + log_info "CI/CD integration tests passed for $component" + fi + + return 0 +} + +# OpenAI Code Review Integration +# Perform automated code review using OpenAI before submitting PRs + +# Initialize OpenAI code review configuration +init_openai_code_review() { + local working_home="$1" + + log_info "Initializing OpenAI code review configuration..." + + # Create code review configuration directory + local review_dir="$working_home/.code-review" + mkdir -p "$review_dir" + + # Create configuration file if it doesn't exist + local config_file="$review_dir/config.yaml" + if [[ ! -f "$config_file" ]]; then + cat >"$config_file" <<'EOF' +# OpenAI Code Review Configuration +openai: + api_key: "" # Set your OpenAI API key here or use environment variable + model: "gpt-4" # Model to use for code review + max_tokens: 4000 # Maximum tokens for review response + temperature: 0.1 # Lower temperature for more focused reviews + +review: + languages: + - python + - javascript + - typescript + - java + - go + - rust + file_extensions: + - .py + - .js + - .ts + - .jsx + - .tsx + - .java + - .go + - .rs + - .cpp + - .c + - .h + - .hpp + exclude_patterns: + - "node_modules/" + - "__pycache__/" + - ".git/" + - "*.min.js" + - "*.min.css" + - "dist/" + - "build/" + - "target/" + - "vendor/" + max_file_size: 100000 # Maximum file size in bytes to review + max_files_per_review: 50 # Maximum number of files to review at once + +output: + format: "html" # html, markdown, json + include_suggestions: true + include_severity: true + include_line_numbers: true + include_code_snippets: true +EOF + log_info "Created code review configuration: $config_file" + fi + + # Create templates directory + mkdir -p "$review_dir/templates" + + # Create HTML template for review reports + local html_template="$review_dir/templates/review_template.html" + if [[ ! -f "$html_template" ]]; then + cat >"$html_template" <<'EOF' + + + + + + Code Review Report - {{.Component}} + + + +
+
+

Code Review Report

+
+

Component: {{.Component}} | Generated: {{.Timestamp}} | Files Reviewed: {{.TotalFiles}}

+
+
+ +
+
+
+
{{.CriticalIssues}}
+
Critical Issues
+
+
+
{{.Warnings}}
+
Warnings
+
+
+
{{.Suggestions}}
+
Suggestions
+
+
+
{{.FilesReviewed}}
+
Files Reviewed
+
+
+
+ + + +
+ {{range .Files}} +
+
+ {{.Filename}} +
{{.Path}}
+
+
+ {{range .Issues}} +
+ {{.Severity}} + {{if .LineNumber}}Line {{.LineNumber}}{{end}} +

{{.Title}}

+

{{.Description}}

+ {{if .CodeSnippet}} +
{{.CodeSnippet}}
+ {{end}} + {{if .Suggestion}} +
+

Suggestion:

+

{{.Suggestion}}

+
+ {{end}} +
+ {{end}} +
+
+ {{end}} +
+ + +
+ + + + +EOF + log_info "Created HTML review template: $html_template" + fi + + log_info "OpenAI code review configuration initialized" +} + +# Get OpenAI API key from environment or config +get_openai_api_key() { + local working_home="$1" + + # Check environment variable first + if [[ -n "$OPENAI_API_KEY" ]]; then + echo "$OPENAI_API_KEY" + return 0 + fi + + # Check config file + local config_file="$working_home/.code-review/config.yaml" + if [[ -f "$config_file" ]]; then + local api_key=$(grep "api_key:" "$config_file" | cut -d':' -f2 | tr -d ' "') + if [[ -n "$api_key" ]]; then + echo "$api_key" + return 0 + fi + fi + + return 1 +} + +# Validate OpenAI configuration +validate_openai_config() { + local working_home="$1" + + log_info "Validating OpenAI configuration..." + + # Check if API key is available + if ! get_openai_api_key "$working_home"; then + log_error "OpenAI API key not found. Please set OPENAI_API_KEY environment variable or configure in .code-review/config.yaml" + return 1 + fi + + # Test API connectivity + local api_key=$(get_openai_api_key "$working_home") + if ! test_openai_api_connectivity "$api_key"; then + log_error "OpenAI API connectivity test failed" + return 1 + fi + + log_info "OpenAI configuration validated successfully" + return 0 +} + +# Test OpenAI API connectivity +test_openai_api_connectivity() { + local api_key="$1" + + log_info "Testing OpenAI API connectivity..." + + local test_response=$(curl -s -X POST "https://api.openai.com/v1/chat/completions" \ + -H "Authorization: Bearer $api_key" \ + -H "Content-Type: application/json" \ + -d '{ + "model": "gpt-4", + "messages": [{"role": "user", "content": "Hello"}], + "max_tokens": 10 + }' 2>/dev/null) + + if [[ -n "$test_response" ]] && echo "$test_response" | grep -q "choices"; then + log_info "OpenAI API connectivity test passed" + return 0 + else + log_error "OpenAI API connectivity test failed" + return 1 + fi +} + +# Get changed files for review +get_changed_files() { + local working_home="$1" + local component="$2" + + log_info "Getting changed files for review..." + + local component_dir="$working_home/freeleaps/apps/$component" + if [[ ! -d "$component_dir" ]]; then + log_error "Component directory not found: $component_dir" + return 1 + fi + + cd "$component_dir" + + # Get staged and unstaged changes + local changed_files=() + + # Get staged files + while IFS= read -r -d '' file; do + if [[ -f "$file" ]]; then + changed_files+=("$file") + fi + done < <(git diff --cached --name-only -z 2>/dev/null) + + # Get unstaged files + while IFS= read -r -d '' file; do + if [[ -f "$file" ]]; then + changed_files+=("$file") + fi + done < <(git diff --name-only -z 2>/dev/null) + + # Remove duplicates + local unique_files=() + for file in "${changed_files[@]}"; do + if [[ ! " ${unique_files[@]} " =~ " ${file} " ]]; then + unique_files+=("$file") + fi + done + + # Filter by supported file types + local supported_files=() + local config_file="$working_home/.code-review/config.yaml" + local extensions=() + + if [[ -f "$config_file" ]]; then + while IFS= read -r line; do + if [[ "$line" =~ ^[[:space:]]*-[[:space:]]*\. ]]; then + extensions+=("${line##*.}") + fi + done < <(grep -A 20 "file_extensions:" "$config_file") + else + # Default extensions + extensions=("py" "js" "ts" "jsx" "tsx" "java" "go" "rs" "cpp" "c" "h" "hpp") + fi + + for file in "${unique_files[@]}"; do + local extension="${file##*.}" + for ext in "${extensions[@]}"; do + if [[ "$extension" == "$ext" ]]; then + supported_files+=("$file") + break + fi + done + done + + # Limit number of files + local max_files=50 + if [[ ${#supported_files[@]} -gt $max_files ]]; then + log_warn "Too many files changed (${#supported_files[@]}). Reviewing first $max_files files." + supported_files=("${supported_files[@]:0:$max_files}") + fi + + printf "%s\n" "${supported_files[@]}" + return 0 +} + +# Generate code review prompt +generate_review_prompt() { + local component="$1" + local files_content="$2" + + cat </dev/null) + + if [[ -z "$response" ]]; then + log_error "Failed to get response from OpenAI API" + return 1 + fi + + # Extract content from response + local review_content=$(echo "$response" | jq -r '.choices[0].message.content' 2>/dev/null) + if [[ -z "$review_content" ]] || [[ "$review_content" == "null" ]]; then + log_error "Failed to extract review content from OpenAI response" + return 1 + fi + + echo "$review_content" + return 0 +} + +# Parse OpenAI review response +parse_review_response() { + local review_content="$1" + local component="$2" + local working_home="$3" + + log_info "Parsing OpenAI review response..." + + # Create review data structure + local review_data="{ + \"component\": \"$component\", + \"timestamp\": \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\", + \"totalFiles\": 0, + \"criticalIssues\": 0, + \"warnings\": 0, + \"suggestions\": 0, + \"files\": [] + }" + + # Parse the review content to extract structured data + # This is a simplified parser - you might want to enhance it based on your needs + local current_file="" + local current_issues=() + + while IFS= read -r line; do + if [[ "$line" =~ ^File:[[:space:]]*(.+)$ ]]; then + # Save previous file if exists + if [[ -n "$current_file" ]]; then + # Add file to review data + # Implementation would parse current_issues and add to review_data + fi + + current_file="${BASH_REMATCH[1]}" + current_issues=() + elif [[ "$line" =~ ^Severity:[[:space:]]*(CRITICAL|WARNING|INFO|SUGGESTION)$ ]]; then + local severity="${BASH_REMATCH[1]}" + # Parse issue details + # Implementation would extract title, description, line number, etc. + fi + done <<<"$review_content" + + # Save last file + if [[ -n "$current_file" ]]; then + # Add file to review data + fi + + echo "$review_data" +} + +# Generate HTML review report +generate_html_report() { + local review_data="$1" + local working_home="$2" + local component="$3" + + log_info "Generating HTML review report for $component..." + + local review_dir="$working_home/.code-review" + local template_file="$review_dir/templates/review_template.html" + local output_file="$review_dir/reports/${component}_review_$(date +%Y%m%d_%H%M%S).html" + + # Create reports directory + mkdir -p "$review_dir/reports" + + # For now, create a simple HTML report + # In a full implementation, you would use a proper template engine + cat >"$output_file" < + + + + + Code Review Report - $component + + + +
+

Code Review Report - $component

+

Generated: $(date)

+
+
+

Review Summary

+

This is a placeholder for the parsed review content.

+

Raw review data:

+
$(echo "$review_data" | jq . 2>/dev/null || echo "$review_data")
+
+ + +EOF + + echo "$output_file" +} + +# Start local web server for viewing reports +start_review_server() { + local working_home="$1" + local port="$2" + + log_info "Starting review server on port $port..." + + local review_dir="$working_home/.code-review/reports" + + # Create a simple Python HTTP server + local server_script="$review_dir/server.py" + cat >"$server_script" <<'EOF' +#!/usr/bin/env python3 +import http.server +import socketserver +import os +import sys +import json +from urllib.parse import urlparse, parse_qs + +class ReviewHandler(http.server.SimpleHTTPRequestHandler): + def do_GET(self): + parsed_url = urlparse(self.path) + + if parsed_url.path == '/': + # Serve index page + self.send_response(200) + self.send_header('Content-type', 'text/html') + self.end_headers() + + # Generate index page + index_html = self.generate_index() + self.wfile.write(index_html.encode()) + elif parsed_url.path == '/api/reports': + # API endpoint for reports list + self.send_response(200) + self.send_header('Content-type', 'application/json') + self.end_headers() + + reports = self.get_reports_list() + self.wfile.write(json.dumps(reports).encode()) + else: + # Serve static files + super().do_GET() + + def generate_index(self): + reports = self.get_reports_list() + + html = ''' + + + + + + DevBox Code Review Reports + + + +
+
+

DevBox Code Review Reports

+

View and manage your automated code review reports

+
+ + + +
+
Loading reports...
+
+
+ + + + + ''' + return html + + def get_reports_list(self): + reports = [] + current_dir = os.getcwd() + + for filename in os.listdir(current_dir): + if filename.endswith('.html'): + # Extract component name from filename + component = filename.replace('_review_', ' ').replace('.html', '').split()[0] + + # Get file stats + filepath = os.path.join(current_dir, filename) + stat = os.stat(filepath) + + reports.append({ + 'filename': filename, + 'component': component, + 'timestamp': stat.st_mtime, + 'files': 'N/A', # Would need to parse from report + 'issues': 'N/A' # Would need to parse from report + }) + + # Sort by timestamp (newest first) + reports.sort(key=lambda x: x['timestamp'], reverse=True) + + # Convert timestamps to readable format + import datetime + for report in reports: + report['timestamp'] = datetime.datetime.fromtimestamp(report['timestamp']).strftime('%Y-%m-%d %H:%M:%S') + + return reports + +if __name__ == '__main__': + PORT = int(sys.argv[1]) if len(sys.argv) > 1 else 8080 + + with socketserver.TCPServer(("", PORT), ReviewHandler) as httpd: + print(f"Review server running on port {PORT}") + print(f"Open http://localhost:{PORT} in your browser") + httpd.serve_forever() +EOF + + # Make server script executable + chmod +x "$server_script" + + # Start server in background + cd "$review_dir" + python3 "$server_script" "$port" >"$review_dir/server.log" 2>&1 & + local server_pid=$! + + # Save server PID + echo "$server_pid" >"$review_dir/server.pid" + + log_info "Review server started with PID $server_pid" + log_info "Open http://localhost:$port in your browser to view reports" + + return 0 +} + +# Stop review server +stop_review_server() { + local working_home="$1" + + local pid_file="$working_home/.code-review/server.pid" + if [[ -f "$pid_file" ]]; then + local server_pid=$(cat "$pid_file") + if kill -0 "$server_pid" 2>/dev/null; then + log_info "Stopping review server (PID: $server_pid)..." + kill "$server_pid" + rm -f "$pid_file" + fi + fi +} + +# Perform complete code review workflow +perform_code_review() { + local working_home="$1" + local component="$2" + local api_key="$3" + local port="$4" + + log_info "Starting code review workflow for $component..." + + # Initialize code review configuration + init_openai_code_review "$working_home" + + # Validate OpenAI configuration + if ! validate_openai_config "$working_home"; then + return 1 + fi + + # Get changed files + local changed_files=$(get_changed_files "$working_home" "$component") + if [[ -z "$changed_files" ]]; then + log_warn "No changed files found for review" + return 0 + fi + + log_info "Found $(echo "$changed_files" | wc -l) files to review" + + # Prepare files content for review + local files_content="" + local component_dir="$working_home/freeleaps/apps/$component" + + while IFS= read -r file; do + if [[ -f "$component_dir/$file" ]]; then + local file_content=$(cat "$component_dir/$file") + files_content+="\n\n=== File: $file ===\n" + files_content+="$file_content" + fi + done <<<"$changed_files" + + # Perform OpenAI review + local review_content=$(perform_openai_code_review "$working_home" "$component" "$files_content") + if [[ -z "$review_content" ]]; then + log_error "Failed to perform code review" + return 1 + fi + + # Parse review response + local review_data=$(parse_review_response "$review_content" "$component" "$working_home") + + # Generate HTML report + local report_file=$(generate_html_report "$review_data" "$working_home" "$component") + + # Start review server if port is provided + if [[ -n "$port" ]]; then + start_review_server "$working_home" "$port" + fi + + log_info "Code review completed successfully" + log_info "Report generated: $report_file" + + if [[ -n "$port" ]]; then + log_info "View reports at: http://localhost:$port" + fi + + return 0 +} + +# :command.function +devbox_review_command() { + local WORKING_HOME="$(get_arg '--working-home' "${HOME}/devbox")" + local COMPONENT="$(get_arg '--component')" + local API_KEY="$(get_arg '--api-key')" + local PORT="$(get_arg '--port' '8080')" + local START_SERVER="$(get_arg '--start-server' 'true')" + local STOP_SERVER="$(get_arg '--stop-server' 'false')" + + log_info "Starting OpenAI code review process..." + + # Handle stop server request + if [[ "$STOP_SERVER" == "true" ]]; then + stop_review_server "$WORKING_HOME" + exit_with_message "Review server stopped successfully" 0 + fi + + # Validate required parameters + if [[ -z "$COMPONENT" ]]; then + exit_with_message "Component is required. Use --component to specify which component to review." 1 + fi + + # Set API key if provided + if [[ -n "$API_KEY" ]]; then + export OPENAI_API_KEY="$API_KEY" + fi + + # Perform code review + if perform_code_review "$WORKING_HOME" "$COMPONENT" "$API_KEY" "$PORT"; then + local success_message="Code review completed successfully for $COMPONENT!" + + if [[ "$START_SERVER" == "true" ]]; then + success_message+="\nView reports at: http://localhost:$PORT" + success_message+="\nTo stop the review server: devbox review --stop-server" + fi + + exit_with_message "$success_message" 0 + else + exit_with_message "Code review failed for $COMPONENT. Check the logs above for details." 1 + fi +} + +# :command.usage +devbox_review_usage() { + if [[ -n $long_usage ]]; then + printf "Command\n" + printf " devbox review : Perform OpenAI-powered code review before submitting PRs.\n\n" + + printf "Arguments\n" + printf " --working-home -w [Optional] : Specifies the working home directory. Default: %s/devbox\n" "$HOME" + printf " --component -c [Required] : Specifies the component to review\n" + printf " --api-key -k [Optional] : Specifies the OpenAI API key. Can also use OPENAI_API_KEY env var\n" + printf " --port -p [Optional] : Specifies the port for the review server. Default: 8080\n" + printf " --start-server -s [Optional] : Start local web server to view reports. Default: true\n" + printf " --stop-server [Optional] : Stop the review server\n\n" + + printf "Global Arguments\n" + printf " --help -h : Show this help message and exit.\n\n" + + printf "Examples\n" + printf " Review chat component:\n" + printf " devbox review --component=chat\n\n" + printf " Review with custom API key:\n" + printf " devbox review --component=authentication --api-key=your-api-key\n\n" + printf " Review without starting server:\n" + printf " devbox review --component=content --start-server=false\n\n" + printf " Review on custom port:\n" + printf " devbox review --component=chat --port=9090\n\n" + printf " Stop review server:\n" + printf " devbox review --stop-server\n\n" + else + printf "devbox review - Perform OpenAI-powered code review before submitting PRs.\n\n" + fi +} + +# :command.parse_requirements +devbox_review_parse_requirements() { + # :command.fixed_flags_filter + while [[ $# -gt 0 ]]; do + key="$1" + case "$key" in + --help | -h) + long_usage=yes + devbox_review_usage + exit + ;; + *) + break + ;; + esac + done + + # :command.command_filter + action="review" + + # :command.parse_requirements_while + while [[ $# -gt 0 ]]; do + key="$1" + case "$key" in + # :flag.case + --working-home | -w) + # :flag.case_arg + if [[ -n ${2+x} ]]; then + add_arg '--working-home' "$2" + shift 2 + else + printf "%s\n" "--working-home requires an argument: --working-home WORKING_HOME" >&2 + exit 1 + fi + ;; + # :flag.case + --component | -c) + # :flag.case_arg + if [[ -n ${2+x} ]]; then + add_arg '--component' "$2" + shift 2 + else + printf "%s\n" "--component requires an argument: --component COMPONENT" >&2 + exit 1 + fi + ;; + # :flag.case + --api-key | -k) + # :flag.case_arg + if [[ -n ${2+x} ]]; then + add_arg '--api-key' "$2" + shift 2 + else + printf "%s\n" "--api-key requires an argument: --api-key API_KEY" >&2 + exit 1 + fi + ;; + # :flag.case + --port | -p) + # :flag.case_arg + if [[ -n ${2+x} ]]; then + add_arg '--port' "$2" + shift 2 + else + printf "%s\n" "--port requires an argument: --port PORT" >&2 + exit 1 + fi + ;; + # :flag.case + --start-server | -s) + # :flag.case_arg + if [[ -n ${2+x} ]]; then + add_arg '--start-server' "$2" + shift 2 + else + printf "%s\n" "--start-server requires an argument: --start-server START_SERVER" >&2 + exit 1 + fi + ;; + # :flag.case + --stop-server) + # :flag.case_no_arg + add_arg '--stop-server' 'true' + shift + ;; + -?*) + printf "invalid option: %s\n" "$key" >&2 + exit 1 + ;; + *) + # :command.parse_requirements_case + # :command.parse_requirements_case_simple + printf "invalid argument: %s\n" "$key" >&2 + exit 1 + ;; + esac + done + + # :command.default_assignments + if [ -z "$(get_arg '--working-home')" ]; then + add_arg '--working-home' "${HOME}/devbox" + fi + if [ -z "$(get_arg '--port')" ]; then + add_arg '--port' '8080' + fi + if [ -z "$(get_arg '--start-server')" ]; then + add_arg '--start-server' 'true' + fi +}