diff --git a/.github/scripts/coverage-badge.py b/.github/scripts/coverage-badge.py new file mode 100644 index 0000000..9334e18 --- /dev/null +++ b/.github/scripts/coverage-badge.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python3 +"""Reads a Cobertura report, writes a coverage badge (SVG) and a Markdown summary.""" +import sys +import xml.etree.ElementTree as ET + +report, badge, summary = sys.argv[1:4] +root = ET.parse(report).getroot() +line = float(root.attrib["line-rate"]) * 100 +branch = float(root.attrib["branch-rate"]) * 100 + +color = "#4c1" if line >= 90 else "#dfb317" if line >= 75 else "#e05d44" +label, value = "coverage", f"{line:.0f}%" +lw, vw = 61, 6 * len(value) + 14 +w = lw + vw + +with open(badge, "w") as f: + f.write(f""" + + + + +{label}{label} +{value}{value} + +""") + +rows = "\n".join( + f"| {c.attrib['name']} | {float(c.attrib['line-rate']) * 100:.0f}% |" + for c in sorted(root.iter("class"), key=lambda c: c.attrib["name"]) + if "/" not in c.attrib["name"] and "<" not in c.attrib["name"] +) +with open(summary, "w") as f: + f.write(f"## Code coverage\n\nLines: **{line:.1f}%** ยท Branches: **{branch:.1f}%**\n\n| Class | Lines |\n|---|---|\n{rows}\n") diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index aaa5c26..60ce4d9 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -12,6 +12,8 @@ jobs: matrix: os: [ ubuntu-latest, windows-latest, macos-latest ] runs-on: ${{ matrix.os }} + permissions: + contents: write steps: - uses: actions/checkout@v7 with: @@ -25,4 +27,20 @@ jobs: - name: Build run: dotnet build -c Release --no-restore - name: Test - run: dotnet test -c Release --no-build --verbosity normal + run: dotnet test -c Release --no-build --verbosity normal --collect:"XPlat Code Coverage" --results-directory ./coverage + - name: Coverage summary and badge + if: matrix.os == 'ubuntu-latest' + run: | + python3 .github/scripts/coverage-badge.py "$(ls coverage/*/coverage.cobertura.xml | head -n1)" coverage/coverage.svg "$GITHUB_STEP_SUMMARY" + - name: Publish coverage badge + if: matrix.os == 'ubuntu-latest' && github.event_name == 'push' + env: + GITHUB_TOKEN: ${{ github.token }} + run: | + cd coverage + git init -q -b badges + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git add coverage.svg + git commit -q -m "Update coverage badge" + git push -f "https://x-access-token:${GITHUB_TOKEN}@github.com/${{ github.repository }}.git" badges diff --git a/.gitignore b/.gitignore index 48533d5..1a10596 100644 --- a/.gitignore +++ b/.gitignore @@ -756,4 +756,7 @@ FodyWeavers.xsd ### VisualStudio Patch ### # Additional files built by Visual Studio -# End of https://www.toptal.com/developers/gitignore/api/dotnetcore,linux,windows,macos,jetbrains,rider,visualstudio,visualstudiocode,aspnetcore \ No newline at end of file +# End of https://www.toptal.com/developers/gitignore/api/dotnetcore,linux,windows,macos,jetbrains,rider,visualstudio,visualstudiocode,aspnetcore + +# Local coverage output +/coverage/ diff --git a/README.md b/README.md index 6c970a2..af37237 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ [![.NET](https://github.com/capjan/regex/actions/workflows/dotnet.yml/badge.svg)](https://github.com/capjan/regex/actions/workflows/dotnet.yml) +[![Coverage](https://raw.githubusercontent.com/capjan/regex/badges/coverage.svg)](https://github.com/capjan/regex/actions/workflows/dotnet.yml) # regex