feat: Add issue and pull request templates for better contribution gu… #10
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Test with Comments | ||
| on: | ||
| pull_request: | ||
| branches: [ main, develop ] | ||
| types: [opened, synchronize, reopened] | ||
| permissions: | ||
| contents: read | ||
| pull-requests: 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 test results summary | ||
| python3 << 'EOF' | ||
| import json | ||
| import sys | ||
| import os | ||
| # Parse pytest JSON report | ||
| try: | ||
| with open('reports/pytest-report.json', 'r') as f: | ||
| report = json.load(f) | ||
| 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 = [test for test in tests if test.get('outcome') == 'failed'] | ||
| # 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) | ||
| except FileNotFoundError: | ||
| print("Test report not found, creating default summary") | ||
| 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) | ||
| EOF | ||
| - name: Parse coverage results | ||
| if: always() | ||
| run: | | ||
| # Extract coverage percentage | ||
| if [ -f "coverage.xml" ]; then | ||
| COVERAGE=$(python3 -c " | ||
| import xml.etree.ElementTree as ET | ||
| try: | ||
| tree = ET.parse('coverage.xml') | ||
| root = tree.getroot() | ||
| coverage = root.attrib.get('line-rate', '0') | ||
| print(f'{float(coverage) * 100:.1f}') | ||
| except: | ||
| print('0.0') | ||
| ") | ||
| else | ||
| COVERAGE="0.0" | ||
| fi | ||
| echo "COVERAGE_PERCENT=$COVERAGE" >> $GITHUB_ENV | ||
| - name: Generate detailed comment | ||
| if: always() | ||
| uses: actions/github-script@v6 | ||
| 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) { | ||
| 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) { | ||
| 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 coverageBar = createProgressBar(parseFloat(process.env.COVERAGE_PERCENT || '0')); | ||
| // 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}** | ||
| \`\`\` | ||
| ${test.call?.longrepr || 'No details available'} | ||
| \`\`\` | ||
| `).join('\n')} | ||
| ${testSummary.failed_tests.length >= 5 ? '_Note: Only showing first 5 failures_' : ''} | ||
| `; | ||
| } | ||
| // 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** | ${process.env.COVERAGE_PERCENT || '0.0'}% | ${coverageBar} | | ||
| ### 🔍 Test Details | ||
| <details> | ||
| <summary>📋 Click to view detailed test output</summary> | ||
| \`\`\` | ||
| ${testOutput.slice(-2000)} // Last 2000 chars to avoid comment size limits | ||
| \`\`\` | ||
| </details> | ||
| ${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}\` | ||
| ### 📎 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.' | ||
| } | ||
| <sub>🤖 This comment was automatically generated by the test workflow</sub>`; | ||
| // 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 | ||
| }); | ||
| } else { | ||
| await github.rest.issues.createComment({ | ||
| owner: context.repo.owner, | ||
| repo: context.repo.repo, | ||
| issue_number: context.issue.number, | ||
| body: comment | ||
| }); | ||
| } | ||
| - name: Upload test reports | ||
| if: always() | ||
| uses: actions/upload-artifact@v3 | ||
| 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: | | ||
| if [ -f "test_summary.json" ]; then | ||
| FAILED=$(python3 -c "import json; print(json.load(open('test_summary.json'))['failed'])") | ||
| if [ "$FAILED" -gt 0 ]; then | ||
| echo "Tests failed, marking job as failed" | ||
| exit 1 | ||
| fi | ||
| fi | ||
| echo "All tests passed successfully!" | ||