فهرست منبع

chore: replace coverage job (#2404)

* chore: replace coverage job

* feat: check coverage is above threshold
João André 3 ماه پیش
والد
کامیت
4a6315fada
4فایلهای تغییر یافته به همراه128 افزوده شده و 6 حذف شده
  1. 8 0
      .coveragerc
  2. 18 6
      .github/workflows/overall-tests.yml
  3. 1 0
      pyproject.toml
  4. 101 0
      tools/coverage_check.py

+ 8 - 0
.coveragerc

@@ -0,0 +1,8 @@
+[run]
+omit =
+    tests/*
+
+[report]
+exclude_lines =
+    # Ignore pragma: no cover comments
+    pragma: no cover

+ 18 - 6
.github/workflows/overall-tests.yml

@@ -20,9 +20,12 @@ jobs:
   coverage:
     timeout-minutes: 50
     runs-on: ubuntu-latest
+
     if: github.event_name == 'pull_request' && github.event.pull_request.base.repo.full_name == github.event.pull_request.head.repo.full_name
     steps:
       - uses: actions/checkout@v4
+        with:
+          fetch-depth: 0  # Fetch full history to enable proper diff
 
       - uses: actions/setup-python@v5
         with:
@@ -38,13 +41,22 @@ jobs:
 
       - name: Pytest
         run: |
-          pipenv run pytest --cov=taipy --cov-report="xml:overall-coverage.xml" tests
+          python -m pip install xmltodict
+          pipenv run pytest --cov=taipy --cov-report=xml:${{ github.workspace }}/coverage.xml --cov-config=.coveragerc
 
-      - name: Coverage
-        uses: orgoro/coverage@v3.1
-        with:
-          coverageFile: overall-coverage.xml
-          token: ${{ secrets.GITHUB_TOKEN }}
+
+      - name: Fetch base branch
+        run: |
+          git fetch origin ${{ github.event.pull_request.base.ref }}:refs/remotes/origin/${{ github.event.pull_request.base.ref }}
+
+
+      - name: Check total project coverage
+        run: |
+          python tools/coverage_check.py check-total --coverage-file ${{ github.workspace }}/coverage.xml --threshold 80
+
+      - name: Check pull request coverage
+        run: |
+          python tools/coverage_check.py check-changed --coverage-file ${{ github.workspace }}/coverage.xml --threshold 80 --base-branch ${{ github.event.pull_request.base.ref }}
 
   overall-tests:
     needs: [partial-tests]

+ 1 - 0
pyproject.toml

@@ -51,6 +51,7 @@ exclude = [
     "build",
     "dist",
     "releases",
+    "tools",
     ".venv",
     ".mypy_cache",
     ".pytest_cache",

+ 101 - 0
tools/coverage_check.py

@@ -0,0 +1,101 @@
+# Copyright 2021-2025 Avaiga Private Limited
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
+# an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations under the License.
+
+import argparse
+import xmltodict
+import sys
+import subprocess
+
+
+def check_total_coverage(coverage_file, threshold=80):
+    """Check the total project coverage."""
+    with open(coverage_file) as f:
+        data = xmltodict.parse(f.read())
+    total_coverage = float(data['coverage']['@line-rate']) * 100
+    print(f"Total Coverage: {total_coverage:.2f}%")
+    if total_coverage < threshold:
+        print(f"Total project coverage is below {threshold}%: {total_coverage:.2f}%")
+        sys.exit(1)
+
+
+def check_changed_files_coverage(coverage_file, changed_files, threshold=80):
+    """Check the coverage of changed files."""
+    with open(coverage_file) as f:
+        data = xmltodict.parse(f.read())
+
+    # Handle multiple packages in the coverage report
+    packages = data["coverage"]["packages"]["package"]
+    if not isinstance(packages, list):
+        packages = [packages]
+
+    # Extract coverage data for all files
+    files = {}
+    for package in packages:
+        classes = package["classes"]["class"]
+        if not isinstance(classes, list):
+            classes = [classes]
+        for cls in classes:
+            files[cls["@filename"]] = float(cls["@line-rate"]) * 100
+    qty = 0
+    sum_coverage = 0
+    for file in changed_files:
+        if file in files:
+            coverage = files[file]
+            print(f"Coverage for {file}: {coverage:.2f}%")
+            sum_coverage += coverage
+            qty += 1
+        else:
+            print(f"No coverage data found for {file}")
+
+    if sum_coverage/qty < threshold:
+        print(f"Coverage for changed files is below {threshold}%: {sum_coverage/qty:.2f}%")
+        sys.exit(1)
+    print(f"Coverage for changed files: {sum_coverage/qty:.2f}%")
+
+
+def get_changed_files(base_branch):
+    """Get the list of changed Python files in the pull request."""
+    try:
+        result = subprocess.run(
+            ['git', 'diff', '--name-only', f"origin/{base_branch}", '--', '*.py'],
+            capture_output=True,
+            text=True,
+            check=True,
+        )
+        changed_files = [
+            file.replace("taipy/", "") for file in result.stdout.strip().splitlines() if not file.startswith(('tests/', 'tools/'))
+        ]
+        return changed_files
+    except subprocess.CalledProcessError as e:
+        print(f"Error fetching changed files: {e}")
+        sys.exit(1)
+
+
+if __name__ == '__main__':
+    parser = argparse.ArgumentParser(description="Coverage check script.")
+    parser.add_argument('command', choices=['check-total', 'check-changed'], help="Command to execute")
+    parser.add_argument('--coverage-file', default='coverage.xml', help="Path to the coverage XML file")
+    parser.add_argument('--threshold', type=float, default=80, help="Coverage threshold percentage")
+    parser.add_argument('--base-branch', help="Base branch for comparing changed files")
+
+    args = parser.parse_args()
+
+    if args.command == 'check-total':
+        check_total_coverage(args.coverage_file, args.threshold)
+    elif args.command == 'check-changed':
+        if not args.base_branch:
+            print("Error: --base-branch is required for check-changed")
+            sys.exit(1)
+        changed_files = get_changed_files(args.base_branch)
+        if not changed_files:
+            print("No relevant Python files changed.")
+            sys.exit(0)
+        check_changed_files_coverage(args.coverage_file, changed_files, args.threshold)