ci(lfx): add coverage generation and Codecov reporting (#10266)

* chore: update dependency markers and add pytest-cov for coverage reporting

- Refined dependency markers for several packages to improve compatibility with Python 3.12 and specific platforms.
- Added pytest-cov to development dependencies for enhanced test coverage reporting.
- Updated dependencies for pyobjc frameworks to include platform-specific markers for better compatibility.

* chore: enhance lfx_tests command with coverage reporting

- Updated the lfx_tests target in the Makefile to include coverage reporting options for pytest.
- Added coverage metrics output in XML, HTML, and terminal formats to improve test visibility and analysis.

* chore: update codecov configuration for LFX coverage tracking

- Added LFX coverage target and threshold to the codecov.yml file.
- Defined separate coverage flags for frontend, backend, and LFX components.
- Updated ignore patterns to exclude LFX test and component directories from coverage reports.

* chore: add coverage upload steps to Python CI workflow

- Implemented steps to upload coverage reports to Codecov for Python 3.10.
- Added artifact upload for coverage reports, retaining them for 30 days.

* chore: update LFX coverage target in codecov configuration

- Increased the LFX coverage target from 40% to 60% to encourage aspirational improvement.
- Clarified the allowable drop in coverage threshold from 44% to 39% without failing the status check.

* chore: update coverage configuration in pyproject.toml

- Enabled branch coverage in the coverage run configuration.
- Fixed a typo in the main module check to ensure proper execution.
This commit is contained in:
Gabriel Luiz Freitas Almeida
2025-10-14 18:55:36 -03:00
committed by GitHub
parent c6a3bdbbab
commit 2a6461560d
5 changed files with 80 additions and 19 deletions

View File

@ -168,6 +168,27 @@ jobs:
env:
DO_NOT_TRACK: true # disable telemetry reporting
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v5
if: matrix.python-version == '3.10'
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: ./src/lfx/coverage.xml
flags: lfx
name: lfx-coverage
fail_ci_if_error: false
directory: ./src/lfx/
- name: Upload coverage artifacts
uses: actions/upload-artifact@v4
if: matrix.python-version == '3.10'
with:
name: lfx-coverage-report
path: |
src/lfx/coverage.xml
src/lfx/htmlcov/
retention-days: 30
test-cli:
name: Test CLI - Python ${{ matrix.python-version }}
runs-on: ${{ (inputs['runs-on'] && startsWith(format('{0}', inputs['runs-on']), '[') && fromJSON(inputs['runs-on'])) || inputs['runs-on'] || github.event.inputs['runs-on'] || 'ubuntu-latest' }}

View File

@ -171,7 +171,7 @@ lfx_tests: ## run lfx package unit tests
@echo 'Running LFX Package Tests...'
@cd src/lfx && \
uv sync && \
uv run pytest tests/unit -v $(args)
uv run pytest tests/unit -v --cov=src/lfx --cov-report=xml --cov-report=html --cov-report=term-missing $(args)
integration_tests:
uv run pytest src/backend/tests/integration \

View File

@ -36,6 +36,17 @@ coverage:
flags:
- frontend
# LFX coverage: Target for core package (excludes components)
# Components (third-party integrations) tracked separately
# Current core coverage: ~44%, Target: 60% (aspirational improvement)
lfx:
target: 60%
# Threshold: Allowable drop in coverage before failing the check
# 5% = coverage can drop from 44% to 39% without failing status
threshold: 5%
flags:
- lfx
# New code coverage requirements - realistic target for current state
# Encourages testing new features without blocking development
patch:
@ -51,7 +62,7 @@ comment:
require_base: false # Don't require base branch comparison
require_head: true # Require current branch coverage
# Define separate coverage tracking for frontend and backend
# Define separate coverage tracking for frontend, backend, and lfx
flags:
backend:
paths:
@ -61,6 +72,10 @@ flags:
paths:
- src/frontend/
carryforward: true # Preserve coverage data across builds if missing
lfx:
paths:
- src/lfx/
carryforward: true # Preserve coverage data across builds if missing
# Define coverage components for granular reporting
component_management:
@ -86,6 +101,7 @@ ignore:
- "src/backend/tests/**"
- "src/frontend/tests/**"
- "src/frontend/test-results/**"
- "src/lfx/tests/**"
# Build artifacts and dependencies
- "**/__pycache__/**"
- "**/*.pyc"
@ -94,4 +110,6 @@ ignore:
# Python package init files - typically just imports
- "**/__init__.py"
# Database migrations
- "**/migrations/**"
- "**/migrations/**"
# LFX components - third-party integrations tracked separately
- "src/lfx/components/**"

View File

@ -125,5 +125,25 @@ dev = [
"hypothesis>=6.136.3",
"pytest>=8.4.1",
"pytest-asyncio>=0.26.0",
"pytest-cov>=7.0.0",
"ruff>=0.9.10",
]
[tool.coverage.run]
branch = true
omit = [
"*/tests/*",
"*/__init__.py",
"*/components/*", # Third-party integrations tracked separately
]
[tool.coverage.report]
exclude_lines = [
"pragma: no cover",
"def __repr__",
"raise AssertionError",
"raise NotImplementedError",
"if __name__ == '__main__':",
"if TYPE_CHECKING:",
"@abstractmethod",
]

34
uv.lock generated
View File

@ -2824,10 +2824,10 @@ name = "gassist"
version = "0.0.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "colorama", marker = "python_full_version < '3.12' or platform_machine != 'arm64' or sys_platform != 'darwin'" },
{ name = "flask", marker = "python_full_version < '3.12' or platform_machine != 'arm64' or sys_platform != 'darwin'" },
{ name = "flask-cors", marker = "python_full_version < '3.12' or platform_machine != 'arm64' or sys_platform != 'darwin'" },
{ name = "tqdm", marker = "python_full_version < '3.12' or platform_machine != 'arm64' or sys_platform != 'darwin'" },
{ name = "colorama", marker = "(python_full_version >= '3.12' and platform_machine != 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.12' and sys_platform == 'linux') or (platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" },
{ name = "flask", marker = "(python_full_version >= '3.12' and platform_machine != 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.12' and sys_platform == 'linux') or (platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" },
{ name = "flask-cors", marker = "(python_full_version >= '3.12' and platform_machine != 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.12' and sys_platform == 'linux') or (platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" },
{ name = "tqdm", marker = "(python_full_version >= '3.12' and platform_machine != 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.12' and sys_platform == 'linux') or (platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" },
]
wheels = [
{ url = "https://files.pythonhosted.org/packages/b0/2e/f79632d7300874f7f0e60b61a6ab22455a245e1556116a1729542a77b0da/gassist-0.0.1-py3-none-any.whl", hash = "sha256:bb0fac74b453153a6c74b2db40a14fdde7879cbc10ec692ed170e576c8e2b6aa", size = 23819, upload-time = "2025-05-09T18:22:23.609Z" },
@ -5864,6 +5864,7 @@ dev = [
{ name = "hypothesis" },
{ name = "pytest" },
{ name = "pytest-asyncio" },
{ name = "pytest-cov" },
{ name = "ruff" },
]
@ -5910,6 +5911,7 @@ dev = [
{ name = "hypothesis", specifier = ">=6.136.3" },
{ name = "pytest", specifier = ">=8.4.1" },
{ name = "pytest-asyncio", specifier = ">=0.26.0" },
{ name = "pytest-cov", specifier = ">=7.0.0" },
{ name = "ruff", specifier = ">=0.9.10" },
]
@ -7388,9 +7390,9 @@ name = "ocrmac"
version = "1.0.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "click" },
{ name = "pillow" },
{ name = "pyobjc-framework-vision" },
{ name = "click", marker = "python_full_version >= '3.12' or sys_platform == 'darwin'" },
{ name = "pillow", marker = "python_full_version >= '3.12' or sys_platform == 'darwin'" },
{ name = "pyobjc-framework-vision", marker = "python_full_version >= '3.12' or sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/dd/dc/de3e9635774b97d9766f6815bbb3f5ec9bce347115f10d9abbf2733a9316/ocrmac-1.0.0.tar.gz", hash = "sha256:5b299e9030c973d1f60f82db000d6c2e5ff271601878c7db0885e850597d1d2e", size = 1463997, upload-time = "2024-11-07T12:00:00.197Z" }
wheels = [
@ -9826,7 +9828,7 @@ name = "pyobjc-framework-cocoa"
version = "11.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "pyobjc-core" },
{ name = "pyobjc-core", marker = "python_full_version >= '3.12' or sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/4b/c5/7a866d24bc026f79239b74d05e2cf3088b03263da66d53d1b4cf5207f5ae/pyobjc_framework_cocoa-11.1.tar.gz", hash = "sha256:87df76b9b73e7ca699a828ff112564b59251bb9bbe72e610e670a4dc9940d038", size = 5565335, upload-time = "2025-06-14T20:56:59.683Z" }
wheels = [
@ -9842,8 +9844,8 @@ name = "pyobjc-framework-coreml"
version = "11.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "pyobjc-core" },
{ name = "pyobjc-framework-cocoa" },
{ name = "pyobjc-core", marker = "python_full_version >= '3.12' or sys_platform == 'darwin'" },
{ name = "pyobjc-framework-cocoa", marker = "python_full_version >= '3.12' or sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/0d/5d/4309f220981d769b1a2f0dcb2c5c104490d31389a8ebea67e5595ce1cb74/pyobjc_framework_coreml-11.1.tar.gz", hash = "sha256:775923eefb9eac2e389c0821b10564372de8057cea89f1ea1cdaf04996c970a7", size = 82005, upload-time = "2025-06-14T20:57:12.004Z" }
wheels = [
@ -9859,8 +9861,8 @@ name = "pyobjc-framework-quartz"
version = "11.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "pyobjc-core" },
{ name = "pyobjc-framework-cocoa" },
{ name = "pyobjc-core", marker = "python_full_version >= '3.12' or sys_platform == 'darwin'" },
{ name = "pyobjc-framework-cocoa", marker = "python_full_version >= '3.12' or sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/c7/ac/6308fec6c9ffeda9942fef72724f4094c6df4933560f512e63eac37ebd30/pyobjc_framework_quartz-11.1.tar.gz", hash = "sha256:a57f35ccfc22ad48c87c5932818e583777ff7276605fef6afad0ac0741169f75", size = 3953275, upload-time = "2025-06-14T20:58:17.924Z" }
wheels = [
@ -9876,10 +9878,10 @@ name = "pyobjc-framework-vision"
version = "11.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "pyobjc-core" },
{ name = "pyobjc-framework-cocoa" },
{ name = "pyobjc-framework-coreml" },
{ name = "pyobjc-framework-quartz" },
{ name = "pyobjc-core", marker = "python_full_version >= '3.12' or sys_platform == 'darwin'" },
{ name = "pyobjc-framework-cocoa", marker = "python_full_version >= '3.12' or sys_platform == 'darwin'" },
{ name = "pyobjc-framework-coreml", marker = "python_full_version >= '3.12' or sys_platform == 'darwin'" },
{ name = "pyobjc-framework-quartz", marker = "python_full_version >= '3.12' or sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/40/a8/7128da4d0a0103cabe58910a7233e2f98d18c590b1d36d4b3efaaedba6b9/pyobjc_framework_vision-11.1.tar.gz", hash = "sha256:26590512ee7758da3056499062a344b8a351b178be66d4b719327884dde4216b", size = 133721, upload-time = "2025-06-14T20:58:46.095Z" }
wheels = [