diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml new file mode 100644 index 0000000..2b2eb13 --- /dev/null +++ b/.gitea/workflows/ci.yml @@ -0,0 +1,180 @@ +name: Python CI + +on: + push: + branches: [ main, master, develop ] + paths-ignore: + - 'coverage*.svg' + - 'README.md' + pull_request: + branches: [ main, master, develop ] + +jobs: + test: + runs-on: self-hosted + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.x' + + - name: Install system dependencies + run: | + sudo apt-get update + sudo apt-get install -y libgl1-mesa-glx xvfb libxkbcommon-x11-0 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 libxcb-xinerama0 libxcb-xfixes0 + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + # Install package in development mode with dev dependencies + pip install -e ".[dev]" + # Install additional test packages + pip install coverage-badge interrogate + + - name: Download initial failed badges + run: | + echo "Downloading initial failed badges..." + + # Create cov_info directory first + mkdir -p cov_info + + # Download failed badges as defaults + curl -o cov_info/coverage.svg "https://img.shields.io/badge/coverage-failed-red.svg" + curl -o cov_info/coverage-docs.svg "https://img.shields.io/badge/docs-failed-red.svg" + + echo "Initial failed badges created:" + ls -la cov_info/coverage*.svg + + - name: Run tests with pytest + id: pytest + continue-on-error: true + run: | + # Run tests with coverage using xvfb for Qt + xvfb-run -a python -m pytest tests/ -v --cov=pyPhotoAlbum --cov-report=term-missing --cov-report=json --cov-report=html --cov-report=xml + env: + QT_QPA_PLATFORM: offscreen + + - name: Check documentation coverage + id: docs + continue-on-error: true + run: | + # Generate documentation coverage report + interrogate -v --ignore-init-method --ignore-init-module --ignore-magic --ignore-private --ignore-property-decorators --ignore-semiprivate --fail-under=80 pyPhotoAlbum/ + + - name: Lint with flake8 + run: | + # Stop the build if there are Python syntax errors or undefined names + flake8 pyPhotoAlbum --count --select=E9,F63,F7,F82 --show-source --statistics + # Exit-zero treats all errors as warnings + flake8 pyPhotoAlbum --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + + - name: Create coverage info directory + if: always() + run: | + mkdir -p cov_info + echo "Created cov_info directory for coverage data" + + - name: Update test coverage badge on success + if: steps.pytest.outcome == 'success' && always() + run: | + echo "Tests passed! Generating successful coverage badge..." + + if [ -f coverage.json ]; then + coverage-badge -o cov_info/coverage.svg -f + echo "✅ Test coverage badge updated with actual results" + else + echo "⚠️ No coverage.json found, keeping failed badge" + fi + + - name: Update docs coverage badge on success + if: steps.docs.outcome == 'success' && always() + run: | + echo "Docs check passed! Generating successful docs badge..." + + # Remove existing badge first to avoid overwrite error + rm -f cov_info/coverage-docs.svg + interrogate --generate-badge cov_info/coverage-docs.svg pyPhotoAlbum/ + echo "✅ Docs coverage badge updated with actual results" + + - name: Generate coverage reports + if: steps.pytest.outcome == 'success' + run: | + # Generate coverage summary for README + python -c " + import json + import os + # Read coverage data + if os.path.exists('coverage.json'): + with open('coverage.json', 'r') as f: + coverage_data = json.load(f) + total_coverage = round(coverage_data['totals']['percent_covered'], 1) + # Create coverage summary file in cov_info directory + with open('cov_info/coverage-summary.txt', 'w') as f: + f.write(f'{total_coverage}%') + print(f'Test Coverage: {total_coverage}%') + covered_lines = coverage_data['totals']['covered_lines'] + total_lines = coverage_data['totals']['num_statements'] + print(f'Lines Covered: {covered_lines}/{total_lines}') + else: + print('No coverage data found') + " + + # Copy other coverage files to cov_info + if [ -f coverage.json ]; then cp coverage.json cov_info/; fi + if [ -f coverage.xml ]; then cp coverage.xml cov_info/; fi + if [ -d htmlcov ]; then cp -r htmlcov cov_info/; fi + + - name: Final badge status + if: always() + run: | + echo "=== FINAL BADGE STATUS ===" + echo "Test outcome: ${{ steps.pytest.outcome }}" + echo "Docs outcome: ${{ steps.docs.outcome }}" + + if [ -f cov_info/coverage.svg ]; then + echo "✅ Test coverage badge: $(ls -lh cov_info/coverage.svg)" + else + echo "❌ Test coverage badge: MISSING" + fi + + if [ -f cov_info/coverage-docs.svg ]; then + echo "✅ Docs coverage badge: $(ls -lh cov_info/coverage-docs.svg)" + else + echo "❌ Docs coverage badge: MISSING" + fi + + echo "Coverage info directory contents:" + ls -la cov_info/ 2>/dev/null || echo "No cov_info directory found" + + - name: Upload coverage artifacts + uses: actions/upload-artifact@v3 + with: + name: coverage-reports + path: | + cov_info/ + + - name: Commit badges to badges branch + if: github.ref == 'refs/heads/master' + run: | + git config --local user.email "action@gitea.local" + git config --local user.name "Gitea Action" + + # Set the remote URL to use the token + git remote set-url origin https://${{ secrets.PUSH_TOKEN }}@gitea.tourolle.paris/dtourolle/pyPhotoAlbum.git + + # Create a new orphan branch for badges (this discards any existing badges branch) + git checkout --orphan badges + + # Remove all files except cov_info + find . -maxdepth 1 -not -name '.git' -not -name 'cov_info' -exec rm -rf {} + 2>/dev/null || true + + # Add only the coverage info directory + git add -f cov_info/ + + # Always commit (force overwrite) + echo "Force updating badges branch with new coverage data..." + git commit -m "Update coverage badges [skip ci]" + git push -f origin badges diff --git a/.gitignore b/.gitignore index 358801c..d14130c 100644 --- a/.gitignore +++ b/.gitignore @@ -44,10 +44,13 @@ htmlcov/ .cache nosetests.xml coverage.xml +coverage.json *.cover *.py,cover .hypothesis/ .pytest_cache/ +cov_info/ +coverage*.svg # Translations *.mo diff --git a/BADGES.md b/BADGES.md new file mode 100644 index 0000000..ec8fc77 --- /dev/null +++ b/BADGES.md @@ -0,0 +1,70 @@ +# Coverage Badges Integration + +This document explains how to integrate the coverage badges generated by the CI workflow into your README. + +## How It Works + +The Python CI workflow automatically: +1. Runs tests with coverage reporting +2. Checks documentation coverage with interrogate +3. Generates coverage badges +4. Commits badges to a separate `badges` branch + +## Using the Badges in README + +Once the workflow has run successfully on the `master` branch, you can add the following badges to your README.md: + +### Test Coverage Badge + +```markdown +![Coverage Badge](https://gitea.tourolle.paris/dtourolle/pyPhotoAlbum/raw/branch/badges/cov_info/coverage.svg) +``` + +### Documentation Coverage Badge + +```markdown +![Docs Badge](https://gitea.tourolle.paris/dtourolle/pyPhotoAlbum/raw/branch/badges/cov_info/coverage-docs.svg) +``` + +## Example README Section + +```markdown +# pyPhotoAlbum + +![Coverage Badge](https://gitea.tourolle.paris/dtourolle/pyPhotoAlbum/raw/branch/badges/cov_info/coverage.svg) +![Docs Badge](https://gitea.tourolle.paris/dtourolle/pyPhotoAlbum/raw/branch/badges/cov_info/coverage-docs.svg) + +A Python application for designing photo albums and exporting them to PDF. +``` + +## Workflow Details + +- **Workflow File**: `.gitea/workflows/ci.yml` +- **Trigger**: Pushes to `main`, `master`, or `develop` branches +- **Runner**: Self-hosted +- **Badge Branch**: `badges` (automatically created/updated) +- **Badge Location**: `cov_info/` directory in badges branch + +## Requirements + +The workflow requires a `PUSH_TOKEN` secret to be configured in your Gitea repository settings. This token allows the workflow to push to the badges branch. + +### Setting Up the PUSH_TOKEN + +1. Go to your Gitea profile settings +2. Navigate to Applications → Generate New Token +3. Give it a descriptive name (e.g., "CI Badges Token") +4. Select the `repository` scope +5. Generate the token +6. Go to your repository → Settings → Secrets +7. Add a new secret named `PUSH_TOKEN` with the token value + +## Coverage Reports + +In addition to badges, the workflow also generates: +- `coverage.json` - Machine-readable coverage data +- `coverage.xml` - XML format coverage report +- `htmlcov/` - HTML coverage report +- `coverage-summary.txt` - Simple text summary of coverage percentage + +All these files are available as artifacts after each workflow run and are stored in the `badges` branch under `cov_info/`.