diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 4af6c27..36fa8d5 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,12 +1,3 @@ ---- -name: Pull Request -about: Describe the changes in your pull request -title: '' -labels: '' -assignees: '' - ---- - ## ๐Ÿ“‹ Description Brief description of the changes @@ -32,11 +23,5 @@ Brief description of the changes - [ ] My changes generate no new warnings - [ ] Any dependent changes have been merged and published in downstream modules -## ๐Ÿ”— Related Issues -Fixes #(issue number) - -## ๐Ÿ“ธ Screenshots (if appropriate) -Add screenshots to help explain your changes - ## ๐Ÿ” Additional Notes Add any other notes about the pull request here. diff --git a/.github/workflows/ai-code-suggestions.yml b/.github/workflows/ai-code-suggestions.yml deleted file mode 100644 index c525221..0000000 --- a/.github/workflows/ai-code-suggestions.yml +++ /dev/null @@ -1,269 +0,0 @@ -name: AI Code Suggestions - -on: - pull_request: - types: [opened, synchronize] - branches: [ main, develop ] - workflow_dispatch: - -permissions: - contents: read - pull-requests: write - issues: write - checks: write - -jobs: - ai-suggestions: - runs-on: ubuntu-latest - if: github.event.pull_request.draft == false - - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Set up Python - uses: actions/setup-python@v4 - with: - python-version: '3.11' - - - name: Install analysis tools - run: | - python -m pip install --upgrade pip - pip install -r requirements.txt - pip install pylint mypy autopep8 vulture - - - name: Get changed Python files - id: changed-python-files - uses: tj-actions/changed-files@v40 - with: - files: | - **/*.py - files_ignore: | - **/migrations/** - **/tests/** - **/__pycache__/** - - - name: Run Code Analysis - if: steps.changed-python-files.outputs.any_changed == 'true' - run: | - echo "## ๐Ÿ” Code Analysis & Suggestions" > suggestions.md - echo "" >> suggestions.md - - # Create array of changed files - IFS=' ' read -ra CHANGED_FILES <<< "${{ steps.changed-python-files.outputs.all_changed_files }}" - - for file in "${CHANGED_FILES[@]}"; do - if [[ -f "$file" ]]; then - echo "### ๐Ÿ“„ Analysis for \`$file\`" >> suggestions.md - echo "" >> suggestions.md - - # Run pylint for code quality - echo "#### ๐Ÿ“Š Code Quality (Pylint)" >> suggestions.md - pylint "$file" --output-format=text --score=yes --reports=no 2>/dev/null | head -20 >> suggestions.md || echo "No pylint issues found." >> suggestions.md - echo "" >> suggestions.md - - # Run mypy for type checking - echo "#### ๐Ÿ” Type Checking (MyPy)" >> suggestions.md - mypy "$file" --no-error-summary 2>/dev/null | head -10 >> suggestions.md || echo "No type issues found." >> suggestions.md - echo "" >> suggestions.md - - # Check for dead code - echo "#### ๐Ÿงน Dead Code Detection" >> suggestions.md - vulture "$file" 2>/dev/null | head -10 >> suggestions.md || echo "No dead code detected." >> suggestions.md - echo "" >> suggestions.md - - echo "---" >> suggestions.md - echo "" >> suggestions.md - fi - done - - # Add general suggestions - echo "## ๐Ÿ’ก General Suggestions" >> suggestions.md - echo "" >> suggestions.md - echo "### ๐ŸŽฏ Django Best Practices" >> suggestions.md - echo "- Use Django's built-in validators instead of custom validation where possible" >> suggestions.md - echo "- Implement proper error handling with try-catch blocks" >> suggestions.md - echo "- Use Django's timezone utilities for datetime operations" >> suggestions.md - echo "- Consider using select_related() and prefetch_related() for database optimization" >> suggestions.md - echo "- Add docstrings to all public methods and classes" >> suggestions.md - echo "" >> suggestions.md - - echo "### ๐Ÿ” Security Recommendations" >> suggestions.md - echo "- Validate and sanitize all user inputs" >> suggestions.md - echo "- Use Django's built-in CSRF protection" >> suggestions.md - echo "- Implement proper authentication and authorization checks" >> suggestions.md - echo "- Use Django's ORM to prevent SQL injection attacks" >> suggestions.md - echo "" >> suggestions.md - - echo "### ๐Ÿš€ Performance Tips" >> suggestions.md - echo "- Use database indexes for frequently queried fields" >> suggestions.md - echo "- Implement caching for expensive operations" >> suggestions.md - echo "- Consider using async views for I/O-bound operations" >> suggestions.md - echo "- Use pagination for large datasets" >> suggestions.md - - - name: Check Test Coverage - if: steps.changed-python-files.outputs.any_changed == 'true' - run: | - echo "" >> suggestions.md - echo "## ๐Ÿงช Test Coverage Analysis" >> suggestions.md - echo "" >> suggestions.md - - # Run pytest with coverage for changed files - coverage run -m pytest --tb=short || true - coverage report --include="${{ steps.changed-python-files.outputs.all_changed_files }}" >> suggestions.md || echo "Coverage data not available" >> suggestions.md - - - name: Django-specific Analysis - if: steps.changed-python-files.outputs.any_changed == 'true' - run: | - echo "" >> suggestions.md - echo "## ๐Ÿ Django-Specific Analysis" >> suggestions.md - echo "" >> suggestions.md - - # Check for Django migrations - if python manage.py makemigrations --dry-run --verbosity=0 2>/dev/null; then - echo "โœ… **Migrations**: No pending migrations" >> suggestions.md - else - echo "โš ๏ธ **Migrations**: Pending migrations detected. Run \`python manage.py makemigrations\`" >> suggestions.md - fi - echo "" >> suggestions.md - - # Check Django system - echo "### ๐Ÿ”ง Django System Check" >> suggestions.md - python manage.py check --verbosity=0 >> suggestions.md 2>&1 || echo "System check completed with issues" >> suggestions.md - - - name: Post AI Suggestions - if: steps.changed-python-files.outputs.any_changed == 'true' - uses: actions/github-script@v7 - with: - script: | - const fs = require('fs'); - - let suggestions = ''; - if (fs.existsSync('suggestions.md')) { - suggestions = fs.readFileSync('suggestions.md', 'utf8'); - - // Truncate if too long for GitHub comment - if (suggestions.length > 65000) { - suggestions = suggestions.substring(0, 65000) + '\n\n... (truncated for length)'; - } - } - - const comment = ` - ## ๐Ÿค– AI-Powered Code Suggestions - - ${suggestions || 'No specific suggestions for the changed files.'} - - ### ๐Ÿ“š Additional Resources - - - [Django Best Practices](https://docs.djangoproject.com/en/stable/misc/design-philosophies/) - - [Django Security](https://docs.djangoproject.com/en/stable/topics/security/) - - [Python PEP 8 Style Guide](https://pep8.org/) - - [Django REST Framework Best Practices](https://www.django-rest-framework.org/community/3.0-announcement/) - - ### ๐Ÿค Next Steps - - 1. Review the suggestions above - 2. Apply relevant improvements - 3. Add/update tests if needed - 4. Update documentation - 5. Request human review - - --- - *AI-generated suggestions - Use your judgment to apply relevant recommendations.* - `; - - github.rest.issues.createComment({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo, - body: comment - }); - - performance-analysis: - runs-on: ubuntu-latest - if: github.event.pull_request.draft == false - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Set up Python - uses: actions/setup-python@v4 - with: - python-version: '3.11' - - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install -r requirements.txt - pip install django-debug-toolbar memory-profiler - - - name: Run Performance Tests - run: | - echo "## โšก Performance Analysis" > performance.md - echo "" >> performance.md - - # Test Django startup time - echo "### ๐Ÿš€ Django Startup Time" >> performance.md - echo "\`\`\`" >> performance.md - time python manage.py check 2>&1 | head -10 >> performance.md - echo "\`\`\`" >> performance.md - echo "" >> performance.md - - # Check for N+1 queries potential - echo "### ๐Ÿ—ƒ๏ธ Database Query Analysis" >> performance.md - echo "Look for potential N+1 query issues in your models and views:" >> performance.md - echo "- Use \`select_related()\` for foreign key relationships" >> performance.md - echo "- Use \`prefetch_related()\` for many-to-many relationships" >> performance.md - echo "- Consider database indexing for frequently queried fields" >> performance.md - echo "" >> performance.md - - echo "### ๐Ÿ’พ Memory Usage Recommendations" >> performance.md - echo "- Use generator expressions for large datasets" >> performance.md - echo "- Implement pagination for API endpoints" >> performance.md - echo "- Consider using \`only()\` and \`defer()\` for selective field loading" >> performance.md - echo "- Use caching for expensive computations" >> performance.md - - - name: Post Performance Analysis - uses: actions/github-script@v7 - with: - script: | - const fs = require('fs'); - - let performanceAnalysis = ''; - if (fs.existsSync('performance.md')) { - performanceAnalysis = fs.readFileSync('performance.md', 'utf8'); - } - - const comment = ` - ${performanceAnalysis} - - ### ๐ŸŽฏ Performance Optimization Tips - - 1. **Database Optimization** - - Add indexes to frequently queried fields - - Use select_related() and prefetch_related() - - Consider database connection pooling - - 2. **Caching Strategy** - - Implement Redis caching for expensive operations - - Use Django's cache framework - - Consider CDN for static assets - - 3. **Code Optimization** - - Use generator expressions for large datasets - - Implement proper pagination - - Optimize serializers and views - - --- - *Performance suggestions based on Django best practices.* - `; - - github.rest.issues.createComment({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo, - body: comment - }); diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e26cfe5..ec32038 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,11 +19,11 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [3.10.x, 3.11.x, 3.12.x] + python-version: [3.10.x, 3.11.x, 3.12.x, 3.13.x] services: postgres: - image: postgres:15 + image: postgres:17-alpine env: POSTGRES_PASSWORD: postgres POSTGRES_USER: postgres @@ -37,7 +37,7 @@ jobs: - 5432:5432 redis: - image: redis:7 + image: redis:8-alpine options: >- --health-cmd "redis-cli ping" --health-interval 10s @@ -47,15 +47,15 @@ jobs: - 6379:6379 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Cache pip dependencies - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ~/.cache/pip key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} @@ -96,9 +96,9 @@ jobs: - name: Upload coverage to Codecov if: matrix.python-version == '3.11.x' - uses: codecov/codecov-action@v3 + uses: codecov/codecov-action@v5 with: - file: ./coverage.xml + files: ./coverage.xml flags: unittests name: codecov-umbrella @@ -107,39 +107,13 @@ jobs: run: | python manage.py test - - name: Post test results to PR - if: github.event_name == 'pull_request' && matrix.python-version == '3.11.x' - uses: actions/github-script@v7 - with: - script: | - const comment = ` - ## ๐Ÿงช Test Results (Python ${{ matrix.python-version }}) - - โœ… **Tests Status**: ${{ job.status }} - - ### Test Summary - - All tests executed successfully - - Django system checks passed - - Database migrations verified - - Coverage report generated - - ๐Ÿ“Š **Coverage Report**: Available in artifacts - `; - - github.rest.issues.createComment({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo, - body: comment - }); - lint: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: 3.11.x @@ -166,10 +140,10 @@ jobs: security: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: 3.11.x @@ -187,7 +161,7 @@ jobs: run: | # Install compatible versions for safety pip install "typer<0.13.0" "safety==2.4.0b2" || pip install "safety<3.0.0" - + # Run safety check with fallback to pip-audit safety check --json --output safety-report.json || echo "Safety JSON output failed, continuing..." safety check || { @@ -203,7 +177,7 @@ jobs: if: github.ref == 'refs/heads/main' steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Build Docker image run: | diff --git a/.github/workflows/deployment-status.yml b/.github/workflows/deployment-status.yml index 6fc2693..a383f35 100644 --- a/.github/workflows/deployment-status.yml +++ b/.github/workflows/deployment-status.yml @@ -15,7 +15,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Determine status and context id: status diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 304fcff..44f38f0 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -2,7 +2,7 @@ name: Pull Request Checks on: pull_request: - branches: [ main, develop ] + branches: [main, develop] types: [opened, synchronize, reopened] permissions: @@ -12,15 +12,42 @@ permissions: checks: write jobs: - pr-checks: + pr-check-cov: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v5 + - name: Set up Python 3.12.x + uses: actions/setup-python@v5 + with: + python-version: 3.12.x + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt + + - name: Run tests with coverage + run: | + pytest --verbose --tb=short --cov=. --cov-report=xml --cov-report=term + + - name: Upload coverage reports to Codecov with GitHub Action + uses: codecov/codecov-action@v5 + env: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + verbose: true + fail_ci_if_error: true + + pr-checks-tests: runs-on: ubuntu-latest strategy: matrix: python-version: [3.10.x, 3.11.x, 3.12.x] + postgres-version: [17-alpine] + redis-version: [8-alpine] services: postgres: - image: postgres:15 + image: postgres:${{matrix.postgres-version}} env: POSTGRES_PASSWORD: postgres POSTGRES_USER: postgres @@ -34,7 +61,7 @@ jobs: - 5432:5432 redis: - image: redis:7 + image: redis:${{matrix.redis-version}} options: >- --health-cmd "redis-cli ping" --health-interval 10s @@ -44,121 +71,105 @@ jobs: - 6379:6379 steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 - with: - python-version: ${{ matrix.python-version }} - - - name: Cache pip dependencies - uses: actions/cache@v3 - with: - path: ~/.cache/pip - key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} - restore-keys: | - ${{ runner.os }}-pip- - - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install -r requirements.txt - - - name: Set up environment variables - run: | - echo "DJANGO_DEBUG=False" >> $GITHUB_ENV - echo "SECRET_KEY=test-secret-key-for-ci" >> $GITHUB_ENV - echo "ALLOWED_HOSTS=localhost,127.0.0.1" >> $GITHUB_ENV - echo "DATABASE_TYPE=pgsql" >> $GITHUB_ENV - echo "DATABASE_USER=postgres" >> $GITHUB_ENV - echo "DATABASE_PASSWORD=postgres" >> $GITHUB_ENV - echo "DATABASE_HOST=localhost" >> $GITHUB_ENV - echo "DATABASE_PORT=5432" >> $GITHUB_ENV - echo "DATABASE_NAME=test_backend" >> $GITHUB_ENV - echo "REDIS_HOST=localhost" >> $GITHUB_ENV - echo "REDIS_PORT=6379" >> $GITHUB_ENV - echo "REDIS_DB=0" >> $GITHUB_ENV - - - name: Run migrations - run: | - python manage.py migrate - - - name: Run Django system checks - run: | - python manage.py check - - - name: Run tests with coverage - run: | - pytest --verbose --tb=short --cov=. --cov-report=xml --cov-report=term - - - name: Upload coverage reports to Codecov with GitHub Action - uses: codecov/codecov-action@v5 - env: - CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} - - - name: Comment PR with test results - uses: actions/github-script@v7 - if: github.event_name == 'pull_request' - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - try { - const comment = `## ๐Ÿงช Test Results for Python ${{ matrix.python-version }} - - โœ… **All tests passed successfully!** - - ### ๐Ÿ“‹ Summary: - - โœ… Django system checks passed - - โœ… Database migrations applied - - โœ… Unit tests completed - - โœ… Code coverage generated - - ### ๐Ÿ—๏ธ Build Information: - - **Python Version**: ${{ matrix.python-version }} - - **Django Version**: 5.2.1 - - **Database**: PostgreSQL 15 - - **Cache**: Redis 7 - - --- - *This comment was automatically generated by the CI pipeline.*`; - - await github.rest.issues.createComment({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo, - body: comment - }); - } catch (error) { - console.log('Failed to post comment:', error.message); - // Don't fail the workflow if commenting fails + - name: Checkout code + uses: actions/checkout@v5 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + + - name: Cache pip dependencies + uses: actions/cache@v4 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} + restore-keys: | + ${{ runner.os }}-pip- + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt + + - name: Set up environment variables + run: | + echo "DJANGO_DEBUG=False" >> $GITHUB_ENV + echo "SECRET_KEY=test-secret-key-for-ci" >> $GITHUB_ENV + echo "ALLOWED_HOSTS=localhost,127.0.0.1" >> $GITHUB_ENV + echo "DATABASE_TYPE=pgsql" >> $GITHUB_ENV + echo "DATABASE_USER=postgres" >> $GITHUB_ENV + echo "DATABASE_PASSWORD=postgres" >> $GITHUB_ENV + echo "DATABASE_HOST=localhost" >> $GITHUB_ENV + echo "DATABASE_PORT=5432" >> $GITHUB_ENV + echo "DATABASE_NAME=test_backend" >> $GITHUB_ENV + echo "REDIS_HOST=localhost" >> $GITHUB_ENV + echo "REDIS_PORT=6379" >> $GITHUB_ENV + echo "REDIS_DB=0" >> $GITHUB_ENV + + - name: Run migrations + run: | + python manage.py migrate + + - name: Run Django system checks + run: | + python manage.py check + + - name: Run tests + run: | + pytest --verbose --tb=short + pr-check-formating: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v5 + - name: Set up Python 3.12.x + uses: actions/setup-python@v5 + with: + python-version: 3.12.x + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt + - name: Lint with flake8 + run: | + flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics + flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + + - name: Check code formatting with black + run: | + black --check --diff . + + - name: Check import sorting with isort + run: | + isort --check-only --diff . + pr-sec-check: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v5 + - name: Set up Python 3.12.x + uses: actions/setup-python@v5 + with: + python-version: 3.12.x + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt + - name: Security scan with bandit + run: | + bandit -r . --severity-level medium + + - name: Dependency security check + run: | + # Install compatible versions for safety + pip install "typer<0.13.0" "safety==2.4.0b2" || pip install "safety<3.0.0" + + # Run safety check with fallback + safety check || { + echo "Safety check failed, trying pip-audit as fallback..." + pip install pip-audit + pip-audit --desc || echo "Dependency security scan completed with warnings" } - - name: Lint with flake8 - run: | - flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics - flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics - - - name: Check code formatting with black - run: | - black --check --diff . - - - name: Check import sorting with isort - run: | - isort --check-only --diff . - - - name: Security scan with bandit - run: | - bandit -r . --severity-level medium - - - name: Dependency security check - run: | - # Install compatible versions for safety - pip install "typer<0.13.0" "safety==2.4.0b2" || pip install "safety<3.0.0" - - # Run safety check with fallback - safety check || { - echo "Safety check failed, trying pip-audit as fallback..." - pip install pip-audit - pip-audit --desc || echo "Dependency security scan completed with warnings" - } + diff --git a/.github/workflows/test-with-comments.yml b/.github/workflows/test-with-comments.yml deleted file mode 100644 index b2f880a..0000000 --- a/.github/workflows/test-with-comments.yml +++ /dev/null @@ -1,400 +0,0 @@ -name: Test with Comments - -on: - pull_request: - branches: [ main, develop ] - types: [opened, synchronize, reopened] - -permissions: - contents: read - pull-requests: write - issues: write - checks: write - -jobs: - test-with-comments: - runs-on: ubuntu-latest - - services: - postgres: - image: postgres:15 - env: - POSTGRES_PASSWORD: postgres - POSTGRES_USER: postgres - POSTGRES_DB: test_backend - options: >- - --health-cmd pg_isready - --health-interval 10s - --health-timeout 5s - --health-retries 5 - ports: - - 5432:5432 - - redis: - image: redis:7 - options: >- - --health-cmd "redis-cli ping" - --health-interval 10s - --health-timeout 5s - --health-retries 5 - ports: - - 6379:6379 - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Set up Python - uses: actions/setup-python@v4 - with: - python-version: 3.11.x - - - name: Cache pip dependencies - uses: actions/cache@v3 - with: - path: ~/.cache/pip - key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} - restore-keys: | - ${{ runner.os }}-pip- - - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install -r requirements.txt - pip install pytest-cov pytest-html pytest-json-report - - - name: Set up environment variables - run: | - echo "DJANGO_DEBUG=False" >> $GITHUB_ENV - echo "SECRET_KEY=test-secret-key-for-ci" >> $GITHUB_ENV - echo "ALLOWED_HOSTS=localhost,127.0.0.1" >> $GITHUB_ENV - echo "DATABASE_TYPE=pgsql" >> $GITHUB_ENV - echo "DATABASE_USER=postgres" >> $GITHUB_ENV - echo "DATABASE_PASSWORD=postgres" >> $GITHUB_ENV - echo "DATABASE_HOST=localhost" >> $GITHUB_ENV - echo "DATABASE_PORT=5432" >> $GITHUB_ENV - echo "DATABASE_NAME=test_backend" >> $GITHUB_ENV - echo "REDIS_HOST=localhost" >> $GITHUB_ENV - echo "REDIS_PORT=6379" >> $GITHUB_ENV - echo "REDIS_DB=0" >> $GITHUB_ENV - - - name: Run migrations - run: | - python manage.py migrate - - - name: Run tests with coverage and reporting - run: | - pytest \ - --verbose \ - --tb=short \ - --cov=. \ - --cov-report=xml \ - --cov-report=html \ - --cov-report=term \ - --html=reports/pytest-report.html \ - --self-contained-html \ - --json-report \ - --json-report-file=reports/pytest-report.json \ - --junit-xml=reports/junit.xml \ - 2>&1 | tee test-output.log - - - name: Parse test results - if: always() - run: | - # Create reports directory if it doesn't exist - mkdir -p reports - - # Create test results summary - python3 << 'EOF' - import json - import sys - import os - - # Parse pytest JSON report - try: - if os.path.exists('reports/pytest-report.json'): - with open('reports/pytest-report.json', 'r') as f: - report = json.load(f) - else: - print("Pytest JSON report not found, creating default") - report = {'summary': {}, 'tests': []} - - summary = report.get('summary', {}) - tests = report.get('tests', []) - - total = summary.get('total', 0) - passed = summary.get('passed', 0) - failed = summary.get('failed', 0) - skipped = summary.get('skipped', 0) - - # Calculate percentages - if total > 0: - pass_rate = (passed / total) * 100 - fail_rate = (failed / total) * 100 - else: - pass_rate = 0 - fail_rate = 0 - - # Get failed tests details - failed_tests = [] - for test in tests: - if test.get('outcome') == 'failed': - failed_tests.append({ - 'nodeid': test.get('nodeid', 'Unknown test'), - 'longrepr': test.get('call', {}).get('longrepr', 'No details available') - }) - - # Write summary to file - with open('test_summary.json', 'w') as f: - json.dump({ - 'total': total, - 'passed': passed, - 'failed': failed, - 'skipped': skipped, - 'pass_rate': round(pass_rate, 2), - 'fail_rate': round(fail_rate, 2), - 'failed_tests': failed_tests[:5] # Limit to first 5 failures - }, f, indent=2) - - print(f"Test summary created: {total} total, {passed} passed, {failed} failed") - - except Exception as e: - print(f"Error parsing test results: {e}") - # Create default summary on error - with open('test_summary.json', 'w') as f: - json.dump({ - 'total': 0, - 'passed': 0, - 'failed': 0, - 'skipped': 0, - 'pass_rate': 0, - 'fail_rate': 0, - 'failed_tests': [] - }, f, indent=2) - EOF - - - name: Parse coverage results - if: always() - run: | - # Extract coverage percentage - COVERAGE="0.0" - if [ -f "coverage.xml" ]; then - COVERAGE=$(python3 -c " - import xml.etree.ElementTree as ET - import sys - try: - tree = ET.parse('coverage.xml') - root = tree.getroot() - coverage = root.attrib.get('line-rate', '0') - percentage = float(coverage) * 100 - print(f'{percentage:.1f}') - except Exception as e: - print('0.0', file=sys.stderr) - print('0.0') - " 2>/dev/null) - fi - - echo "COVERAGE_PERCENT=$COVERAGE" >> $GITHUB_ENV - echo "Coverage percentage: $COVERAGE%" - - - name: Generate detailed comment - if: always() - uses: actions/github-script@v7 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - const fs = require('fs'); - - // Read test summary - let testSummary; - try { - testSummary = JSON.parse(fs.readFileSync('test_summary.json', 'utf8')); - } catch (error) { - console.log('Error reading test summary:', error.message); - testSummary = { - total: 0, passed: 0, failed: 0, skipped: 0, - pass_rate: 0, fail_rate: 0, failed_tests: [] - }; - } - - // Read test output log - let testOutput = ''; - try { - testOutput = fs.readFileSync('test-output.log', 'utf8'); - } catch (error) { - console.log('Error reading test output:', error.message); - testOutput = 'Test output not available'; - } - - // Determine overall status - const overallStatus = testSummary.failed === 0 ? 'โœ… PASSED' : 'โŒ FAILED'; - const statusEmoji = testSummary.failed === 0 ? '๐ŸŽ‰' : 'โš ๏ธ'; - - // Create progress bars - const createProgressBar = (percentage, width = 20) => { - const filled = Math.round((percentage / 100) * width); - const empty = width - filled; - return 'โ–ˆ'.repeat(filled) + 'โ–‘'.repeat(empty); - }; - - const passBar = createProgressBar(testSummary.pass_rate); - const coveragePercent = parseFloat(process.env.COVERAGE_PERCENT || '0'); - const coverageBar = createProgressBar(coveragePercent); - - // Generate failed tests section - let failedTestsSection = ''; - if (testSummary.failed_tests && testSummary.failed_tests.length > 0) { - failedTestsSection = ` - ### โŒ Failed Tests - - ${testSummary.failed_tests.map(test => ` - **${test.nodeid || 'Unknown test'}** - \`\`\` - ${test.call?.longrepr || test.longrepr || 'No details available'} - \`\`\` - `).join('\n')} - - ${testSummary.failed_tests.length >= 5 ? '_Note: Only showing first 5 failures_' : ''} - `; - } - - // Truncate test output to avoid comment size limits - const maxOutputLength = 2000; - const truncatedOutput = testOutput.length > maxOutputLength - ? testOutput.slice(-maxOutputLength) - : testOutput; - - // Create the comment - const comment = `## ${statusEmoji} Test Results Report - - ### ๐Ÿ“Š Overall Status: ${overallStatus} - - | Metric | Value | Progress | - |--------|-------|----------| - | **Total Tests** | ${testSummary.total} | | - | **Passed** | ${testSummary.passed} | ${passBar} ${testSummary.pass_rate}% | - | **Failed** | ${testSummary.failed} | | - | **Skipped** | ${testSummary.skipped} | | - | **Coverage** | ${coveragePercent}% | ${coverageBar} | - - ### ๐Ÿ” Test Details - -
- ๐Ÿ“‹ Click to view detailed test output - - \`\`\` - ${truncatedOutput} - \`\`\` - -
- - ${failedTestsSection} - - ### ๐Ÿ—๏ธ Build Information - - - **Python Version**: 3.11.x - - **Django Version**: 5.2.1 - - **Database**: PostgreSQL 15 - - **Cache**: Redis 7 - - **Commit**: \`${context.sha.substring(0, 8)}\` - - **Branch**: \`${context.payload.pull_request?.head?.ref || 'unknown'}\` - - ### ๐Ÿ“Ž Artifacts - - ${testSummary.failed === 0 ? - 'โœ… All tests passed! No artifacts generated.' : - '๐Ÿ“„ Test reports and coverage details are available in the workflow artifacts.' - } - - --- - - ${testSummary.failed === 0 ? - '๐ŸŽ‰ **Great job!** All tests are passing. This PR is ready for review!' : - 'โš ๏ธ **Please fix the failing tests** before merging this PR.' - } - - ๐Ÿค– This comment was automatically generated by the test workflow`; - - try { - // Find existing comment - const { data: comments } = await github.rest.issues.listComments({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: context.issue.number, - }); - - const existingComment = comments.find(comment => - comment.body.includes('Test Results Report') && - comment.user.type === 'Bot' - ); - - // Update or create comment - if (existingComment) { - await github.rest.issues.updateComment({ - owner: context.repo.owner, - repo: context.repo.repo, - comment_id: existingComment.id, - body: comment - }); - console.log('Updated existing comment'); - } else { - await github.rest.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: context.issue.number, - body: comment - }); - console.log('Created new comment'); - } - } catch (error) { - console.log('Error posting comment:', error.message); - // Don't fail the workflow if commenting fails - } - - - name: Upload test reports - if: always() - uses: actions/upload-artifact@v4 - with: - name: test-reports-${{ github.run_number }} - path: | - reports/ - coverage.xml - test-output.log - test_summary.json - retention-days: 30 - - - name: Publish test results - if: always() - uses: dorny/test-reporter@v1 - with: - name: Django Tests - path: reports/junit.xml - reporter: java-junit - fail-on-error: false - - - name: Set job status - if: always() - run: | - echo "Checking final test status..." - if [ -f "test_summary.json" ]; then - FAILED=$(python3 -c " - import json - import sys - try: - with open('test_summary.json', 'r') as f: - data = json.load(f) - print(data.get('failed', 0)) - except Exception as e: - print('0') - ") - echo "Failed tests: $FAILED" - if [ "$FAILED" -gt 0 ]; then - echo "โŒ Tests failed, marking job as failed" - exit 1 - else - echo "โœ… All tests passed successfully!" - fi - else - echo "โš ๏ธ No test summary found, assuming failure" - exit 1 - fi diff --git a/docker-compose.yml b/docker-compose.yml index 2e1a510..48c101d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,8 +1,6 @@ services: backend: build: . - # For production, use the published image: - # image: ghcr.io/seccodesmith/seccodesmith-backend:latest container_name: seccodesmith-backend hostname: backend networks: @@ -52,7 +50,7 @@ services: test: [ "CMD", "redis-cli", "--raw", "incr", "ping" ] postgres: - image: postgres:14.7-alpine + image: postgres:17-alpine container_name: database-Backend hostname: db networks: @@ -86,4 +84,4 @@ networks: volumes: postgres_data: - redis_data: \ No newline at end of file + redis_data: