Bump astral-sh/setup-uv from 5 to 6 #25
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: CI | |
| on: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| branches: [main] | |
| # Only run this workflow when ENABLE_BADGES is set to 'true' in repository variables | |
| jobs: | |
| check-if-enabled: | |
| runs-on: ubuntu-latest | |
| outputs: | |
| enabled: ${{ steps.check.outputs.enabled }} | |
| steps: | |
| - id: check | |
| run: | | |
| if [[ "${ENABLE_BADGES:-${{ vars.ENABLE_BADGES }}}" == "true" ]]; then | |
| echo "enabled=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "enabled=false" >> "$GITHUB_OUTPUT" | |
| echo "::notice::Badges workflow is disabled. Set ENABLE_BADGES repository variable to 'true' to enable it." | |
| fi | |
| # Run checks on pull requests without generating badges | |
| checks-only: | |
| if: github.event_name == 'pull_request' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Install uv | |
| uses: astral-sh/setup-uv@v6 | |
| with: | |
| enable-cache: true | |
| cache-dependency-glob: "pyproject.toml" | |
| - name: Set up Python | |
| id: setup-python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version-file: "pyproject.toml" | |
| - name: Install dependencies | |
| run: uv sync --all-extras --dev | |
| # Cache pre-commit hooks | |
| - name: Cache pre-commit hooks | |
| uses: actions/cache@v4 | |
| with: | |
| path: ~/.cache/pre-commit | |
| key: ${{ runner.os }}-precommit-${{ hashFiles('.pre-commit-config.yaml') }} | |
| restore-keys: | | |
| ${{ runner.os }}-precommit- | |
| # Run pre-commit hooks | |
| - name: Run pre-commit hooks | |
| id: pre-commit | |
| run: uv run pre-commit run --all-files | |
| # Run checks and generate badges only on push to main | |
| checks-and-badges: | |
| needs: check-if-enabled | |
| if: needs.check-if-enabled.outputs.enabled == 'true' && github.event_name == 'push' && github.ref == 'refs/heads/main' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Install uv | |
| uses: astral-sh/setup-uv@v6 | |
| with: | |
| enable-cache: true | |
| cache-dependency-glob: "pyproject.toml" | |
| - name: Set up Python | |
| id: setup-python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version-file: "pyproject.toml" | |
| - name: Check Python dependencies | |
| run: uv lock --check | |
| - name: Install dependencies | |
| run: uv sync --all-extras --dev | |
| # Cache pre-commit hooks | |
| - name: Cache pre-commit hooks | |
| uses: actions/cache@v4 | |
| with: | |
| path: ~/.cache/pre-commit | |
| key: ${{ runner.os }}-precommit-${{ hashFiles('.pre-commit-config.yaml') }} | |
| restore-keys: | | |
| ${{ runner.os }}-precommit- | |
| # Run pre-commit hooks and capture status | |
| - name: Run pre-commit hooks | |
| id: pre-commit | |
| run: uv run pre-commit run --all-files | |
| - name: Set pre-commit status | |
| run: | | |
| if [ "${{ steps.pre-commit.outcome }}" == 'success' ]; then | |
| { | |
| echo "PRE_COMMIT_STATUS=passing" | |
| echo "PRE_COMMIT_COLOR=green" | |
| } >> "$GITHUB_ENV" | |
| else | |
| # Count failed hooks | |
| FAILED_HOOKS=$(echo "${{ steps.pre-commit.outputs.stdout }}" | grep -c "Failed" || echo "0") | |
| if [ "$FAILED_HOOKS" -gt 0 ]; then | |
| { | |
| echo "PRE_COMMIT_STATUS=failing ($FAILED_HOOKS issues)" | |
| echo "PRE_COMMIT_COLOR=red" | |
| } >> "$GITHUB_ENV" | |
| else | |
| { | |
| echo "PRE_COMMIT_STATUS=failing" | |
| echo "PRE_COMMIT_COLOR=red" | |
| } >> "$GITHUB_ENV" | |
| fi | |
| fi | |
| # Extract individual check statuses for badges | |
| - name: Run Ruff check | |
| id: ruff | |
| run: uv run poe ruff-check | |
| - name: Run Ruff format | |
| id: ruff-format | |
| run: uv run poe ruff-format | |
| - name: Set Ruff status | |
| run: | | |
| if [ "${{ steps.ruff.outcome }}" == 'success' ]; then | |
| { | |
| echo "RUFF_STATUS=passing (0 issues)" | |
| echo "RUFF_COLOR=green" | |
| } >> "$GITHUB_ENV" | |
| else | |
| # Try to extract the number of issues | |
| ISSUES=$(echo "${{ steps.ruff.outputs.stderr }}" | grep -o "Found [0-9]\\+ error" | grep -o "[0-9]\\+" || echo "") | |
| if [ -n "$ISSUES" ]; then | |
| { | |
| echo "RUFF_STATUS=failing ($ISSUES issues)" | |
| echo "RUFF_COLOR=red" | |
| } >> "$GITHUB_ENV" | |
| else | |
| { | |
| echo "RUFF_STATUS=failing" | |
| echo "RUFF_COLOR=red" | |
| } >> "$GITHUB_ENV" | |
| fi | |
| fi | |
| - name: Run Pyright | |
| id: pyright | |
| run: uv run poe pyright | |
| - name: Set Pyright status | |
| run: | | |
| if [ "${{ steps.pyright.outcome }}" == 'success' ]; then | |
| { | |
| echo "PYRIGHT_STATUS=passing (0 errors)" | |
| echo "PYRIGHT_COLOR=green" | |
| } >> "$GITHUB_ENV" | |
| else | |
| # Try to extract the number of errors | |
| ERRORS=$(echo "${{ steps.pyright.outputs.stderr }}" | grep -o "[0-9]\\+ error" | grep -o "[0-9]\\+" || echo "") | |
| if [ -n "$ERRORS" ]; then | |
| { | |
| echo "PYRIGHT_STATUS=failing ($ERRORS errors)" | |
| echo "PYRIGHT_COLOR=red" | |
| } >> "$GITHUB_ENV" | |
| else | |
| { | |
| echo "PYRIGHT_STATUS=failing" | |
| echo "PYRIGHT_COLOR=red" | |
| } >> "$GITHUB_ENV" | |
| fi | |
| fi | |
| - name: Run Vulture | |
| id: vulture | |
| run: uv run poe vulture | |
| - name: Set Vulture status | |
| run: | | |
| VULTURE_OUTPUT="${{ steps.vulture.outputs.stdout }}" | |
| if [[ "${{ steps.vulture.outcome }}" == 'success' ]]; then | |
| { | |
| echo "VULTURE_STATUS=0% dead code" | |
| echo "VULTURE_COLOR=green" | |
| } >> "$GITHUB_ENV" | |
| else | |
| # Try to extract percentage if available | |
| UNUSED_CODE=$(echo "$VULTURE_OUTPUT" | grep -o '[0-9]\+% confidence' | head -1 | grep -o '[0-9]\+' || echo "") | |
| if [ -n "$UNUSED_CODE" ]; then | |
| { | |
| echo "VULTURE_STATUS=$UNUSED_CODE% dead code" | |
| if [ "$UNUSED_CODE" -gt 10 ]; then | |
| echo "VULTURE_COLOR=red" | |
| elif [ "$UNUSED_CODE" -gt 5 ]; then | |
| echo "VULTURE_COLOR=yellow" | |
| else | |
| echo "VULTURE_COLOR=green" | |
| fi | |
| } >> "$GITHUB_ENV" | |
| else | |
| { | |
| echo "VULTURE_STATUS=failing" | |
| echo "VULTURE_COLOR=red" | |
| } >> "$GITHUB_ENV" | |
| fi | |
| fi | |
| # Run tests with coverage | |
| - name: Run tests with coverage | |
| id: coverage | |
| run: uv run poe test-coverage | |
| - name: Extract coverage data | |
| run: | | |
| if [ -f coverage.xml ]; then | |
| COVERAGE=$(python -c "import xml.etree.ElementTree as ET; print(ET.parse('coverage.xml').getroot().get('line-rate'))") | |
| COVERAGE_PCT=$(python -c "print(f'{float(\"$COVERAGE\")*100:.2f}%')") | |
| # Convert to numeric for comparison - fix the extraction to get the correct number | |
| COVERAGE_NUM=$(python -c "print(int(float('$COVERAGE')*100))") | |
| # Debug output | |
| echo "Debug: COVERAGE=$COVERAGE" | |
| echo "Debug: COVERAGE_PCT=$COVERAGE_PCT" | |
| echo "Debug: COVERAGE_NUM=$COVERAGE_NUM" | |
| { | |
| echo "COVERAGE=$COVERAGE_PCT" | |
| if [ "$COVERAGE_NUM" -lt 50 ]; then | |
| echo "COVERAGE_COLOR=red" | |
| echo "COVERAGE_DESCRIPTION=insufficient" | |
| elif [ "$COVERAGE_NUM" -lt 65 ]; then | |
| echo "COVERAGE_COLOR=yellow" | |
| echo "COVERAGE_DESCRIPTION=partial" | |
| elif [ "$COVERAGE_NUM" -lt 85 ]; then | |
| echo "COVERAGE_COLOR=green" | |
| echo "COVERAGE_DESCRIPTION=good" | |
| else | |
| echo "COVERAGE_COLOR=brightgreen" | |
| echo "COVERAGE_DESCRIPTION=excellent" | |
| fi | |
| } >> "$GITHUB_ENV" | |
| else | |
| { | |
| echo "COVERAGE=unknown" | |
| echo "COVERAGE_COLOR=red" | |
| echo "COVERAGE_DESCRIPTION=missing" | |
| } >> "$GITHUB_ENV" | |
| fi | |
| # Run bandit security check | |
| - name: Run Bandit | |
| id: bandit | |
| run: uv run poe bandit | |
| - name: Set Bandit status | |
| run: | | |
| if [ "${{ steps.bandit.outcome }}" == 'success' ]; then | |
| { | |
| echo "BANDIT_STATUS=secure (0 issues)" | |
| echo "BANDIT_COLOR=green" | |
| } >> "$GITHUB_ENV" | |
| else | |
| # Try to extract the number of issues | |
| ISSUES=$(echo "${{ steps.bandit.outputs.stdout }}" | grep -o "Issue: [0-9]\\+" | wc -l || echo "") | |
| if [ -n "$ISSUES" ] && [ "$ISSUES" -gt 0 ]; then | |
| { | |
| echo "BANDIT_STATUS=issues found ($ISSUES)" | |
| echo "BANDIT_COLOR=red" | |
| } >> "$GITHUB_ENV" | |
| else | |
| { | |
| echo "BANDIT_STATUS=issues found" | |
| echo "BANDIT_COLOR=red" | |
| } >> "$GITHUB_ENV" | |
| fi | |
| fi | |
| # Run interrogate for docstring coverage | |
| - name: Run Interrogate | |
| id: interrogate | |
| run: uv run poe interrogate | |
| - name: Set Interrogate status | |
| run: | | |
| if [ "${{ steps.interrogate.outcome }}" == 'success' ]; then | |
| # Try to extract the coverage percentage | |
| DOCS_COVERAGE=$(echo "${{ steps.interrogate.outputs.stdout }}" | grep -o '[0-9]\+\.[0-9]\+%' | head -1 || echo "") | |
| if [ -n "$DOCS_COVERAGE" ]; then | |
| # Try to extract the number of missing docstrings | |
| MISSING=$(echo "${{ steps.interrogate.outputs.stdout }}" | grep -o 'missing [0-9]\+ docstring' | grep -o '[0-9]\+' || echo "") | |
| # Extract numeric value for comparison | |
| DOCS_NUM=$(echo "$DOCS_COVERAGE" | sed 's/%//' | sed 's/\..*//') | |
| { | |
| if [ -n "$MISSING" ]; then | |
| echo "INTERROGATE_STATUS=$DOCS_COVERAGE ($MISSING missing)" | |
| else | |
| echo "INTERROGATE_STATUS=$DOCS_COVERAGE" | |
| fi | |
| if [ "$DOCS_NUM" -lt 60 ]; then | |
| echo "INTERROGATE_COLOR=red" | |
| elif [ "$DOCS_NUM" -lt 80 ]; then | |
| echo "INTERROGATE_COLOR=yellow" | |
| else | |
| echo "INTERROGATE_COLOR=green" | |
| fi | |
| } >> "$GITHUB_ENV" | |
| else | |
| { | |
| echo "INTERROGATE_STATUS=passing (100%)" | |
| echo "INTERROGATE_COLOR=green" | |
| } >> "$GITHUB_ENV" | |
| fi | |
| else | |
| { | |
| echo "INTERROGATE_STATUS=failing" | |
| echo "INTERROGATE_COLOR=red" | |
| } >> "$GITHUB_ENV" | |
| fi | |
| # Run radon for code complexity | |
| - name: Run Radon | |
| id: radon | |
| run: uv run poe radon | |
| - name: Set Radon status | |
| run: | | |
| if [ "${{ steps.radon.outcome }}" == 'success' ]; then | |
| # Try to extract the average complexity | |
| COMPLEXITY=$(echo "${{ steps.radon.outputs.stdout }}" | grep -o 'Average complexity: [A-F]' | cut -d' ' -f3 || echo "") | |
| if [ -n "$COMPLEXITY" ]; then | |
| # Try to extract the actual complexity value | |
| COMPLEXITY_VALUE=$(echo "${{ steps.radon.outputs.stdout }}" | grep -o 'Average complexity: [0-9.]\+' | cut -d' ' -f3 || echo "") | |
| { | |
| if [ -n "$COMPLEXITY_VALUE" ]; then | |
| echo "RADON_STATUS=Grade $COMPLEXITY ($COMPLEXITY_VALUE)" | |
| else | |
| echo "RADON_STATUS=Grade $COMPLEXITY" | |
| fi | |
| if [ "$COMPLEXITY" = "A" ] || [ "$COMPLEXITY" = "B" ]; then | |
| echo "RADON_COLOR=green" | |
| elif [ "$COMPLEXITY" = "C" ]; then | |
| echo "RADON_COLOR=yellow" | |
| else | |
| echo "RADON_COLOR=red" | |
| fi | |
| } >> "$GITHUB_ENV" | |
| else | |
| { | |
| echo "RADON_STATUS=passing" | |
| echo "RADON_COLOR=green" | |
| } >> "$GITHUB_ENV" | |
| fi | |
| else | |
| { | |
| echo "RADON_STATUS=failing" | |
| echo "RADON_COLOR=red" | |
| } >> "$GITHUB_ENV" | |
| fi | |
| # Run xenon for code maintainability | |
| - name: Run Xenon | |
| id: xenon | |
| run: uv run poe xenon | |
| - name: Set Xenon status | |
| run: | | |
| if [ "${{ steps.xenon.outcome }}" == 'success' ]; then | |
| # Try to extract metrics if available | |
| RANK=$(echo "${{ steps.xenon.outputs.stdout }}" | grep -o 'Rank: [A-F]' | cut -d' ' -f2 || echo "") | |
| if [ -n "$RANK" ]; then | |
| { | |
| echo "XENON_STATUS=Grade $RANK" | |
| if [ "$RANK" = "A" ] || [ "$RANK" = "B" ]; then | |
| echo "XENON_COLOR=green" | |
| elif [ "$RANK" = "C" ]; then | |
| echo "XENON_COLOR=yellow" | |
| else | |
| echo "XENON_COLOR=red" | |
| fi | |
| } >> "$GITHUB_ENV" | |
| else | |
| { | |
| echo "XENON_STATUS=maintainable" | |
| echo "XENON_COLOR=green" | |
| } >> "$GITHUB_ENV" | |
| fi | |
| else | |
| { | |
| echo "XENON_STATUS=needs refactoring" | |
| echo "XENON_COLOR=red" | |
| } >> "$GITHUB_ENV" | |
| fi | |
| # Create a single JSON file with all badge data | |
| - name: Prepare badge data | |
| run: | | |
| cat > badges.json << EOF | |
| { | |
| "schemaVersion": 1, | |
| "badges": [ | |
| { | |
| "label": "pre-commit", | |
| "message": "${{ env.PRE_COMMIT_STATUS }}", | |
| "color": "${{ env.PRE_COMMIT_COLOR }}", | |
| "namedLogo": "github", | |
| "description": "Pre-commit hooks status" | |
| }, | |
| { | |
| "label": "coverage", | |
| "message": "${{ env.COVERAGE }} (${{ env.COVERAGE_DESCRIPTION }})", | |
| "color": "${{ env.COVERAGE_COLOR }}", | |
| "namedLogo": "pytest", | |
| "description": "Test coverage percentage" | |
| }, | |
| { | |
| "label": "ruff", | |
| "message": "${{ env.RUFF_STATUS }}", | |
| "color": "${{ env.RUFF_COLOR }}", | |
| "namedLogo": "ruff", | |
| "description": "Ruff linting status" | |
| }, | |
| { | |
| "label": "typing", | |
| "message": "${{ env.PYRIGHT_STATUS }}", | |
| "color": "${{ env.PYRIGHT_COLOR }}", | |
| "namedLogo": "python", | |
| "description": "Pyright type checking status" | |
| }, | |
| { | |
| "label": "dead code", | |
| "message": "${{ env.VULTURE_STATUS }}", | |
| "color": "${{ env.VULTURE_COLOR }}", | |
| "namedLogo": "python", | |
| "description": "Vulture dead code detection" | |
| }, | |
| { | |
| "label": "security", | |
| "message": "${{ env.BANDIT_STATUS }}", | |
| "color": "${{ env.BANDIT_COLOR }}", | |
| "namedLogo": "shieldsdotio", | |
| "description": "Bandit security check status" | |
| }, | |
| { | |
| "label": "docs", | |
| "message": "${{ env.INTERROGATE_STATUS }}", | |
| "color": "${{ env.INTERROGATE_COLOR }}", | |
| "namedLogo": "readthedocs", | |
| "description": "Interrogate docstring coverage" | |
| }, | |
| { | |
| "label": "complexity", | |
| "message": "${{ env.RADON_STATUS }}", | |
| "color": "${{ env.RADON_COLOR }}", | |
| "namedLogo": "codacy", | |
| "description": "Radon code complexity rating" | |
| }, | |
| { | |
| "label": "maintainability", | |
| "message": "${{ env.XENON_STATUS }}", | |
| "color": "${{ env.XENON_COLOR }}", | |
| "namedLogo": "codeclimate", | |
| "description": "Xenon code maintainability status" | |
| } | |
| ] | |
| } | |
| EOF | |
| # Update the single Gist file with all badge data | |
| - name: Update Badges Gist | |
| uses: exuanbo/actions-deploy-gist@v1 | |
| with: | |
| token: ${{ secrets.BADGES_TOKEN }} | |
| gist_id: ${{ secrets.BADGES_GIST_ID }} | |
| file_path: badges.json | |
| file_type: text | |
| # Fail the workflow if pre-commit failed | |
| - name: Check for failures | |
| if: steps.pre-commit.outcome != 'success' | |
| run: exit 1 |