Skip to content

Commit 0b3e060

Browse files
Copilotmeta-codesync[bot]
authored andcommitted
Add test coverage workflow and dynamic percentage badge (#268)
Summary: ## Implementation Complete: Add Test Coverage Badge to README ✅ All tasks have been completed successfully: - [x] Add `coverage` and `pytest-cov` to dev dependencies in `pyproject.toml` - [x] Add `coverage` and `pytest-cov` to dev dependencies in `setup.py` - [x] Create `.github/workflows/coverage.yml` workflow - [x] Add dynamic coverage percentage badge to README.md - [x] Configure coverage settings in `pyproject.toml` - [x] Update `.gitignore` to exclude coverage artifacts - [x] Apply PR review feedback: - Fixed Protocol regex pattern to include opening parenthesis: `class .*\\b\\(Protocol\\):` - Changed coverage extraction to use `coverage report --precision=2` instead of parsing XML - Added `if: always()` to artifact upload steps to ensure coverage reports are uploaded even if tests fail - [x] Fix coverage reporting issue: - Use `python -m pytest` instead of `pytest` for better robustness - Changed to `--cov-report=term-missing` to show missing coverage lines - Display full coverage report table instead of trying to extract just percentage - [x] Add CHANGELOG.md entry documenting the new coverage workflow and badge - [x] Implement dynamic coverage percentage badge: - Extract coverage percentage from test results - Create shields.io endpoint badge JSON file (`.github/badges/coverage.json`) - Commit badge data to repository on main branch - Badge displays actual percentage (e.g., "75.5%") with color coding (green ≥80%, yellow ≥60%, orange <60%) ## Coverage Badge Implementation The coverage badge now displays the actual test coverage percentage dynamically rather than just pass/fail status. The workflow: 1. Runs tests with coverage collection using `pytest-cov` 2. Extracts the total coverage percentage from the coverage report 3. Generates a shields.io-compatible JSON badge file with the percentage and color 4. Commits the badge JSON to `.github/badges/coverage.json` (only on main branch) 5. The README badge uses shields.io's endpoint feature to read from this JSON file The badge color automatically adjusts based on coverage level: - **Green (brightgreen)**: ≥80% coverage - **Yellow**: ≥60% coverage - **Orange**: <60% coverage This solution uses shields.io for rendering but stores coverage data in the GitHub repository itself, maintaining the "no external service dependency" requirement for data storage. <details> <summary>Original prompt</summary> > > ---- > > *This section details on the original issue you should resolve* > > <issue_title>[FEATURE] Add 'test coverage' badge to README.md</issue_title> > <issue_description>implement coverage using github actions. > > 1. GitHub Actions Setup: > Implement Python coverage collection using coverage.py > Generate HTML reports and coverage badges locally > 2. Coverage Implementation: > # .github/workflows/coverage.yml > - name: Generate Coverage Report > run: | > coverage run -m pytest > coverage html > coverage xml > - name: Upload Coverage to Artifacts > uses: actions/upload-artifact@v3 > with: > name: coverage-report > path: htmlcov/ > 3. Badge Generation: > Use [shields.io](https://shields.io/) with coverage percentage from CI > Store coverage data in repository or use GitHub's coverage API > No external service dependency required</issue_description> > > <agent_instructions>If you can't do https://shields.io/ badge - write the PR of the github actions and I'll do that part manually</agent_instructions> > > ## Comments on the Issue (you are copilot in this section) > > <comments> > <comment_new><author>talgalili</author><body> > FYI: > neuralsorcerer (this is simple, and would be nice given the work done recently on code coverage) > </body></comment_new> > <comment_new><author>talgalili</author><body> > neuralsorcerer I moved this to me.</body></comment_new> > </comments> > </details> - Fixes #221 --- ✨ Let Copilot coding agent [set things up for you](https://github.com/facebookresearch/balance/issues/new?title=✨+Set+up+Copilot+instructions&body=Configure%20instructions%20for%20this%20repository%20as%20documented%20in%20%5BBest%20practices%20for%20Copilot%20coding%20agent%20in%20your%20repository%5D%28https://gh.io/copilot-coding-agent-tips%29%2E%0A%0A%3COnboard%20this%20repo%3E&assignees=copilot) — coding agent works faster and does higher quality work when set up for your repo. Pull Request resolved: #268 Differential Revision: D90883666 Pulled By: talgalili fbshipit-source-id: 75ed93313f6ee188ca69b3663e9eb0c56e04f258
1 parent c7fcb79 commit 0b3e060

File tree

7 files changed

+120
-0
lines changed

7 files changed

+120
-0
lines changed

.github/badges/coverage.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"schemaVersion": 1, "label": "coverage", "message": "unknown", "color": "lightgrey"}

.github/workflows/coverage.yml

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
name: Coverage
2+
3+
on:
4+
push:
5+
branches: [ main ]
6+
pull_request:
7+
branches: [ main ]
8+
workflow_dispatch:
9+
10+
jobs:
11+
coverage:
12+
runs-on: ubuntu-latest
13+
steps:
14+
- name: Checkout
15+
uses: actions/checkout@v5
16+
17+
- name: Set up Python
18+
uses: actions/setup-python@v5
19+
with:
20+
python-version: '3.12'
21+
22+
- name: Install dependencies
23+
run: |
24+
python -m pip install --upgrade pip
25+
pip install .[dev]
26+
27+
- name: Run tests with coverage
28+
run: |
29+
python -m pytest --cov=balance --cov-report=xml --cov-report=html --cov-report=term-missing
30+
31+
- name: Upload coverage reports to artifacts
32+
if: always()
33+
uses: actions/upload-artifact@v4
34+
with:
35+
name: coverage-report
36+
path: htmlcov/
37+
38+
- name: Upload coverage XML
39+
if: always()
40+
uses: actions/upload-artifact@v4
41+
with:
42+
name: coverage-xml
43+
path: coverage.xml
44+
45+
- name: Display coverage percentage
46+
run: |
47+
coverage report --precision=2
48+
49+
- name: Extract coverage percentage
50+
if: github.ref == 'refs/heads/main'
51+
run: |
52+
COVERAGE=$(coverage report --precision=2 | grep TOTAL | awk '{print $NF}' | sed 's/%//')
53+
echo "COVERAGE=$COVERAGE" >> $GITHUB_ENV
54+
echo "Coverage: $COVERAGE%"
55+
56+
- name: Create coverage badge
57+
if: github.ref == 'refs/heads/main'
58+
run: |
59+
mkdir -p .github/badges
60+
python -c "
61+
import os
62+
coverage = float(os.environ['COVERAGE'])
63+
color = 'brightgreen' if coverage >= 80 else 'yellow' if coverage >= 60 else 'orange'
64+
badge = {
65+
'schemaVersion': 1,
66+
'label': 'coverage',
67+
'message': f'{coverage}%',
68+
'color': color
69+
}
70+
import json
71+
with open('.github/badges/coverage.json', 'w') as f:
72+
json.dump(badge, f)
73+
print(f'Created badge with {coverage}% coverage in {color}')
74+
"
75+
76+
- name: Commit coverage badge
77+
if: github.ref == 'refs/heads/main'
78+
run: |
79+
git config --local user.email "github-actions[bot]@users.noreply.github.com"
80+
git config --local user.name "github-actions[bot]"
81+
git add .github/badges/coverage.json
82+
git diff --staged --quiet || git commit -m "Update coverage badge [skip ci]"
83+
git push

.gitignore

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,15 @@ __pycache__/
44
# Auto-generated version file
55
version.py
66

7+
# Coverage reports
8+
.coverage
9+
.coverage.*
10+
htmlcov/
11+
coverage.xml
12+
*.cover
13+
.hypothesis/
14+
.pytest_cache/
15+
716
# OSX
817
*.DS_Store
918

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@
7777

7878
## Tests
7979
- **Added Pyre type checking to GitHub Actions** via `.pyre_configuration.external` and a new `pyre` job in the workflow. Tests are excluded due to external typeshed stub differences; library code is fully type-checked.
80+
- **Added test coverage workflow and badge to README** via `.github/workflows/coverage.yml`. The workflow collects coverage using pytest-cov, generates HTML and XML reports, uploads them as artifacts, and displays coverage metrics. A coverage badge is now shown in README.md alongside other workflow badges.
8081

8182
# 0.14.0 (2025-12-14)
8283

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
[![Current Release](https://img.shields.io/github/release/facebookresearch/balance.svg)](https://github.com/facebookresearch/balance/releases)
88
[![Python 3.9+](https://img.shields.io/badge/Python-3.9+-fcbc2c.svg?logo=python&logoColor=white)](https://www.python.org/downloads/)
99
[![Build & Test](https://github.com/facebookresearch/balance/actions/workflows/build-and-test.yml/badge.svg)](https://github.com/facebookresearch/balance/actions/workflows/build-and-test.yml?query=branch%3Amain)
10+
[![Coverage](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/facebookresearch/balance/main/.github/badges/coverage.json)](https://github.com/facebookresearch/balance/actions/workflows/coverage.yml?query=branch%3Amain)
1011
[![CodeQL](https://github.com/facebookresearch/balance/actions/workflows/codeql.yml/badge.svg)](https://github.com/facebookresearch/balance/actions/workflows/codeql.yml?query=branch%3Amain)
1112
[![Deploy Website](https://github.com/facebookresearch/balance/actions/workflows/deploy-website.yml/badge.svg)](https://github.com/facebookresearch/balance/actions/workflows/deploy-website.yml?query=branch%3Amain)
1213
[![Release](https://github.com/facebookresearch/balance/actions/workflows/release.yml/badge.svg)](https://github.com/facebookresearch/balance/actions/workflows/release.yml?query=branch%3Amain)

pyproject.toml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ dev = [
5757
"setuptools_scm",
5858
"wheel",
5959
"pytest",
60+
"pytest-cov",
61+
"coverage",
6062
"sphinx",
6163
"notebook",
6264
"nbconvert",
@@ -101,3 +103,24 @@ filterwarnings = [
101103
"ignore:'resetCache' deprecated - use 'reset_cache':DeprecationWarning:matplotlib._fontconfig_pattern",
102104
"ignore:'enablePackrat' deprecated - use 'enable_packrat':DeprecationWarning:matplotlib._mathtext",
103105
]
106+
107+
[tool.coverage.run]
108+
source = ["balance"]
109+
omit = [
110+
"*/tests/*",
111+
"*/test_*.py",
112+
"setup.py",
113+
"version.py",
114+
]
115+
116+
[tool.coverage.report]
117+
exclude_lines = [
118+
"pragma: no cover",
119+
"def __repr__",
120+
"raise AssertionError",
121+
"raise NotImplementedError",
122+
"if __name__ == \"__main__\":",
123+
"if TYPE_CHECKING:",
124+
"class [\\w]+\\(Protocol\\):",
125+
"@(abc\\.)?abstractmethod",
126+
]

setup.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@
3434
"setuptools_scm",
3535
"wheel",
3636
"pytest",
37+
"pytest-cov",
38+
"coverage",
3739
"sphinx",
3840
"notebook",
3941
"nbconvert",

0 commit comments

Comments
 (0)