From 4647c2010db9f5cb5d7f6be9c7eed3923969eec1 Mon Sep 17 00:00:00 2001 From: Michal Baumgartner Date: Fri, 7 Nov 2025 11:03:33 +0100 Subject: [PATCH 01/15] chore(ci): Combine code coverage in CI before uploading to codecov --- .github/workflows/ci.yml | 99 ++++++++++++++++++++++++++++++++++------ 1 file changed, 85 insertions(+), 14 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 81e283f..430ebf6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -338,30 +338,22 @@ jobs: env: TOOLKIT_VERSION: ${{ steps.version.outputs.VERSION }} PYTHON_VERSION: ${{ matrix.python-version }} - COVERAGE_FILE: coverage/.coverage.${{ matrix.python-version }} + COVERAGE_FILE: .coverage.${{ matrix.python-version }} run: | set -euo pipefail - # Create coverage directory - mkdir -p coverage - - # Run tests with coverage poetry run pytest tests/unit \ --cov=deepnote_toolkit \ --cov=installer \ --cov=deepnote_core \ --cov-branch \ --cov-report=term-missing:skip-covered \ - --cov-report=xml:coverage/coverage-${PYTHON_VERSION}.xml \ - --cov-report=json:coverage/coverage-${PYTHON_VERSION}.json \ --junitxml=junit.xml \ -o junit_family=legacy # Check if coverage file was generated - if [ -f "coverage/.coverage.${PYTHON_VERSION}" ]; then + if [ -f ".coverage.${PYTHON_VERSION}" ]; then echo "coverage_generated=true" >> $GITHUB_OUTPUT - echo "Coverage files found:" - ls -la coverage/ else echo "coverage_generated=false" >> $GITHUB_OUTPUT echo "Warning: No coverage file generated" @@ -373,7 +365,7 @@ jobs: PYTHON_VERSION: ${{ matrix.python-version }} run: | echo "## Python ${PYTHON_VERSION} Coverage" >> $GITHUB_STEP_SUMMARY - poetry run coverage report --data-file=coverage/.coverage.${PYTHON_VERSION} --format=markdown >> $GITHUB_STEP_SUMMARY + poetry run coverage report --data-file=.coverage.${PYTHON_VERSION} --format=markdown >> $GITHUB_STEP_SUMMARY - name: Upload test results to Codecov (these are results not coverage reports) if: ${{ !cancelled() }} @@ -382,14 +374,93 @@ jobs: token: ${{ secrets.CODECOV_TOKEN }} flags: python-${{ matrix.python-version }} - - name: Upload coverage to Codecov + - name: Upload coverage artifacts + if: steps.test-unit.outputs.coverage_generated == 'true' + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + with: + name: coverage-${{ matrix.python-version }} + path: .coverage.${{ matrix.python-version }} + retention-days: 1 + + coverage-combine: + name: Combine and Upload Coverage + runs-on: ubuntu-latest + needs: tests-unit + if: always() + steps: + - name: Checkout code + uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4 + with: + persist-credentials: false + + - name: Set up Python + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 + with: + python-version: '3.11' + + - name: Install coverage + run: pip install coverage[toml] + + - name: Download all coverage artifacts + uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4 + continue-on-error: true + with: + pattern: coverage-* + path: coverage-data/ + merge-multiple: true + + - name: Combine coverage files + id: combine + run: | + set -euo pipefail + + if [ ! -d "coverage-data" ] || [ -z "$(ls -A coverage-data 2>/dev/null)" ]; then + echo "No coverage artifacts were downloaded. All tests may have failed before generating coverage." + echo "success=false" >> $GITHUB_OUTPUT + exit 0 + fi + + echo "Downloaded coverage files:" + ls -la coverage-data/ + + shopt -s nullglob + coverage_files=(coverage-data/.coverage.*) + if [ ${#coverage_files[@]} -eq 0 ]; then + echo "No .coverage.* files found to combine" + echo "success=false" >> $GITHUB_OUTPUT + exit 0 + fi + + echo "Found ${#coverage_files[@]} coverage file(s) to combine" + + cd coverage-data + coverage combine + coverage xml -o coverage.xml + coverage report + + echo "## Combined Coverage Report" >> $GITHUB_STEP_SUMMARY + coverage report --format=markdown >> $GITHUB_STEP_SUMMARY + + echo "success=true" >> $GITHUB_OUTPUT + + - name: Upload combined coverage to Codecov + if: steps.combine.outputs.success == 'true' uses: codecov/codecov-action@5a1091511ad55cbe89839c7260b706298ca349f7 # v5 with: token: ${{ secrets.CODECOV_TOKEN }} - slug: deepnote/deepnote-toolkit - files: ./coverage/coverage-${{ matrix.python-version }}.xml + slug: ${{ github.repository }} + files: ./coverage-data/coverage.xml + flags: combined fail_ci_if_error: ${{ github.event.pull_request.head.repo.full_name == github.repository || github.event_name == 'push' }} + - name: Upload combined coverage report + if: steps.combine.outputs.success == 'true' + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + with: + name: coverage-combined-report + path: coverage-data/coverage.xml + retention-days: 30 + audit-prod: name: Audit - Production runs-on: ubuntu-latest From 1bea06f3443b87347469e4cf5d10c42e8404f4c8 Mon Sep 17 00:00:00 2001 From: Michal Baumgartner Date: Fri, 7 Nov 2025 11:18:44 +0100 Subject: [PATCH 02/15] fix(ci): Account for parallel mode creating multiple coverage files --- .github/workflows/ci.yml | 35 ++++++++++++++++------------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 430ebf6..8de8e0b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -338,7 +338,6 @@ jobs: env: TOOLKIT_VERSION: ${{ steps.version.outputs.VERSION }} PYTHON_VERSION: ${{ matrix.python-version }} - COVERAGE_FILE: .coverage.${{ matrix.python-version }} run: | set -euo pipefail @@ -351,14 +350,21 @@ jobs: --junitxml=junit.xml \ -o junit_family=legacy - # Check if coverage file was generated - if [ -f ".coverage.${PYTHON_VERSION}" ]; then - echo "coverage_generated=true" >> $GITHUB_OUTPUT - else + # Coverage parallel mode creates multiple .coverage.* files, combine them + shopt -s nullglob + coverage_files=(.coverage.*) + + if [ ${#coverage_files[@]} -eq 0 ]; then + echo "Error: No coverage data generated" echo "coverage_generated=false" >> $GITHUB_OUTPUT - echo "Warning: No coverage file generated" + exit 1 fi + poetry run coverage combine + mv .coverage .coverage.${PYTHON_VERSION} + echo "Coverage saved as .coverage.${PYTHON_VERSION}" + echo "coverage_generated=true" >> $GITHUB_OUTPUT + - name: Per-version coverage summary if: steps.test-unit.outputs.coverage_generated == 'true' env: @@ -414,33 +420,24 @@ jobs: run: | set -euo pipefail - if [ ! -d "coverage-data" ] || [ -z "$(ls -A coverage-data 2>/dev/null)" ]; then - echo "No coverage artifacts were downloaded. All tests may have failed before generating coverage." - echo "success=false" >> $GITHUB_OUTPUT - exit 0 - fi - - echo "Downloaded coverage files:" - ls -la coverage-data/ - shopt -s nullglob coverage_files=(coverage-data/.coverage.*) + if [ ${#coverage_files[@]} -eq 0 ]; then - echo "No .coverage.* files found to combine" + echo "No coverage files to combine (tests may have failed)" echo "success=false" >> $GITHUB_OUTPUT exit 0 fi - echo "Found ${#coverage_files[@]} coverage file(s) to combine" - + echo "Combining ${#coverage_files[@]} coverage file(s)" cd coverage-data + coverage combine coverage xml -o coverage.xml coverage report echo "## Combined Coverage Report" >> $GITHUB_STEP_SUMMARY coverage report --format=markdown >> $GITHUB_STEP_SUMMARY - echo "success=true" >> $GITHUB_OUTPUT - name: Upload combined coverage to Codecov From 2fae2c93f841910183468c82925d552eb6046cef Mon Sep 17 00:00:00 2001 From: Michal Baumgartner Date: Fri, 7 Nov 2025 11:22:45 +0100 Subject: [PATCH 03/15] test: Debug coverage files --- .github/workflows/ci.yml | 39 +++++++++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8de8e0b..e46d749 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -350,21 +350,36 @@ jobs: --junitxml=junit.xml \ -o junit_family=legacy - # Coverage parallel mode creates multiple .coverage.* files, combine them - shopt -s nullglob - coverage_files=(.coverage.*) + # Debug: Check what coverage files exist + echo "=== Coverage files after pytest ===" + ls -la .coverage* 2>/dev/null || echo "No .coverage* files found" + ls -la .coverage.* 2>/dev/null || echo "No .coverage.* files found" - if [ ${#coverage_files[@]} -eq 0 ]; then - echo "Error: No coverage data generated" - echo "coverage_generated=false" >> $GITHUB_OUTPUT - exit 1 + # Coverage parallel mode may create .coverage.machine.pid files + # Check for both .coverage and .coverage.* patterns + if [ -f ".coverage" ]; then + # Single .coverage file exists (parallel mode auto-combined or not used) + mv .coverage .coverage.${PYTHON_VERSION} + echo "Renamed .coverage to .coverage.${PYTHON_VERSION}" + echo "coverage_generated=true" >> $GITHUB_OUTPUT + else + # Check for parallel coverage files + shopt -s nullglob + coverage_files=(.coverage.*) + + if [ ${#coverage_files[@]} -gt 0 ]; then + echo "Found ${#coverage_files[@]} parallel coverage files, combining..." + poetry run coverage combine + mv .coverage .coverage.${PYTHON_VERSION} + echo "Combined and saved as .coverage.${PYTHON_VERSION}" + echo "coverage_generated=true" >> $GITHUB_OUTPUT + else + echo "Error: No coverage data files found" + echo "coverage_generated=false" >> $GITHUB_OUTPUT + exit 1 + fi fi - poetry run coverage combine - mv .coverage .coverage.${PYTHON_VERSION} - echo "Coverage saved as .coverage.${PYTHON_VERSION}" - echo "coverage_generated=true" >> $GITHUB_OUTPUT - - name: Per-version coverage summary if: steps.test-unit.outputs.coverage_generated == 'true' env: From e7a4fe548eb09283d4ee41e7a1600149162e52d7 Mon Sep 17 00:00:00 2001 From: Michal Baumgartner Date: Fri, 7 Nov 2025 11:29:39 +0100 Subject: [PATCH 04/15] fix: Debug coverage artifacts --- .github/workflows/ci.yml | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e46d749..f9b5ee0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -424,19 +424,22 @@ jobs: - name: Download all coverage artifacts uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4 - continue-on-error: true with: pattern: coverage-* - path: coverage-data/ - merge-multiple: true + path: coverage-artifacts/ - name: Combine coverage files id: combine run: | set -euo pipefail + # List downloaded artifacts structure + echo "=== Downloaded artifacts structure ===" + find coverage-artifacts -type f -name ".coverage.*" || echo "No .coverage.* files found" + + # Collect all coverage files from artifact subdirectories shopt -s nullglob - coverage_files=(coverage-data/.coverage.*) + coverage_files=(coverage-artifacts/*/.coverage.*) if [ ${#coverage_files[@]} -eq 0 ]; then echo "No coverage files to combine (tests may have failed)" @@ -444,9 +447,15 @@ jobs: exit 0 fi - echo "Combining ${#coverage_files[@]} coverage file(s)" - cd coverage-data + echo "Found ${#coverage_files[@]} coverage file(s) to combine" + # Copy all coverage files to a single directory for combining + mkdir -p coverage-data + for file in "${coverage_files[@]}"; do + cp "$file" coverage-data/ + done + + cd coverage-data coverage combine coverage xml -o coverage.xml coverage report From 2f1a731a1c404ed0bc6ed7351742e7f93bd10757 Mon Sep 17 00:00:00 2001 From: Michal Baumgartner Date: Fri, 7 Nov 2025 11:37:21 +0100 Subject: [PATCH 05/15] test: Simplify code coverage artifact paths --- .github/workflows/ci.yml | 58 ++++++++++++---------------------------- 1 file changed, 17 insertions(+), 41 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f9b5ee0..9c13977 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -350,43 +350,21 @@ jobs: --junitxml=junit.xml \ -o junit_family=legacy - # Debug: Check what coverage files exist - echo "=== Coverage files after pytest ===" - ls -la .coverage* 2>/dev/null || echo "No .coverage* files found" - ls -la .coverage.* 2>/dev/null || echo "No .coverage.* files found" - - # Coverage parallel mode may create .coverage.machine.pid files - # Check for both .coverage and .coverage.* patterns - if [ -f ".coverage" ]; then - # Single .coverage file exists (parallel mode auto-combined or not used) - mv .coverage .coverage.${PYTHON_VERSION} - echo "Renamed .coverage to .coverage.${PYTHON_VERSION}" - echo "coverage_generated=true" >> $GITHUB_OUTPUT - else - # Check for parallel coverage files - shopt -s nullglob - coverage_files=(.coverage.*) - - if [ ${#coverage_files[@]} -gt 0 ]; then - echo "Found ${#coverage_files[@]} parallel coverage files, combining..." - poetry run coverage combine - mv .coverage .coverage.${PYTHON_VERSION} - echo "Combined and saved as .coverage.${PYTHON_VERSION}" - echo "coverage_generated=true" >> $GITHUB_OUTPUT - else - echo "Error: No coverage data files found" - echo "coverage_generated=false" >> $GITHUB_OUTPUT - exit 1 - fi + # Check if coverage data was generated + if [ ! -f ".coverage" ]; then + echo "Error: No coverage data file generated" + echo "coverage_generated=false" >> $GITHUB_OUTPUT + exit 1 fi + + echo "Coverage data generated successfully" + echo "coverage_generated=true" >> $GITHUB_OUTPUT - name: Per-version coverage summary if: steps.test-unit.outputs.coverage_generated == 'true' - env: - PYTHON_VERSION: ${{ matrix.python-version }} run: | - echo "## Python ${PYTHON_VERSION} Coverage" >> $GITHUB_STEP_SUMMARY - poetry run coverage report --data-file=.coverage.${PYTHON_VERSION} --format=markdown >> $GITHUB_STEP_SUMMARY + echo "## Python ${{ matrix.python-version }} Coverage" >> $GITHUB_STEP_SUMMARY + poetry run coverage report --format=markdown >> $GITHUB_STEP_SUMMARY - name: Upload test results to Codecov (these are results not coverage reports) if: ${{ !cancelled() }} @@ -400,7 +378,7 @@ jobs: uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: name: coverage-${{ matrix.python-version }} - path: .coverage.${{ matrix.python-version }} + path: .coverage retention-days: 1 coverage-combine: @@ -433,13 +411,9 @@ jobs: run: | set -euo pipefail - # List downloaded artifacts structure - echo "=== Downloaded artifacts structure ===" - find coverage-artifacts -type f -name ".coverage.*" || echo "No .coverage.* files found" - - # Collect all coverage files from artifact subdirectories + # Collect all .coverage files from artifact subdirectories shopt -s nullglob - coverage_files=(coverage-artifacts/*/.coverage.*) + coverage_files=(coverage-artifacts/*/.coverage) if [ ${#coverage_files[@]} -eq 0 ]; then echo "No coverage files to combine (tests may have failed)" @@ -449,10 +423,12 @@ jobs: echo "Found ${#coverage_files[@]} coverage file(s) to combine" - # Copy all coverage files to a single directory for combining + # Rename each file to include a unique suffix for coverage combine mkdir -p coverage-data + i=0 for file in "${coverage_files[@]}"; do - cp "$file" coverage-data/ + cp "$file" "coverage-data/.coverage.$i" + ((i++)) done cd coverage-data From d4dedac8a13416cc70c22287703d902432d69c55 Mon Sep 17 00:00:00 2001 From: Michal Baumgartner Date: Fri, 7 Nov 2025 11:42:07 +0100 Subject: [PATCH 06/15] test: Inspect generated coverage files as they are missing before upload --- .github/workflows/ci.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9c13977..d62803f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -373,6 +373,14 @@ jobs: token: ${{ secrets.CODECOV_TOKEN }} flags: python-${{ matrix.python-version }} + - name: Debug - Check file before upload + if: steps.test-unit.outputs.coverage_generated == 'true' + run: | + echo "PWD: $(pwd)" + echo "Looking for .coverage file:" + ls -la .coverage || echo ".coverage not found" + find . -name ".coverage" -type f || echo "No .coverage found anywhere" + - name: Upload coverage artifacts if: steps.test-unit.outputs.coverage_generated == 'true' uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 From 88a9cc81c0454d006ca1a406f40e9bfbb117b68a Mon Sep 17 00:00:00 2001 From: Michal Baumgartner Date: Fri, 7 Nov 2025 11:45:18 +0100 Subject: [PATCH 07/15] fix: Ensure upload-artifact action includes gitignored `.coverage` file --- .github/workflows/ci.yml | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d62803f..d0ad828 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -373,14 +373,6 @@ jobs: token: ${{ secrets.CODECOV_TOKEN }} flags: python-${{ matrix.python-version }} - - name: Debug - Check file before upload - if: steps.test-unit.outputs.coverage_generated == 'true' - run: | - echo "PWD: $(pwd)" - echo "Looking for .coverage file:" - ls -la .coverage || echo ".coverage not found" - find . -name ".coverage" -type f || echo "No .coverage found anywhere" - - name: Upload coverage artifacts if: steps.test-unit.outputs.coverage_generated == 'true' uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 @@ -388,6 +380,7 @@ jobs: name: coverage-${{ matrix.python-version }} path: .coverage retention-days: 1 + include-hidden-files: true coverage-combine: name: Combine and Upload Coverage From f0e982f2d443f561ee743581f43bdbdfb9d368e3 Mon Sep 17 00:00:00 2001 From: Michal Baumgartner Date: Fri, 7 Nov 2025 11:50:13 +0100 Subject: [PATCH 08/15] test: Debug coverage combining step --- .github/workflows/ci.yml | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d0ad828..d94c8d5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -413,6 +413,7 @@ jobs: set -euo pipefail # Collect all .coverage files from artifact subdirectories + echo "Collecting coverage files from artifact subdirectories" shopt -s nullglob coverage_files=(coverage-artifacts/*/.coverage) @@ -428,14 +429,23 @@ jobs: mkdir -p coverage-data i=0 for file in "${coverage_files[@]}"; do + echo "Copying $file to coverage-data/.coverage.$i" cp "$file" "coverage-data/.coverage.$i" ((i++)) done cd coverage-data - coverage combine - coverage xml -o coverage.xml - coverage report + echo "Files in coverage-data:" + ls -la + + echo "Running coverage combine..." + coverage combine || { echo "coverage combine failed"; exit 1; } + + echo "Generating XML report..." + coverage xml -o coverage.xml || { echo "coverage xml failed"; exit 1; } + + echo "Generating text report..." + coverage report || { echo "coverage report failed"; exit 1; } echo "## Combined Coverage Report" >> $GITHUB_STEP_SUMMARY coverage report --format=markdown >> $GITHUB_STEP_SUMMARY From 7d17871621006b6eccd58d528a7d009faa7b9cb1 Mon Sep 17 00:00:00 2001 From: Michal Baumgartner Date: Fri, 7 Nov 2025 11:55:22 +0100 Subject: [PATCH 09/15] fix: Coverage artifact ID incrementing --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d94c8d5..64203e1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -431,7 +431,7 @@ jobs: for file in "${coverage_files[@]}"; do echo "Copying $file to coverage-data/.coverage.$i" cp "$file" "coverage-data/.coverage.$i" - ((i++)) + i=$((i + 1)) done cd coverage-data From dcaa0df755961c0d00cdc9ca88e52d2cb4cfaa1f Mon Sep 17 00:00:00 2001 From: Michal Baumgartner Date: Fri, 7 Nov 2025 11:59:56 +0100 Subject: [PATCH 10/15] fix: Fail fast and use correct directory when combining coverage files --- .github/workflows/ci.yml | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 64203e1..db8ab83 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -381,6 +381,7 @@ jobs: path: .coverage retention-days: 1 include-hidden-files: true + if-no-files-found: error coverage-combine: name: Combine and Upload Coverage @@ -429,20 +430,15 @@ jobs: mkdir -p coverage-data i=0 for file in "${coverage_files[@]}"; do - echo "Copying $file to coverage-data/.coverage.$i" cp "$file" "coverage-data/.coverage.$i" i=$((i + 1)) done - cd coverage-data - echo "Files in coverage-data:" - ls -la - - echo "Running coverage combine..." - coverage combine || { echo "coverage combine failed"; exit 1; } + echo "Running coverage combine from workspace root..." + coverage combine coverage-data/ || { echo "coverage combine failed"; exit 1; } echo "Generating XML report..." - coverage xml -o coverage.xml || { echo "coverage xml failed"; exit 1; } + coverage xml -o coverage-data/coverage.xml || { echo "coverage xml failed"; exit 1; } echo "Generating text report..." coverage report || { echo "coverage report failed"; exit 1; } From 919d72d27ce0f8f6cfc83605d007ea2d50a501f5 Mon Sep 17 00:00:00 2001 From: Michal Baumgartner Date: Fri, 7 Nov 2025 12:05:43 +0100 Subject: [PATCH 11/15] fix: Only upload the provided combined coverage file to codecov --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index db8ab83..2b5feb9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -455,6 +455,7 @@ jobs: slug: ${{ github.repository }} files: ./coverage-data/coverage.xml flags: combined + disable_search: true fail_ci_if_error: ${{ github.event.pull_request.head.repo.full_name == github.repository || github.event_name == 'push' }} - name: Upload combined coverage report From a1a69d3668d679c33746a289a43211a569b49c34 Mon Sep 17 00:00:00 2001 From: Michal Baumgartner Date: Fri, 7 Nov 2025 12:12:30 +0100 Subject: [PATCH 12/15] chore(ci): Simplify the logic and error handling for combining coverage --- .github/workflows/ci.yml | 51 +++++----------------------------------- 1 file changed, 6 insertions(+), 45 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2b5feb9..9196dda 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -334,13 +334,9 @@ jobs: run: poetry install --no-interaction --only-root - name: Run unit tests - id: test-unit env: TOOLKIT_VERSION: ${{ steps.version.outputs.VERSION }} - PYTHON_VERSION: ${{ matrix.python-version }} run: | - set -euo pipefail - poetry run pytest tests/unit \ --cov=deepnote_toolkit \ --cov=installer \ @@ -350,23 +346,12 @@ jobs: --junitxml=junit.xml \ -o junit_family=legacy - # Check if coverage data was generated - if [ ! -f ".coverage" ]; then - echo "Error: No coverage data file generated" - echo "coverage_generated=false" >> $GITHUB_OUTPUT - exit 1 - fi - - echo "Coverage data generated successfully" - echo "coverage_generated=true" >> $GITHUB_OUTPUT - - name: Per-version coverage summary - if: steps.test-unit.outputs.coverage_generated == 'true' run: | echo "## Python ${{ matrix.python-version }} Coverage" >> $GITHUB_STEP_SUMMARY poetry run coverage report --format=markdown >> $GITHUB_STEP_SUMMARY - - name: Upload test results to Codecov (these are results not coverage reports) + - name: Upload test results to Codecov if: ${{ !cancelled() }} uses: codecov/test-results-action@47f89e9acb64b76debcd5ea40642d25a4adced9f # v1 with: @@ -374,7 +359,6 @@ jobs: flags: python-${{ matrix.python-version }} - name: Upload coverage artifacts - if: steps.test-unit.outputs.coverage_generated == 'true' uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: name: coverage-${{ matrix.python-version }} @@ -409,46 +393,24 @@ jobs: path: coverage-artifacts/ - name: Combine coverage files - id: combine run: | - set -euo pipefail - - # Collect all .coverage files from artifact subdirectories - echo "Collecting coverage files from artifact subdirectories" shopt -s nullglob - coverage_files=(coverage-artifacts/*/.coverage) - - if [ ${#coverage_files[@]} -eq 0 ]; then - echo "No coverage files to combine (tests may have failed)" - echo "success=false" >> $GITHUB_OUTPUT - exit 0 - fi - - echo "Found ${#coverage_files[@]} coverage file(s) to combine" - - # Rename each file to include a unique suffix for coverage combine mkdir -p coverage-data + i=0 - for file in "${coverage_files[@]}"; do + for file in coverage-artifacts/*/.coverage; do cp "$file" "coverage-data/.coverage.$i" i=$((i + 1)) done - echo "Running coverage combine from workspace root..." - coverage combine coverage-data/ || { echo "coverage combine failed"; exit 1; } - - echo "Generating XML report..." - coverage xml -o coverage-data/coverage.xml || { echo "coverage xml failed"; exit 1; } - - echo "Generating text report..." - coverage report || { echo "coverage report failed"; exit 1; } + coverage combine coverage-data/ + coverage xml -o coverage-data/coverage.xml + coverage report echo "## Combined Coverage Report" >> $GITHUB_STEP_SUMMARY coverage report --format=markdown >> $GITHUB_STEP_SUMMARY - echo "success=true" >> $GITHUB_OUTPUT - name: Upload combined coverage to Codecov - if: steps.combine.outputs.success == 'true' uses: codecov/codecov-action@5a1091511ad55cbe89839c7260b706298ca349f7 # v5 with: token: ${{ secrets.CODECOV_TOKEN }} @@ -459,7 +421,6 @@ jobs: fail_ci_if_error: ${{ github.event.pull_request.head.repo.full_name == github.repository || github.event_name == 'push' }} - name: Upload combined coverage report - if: steps.combine.outputs.success == 'true' uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: name: coverage-combined-report From 91f6905e28c43f32ac17163071dc07409980be9b Mon Sep 17 00:00:00 2001 From: Michal Baumgartner Date: Fri, 7 Nov 2025 13:08:56 +0100 Subject: [PATCH 13/15] fix: Debug coverage using nox target --- noxfile.py | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/noxfile.py b/noxfile.py index 32b0c49..9c58eda 100644 --- a/noxfile.py +++ b/noxfile.py @@ -146,17 +146,28 @@ def coverage_report(session): import pathlib - coverage_dir = pathlib.Path("coverage") + # Use absolute path relative to session.invoked_from + project_root = pathlib.Path(session.invoked_from) + coverage_dir = project_root / "coverage" + if not coverage_dir.exists(): - session.error("No coverage directory found. Run tests with --coverage first.") - - # Combine all coverage files from coverage directory - session.run( - "coverage", "combine", "--data-file=coverage/.coverage", "coverage/.coverage.*" - ) + session.error("No coverage directory found. Run tests with nox -s unit first.") + + # Check if we have a combined coverage file or individual files + combined_file = coverage_dir / ".coverage" + coverage_files = sorted(coverage_dir.glob(".coverage.*")) + + if not combined_file.exists() and not coverage_files: + session.error("No coverage files found. Run tests with nox -s unit first.") + + if coverage_files: + session.log(f"Combining {len(coverage_files)} coverage files") + session.run("coverage", "combine", "--data-file=coverage/.coverage", *[str(f) for f in coverage_files]) + else: + session.log("Using existing combined coverage file") # Generate reports in coverage directory - session.run("coverage", "report", "--format=markdown") - session.run("coverage", "html", "-d", "coverage/htmlcov") - session.run("coverage", "xml", "-o", "coverage/coverage.xml", "-i") - session.run("coverage", "json", "-o", "coverage/coverage.json") + session.run("coverage", "report", "--data-file=coverage/.coverage", "--format=markdown") + session.run("coverage", "html", "--data-file=coverage/.coverage", "-d", "coverage/htmlcov") + session.run("coverage", "xml", "--data-file=coverage/.coverage", "-o", "coverage/coverage.xml", "-i") + session.run("coverage", "json", "--data-file=coverage/.coverage", "-o", "coverage/coverage.json") From d058de1406af238973b8e62cff75bb7f38fafc06 Mon Sep 17 00:00:00 2001 From: Michal Baumgartner Date: Fri, 7 Nov 2025 13:11:44 +0100 Subject: [PATCH 14/15] fix: Lint --- noxfile.py | 36 +++++++++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/noxfile.py b/noxfile.py index 9c58eda..3cdb98f 100644 --- a/noxfile.py +++ b/noxfile.py @@ -149,7 +149,7 @@ def coverage_report(session): # Use absolute path relative to session.invoked_from project_root = pathlib.Path(session.invoked_from) coverage_dir = project_root / "coverage" - + if not coverage_dir.exists(): session.error("No coverage directory found. Run tests with nox -s unit first.") @@ -159,15 +159,37 @@ def coverage_report(session): if not combined_file.exists() and not coverage_files: session.error("No coverage files found. Run tests with nox -s unit first.") - + if coverage_files: session.log(f"Combining {len(coverage_files)} coverage files") - session.run("coverage", "combine", "--data-file=coverage/.coverage", *[str(f) for f in coverage_files]) + session.run( + "coverage", + "combine", + "--data-file=coverage/.coverage", + *[str(f) for f in coverage_files], + ) else: session.log("Using existing combined coverage file") # Generate reports in coverage directory - session.run("coverage", "report", "--data-file=coverage/.coverage", "--format=markdown") - session.run("coverage", "html", "--data-file=coverage/.coverage", "-d", "coverage/htmlcov") - session.run("coverage", "xml", "--data-file=coverage/.coverage", "-o", "coverage/coverage.xml", "-i") - session.run("coverage", "json", "--data-file=coverage/.coverage", "-o", "coverage/coverage.json") + session.run( + "coverage", "report", "--data-file=coverage/.coverage", "--format=markdown" + ) + session.run( + "coverage", "html", "--data-file=coverage/.coverage", "-d", "coverage/htmlcov" + ) + session.run( + "coverage", + "xml", + "--data-file=coverage/.coverage", + "-o", + "coverage/coverage.xml", + "-i", + ) + session.run( + "coverage", + "json", + "--data-file=coverage/.coverage", + "-o", + "coverage/coverage.json", + ) From a3dc7359fbb2e103d1b5d2d62934ce093670f247 Mon Sep 17 00:00:00 2001 From: Michal Baumgartner Date: Fri, 7 Nov 2025 16:29:13 +0100 Subject: [PATCH 15/15] chore: Incorporate PR review suggestions --- noxfile.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/noxfile.py b/noxfile.py index 3cdb98f..e976c1d 100644 --- a/noxfile.py +++ b/noxfile.py @@ -165,7 +165,7 @@ def coverage_report(session): session.run( "coverage", "combine", - "--data-file=coverage/.coverage", + f"--data-file={combined_file}", *[str(f) for f in coverage_files], ) else: @@ -173,23 +173,27 @@ def coverage_report(session): # Generate reports in coverage directory session.run( - "coverage", "report", "--data-file=coverage/.coverage", "--format=markdown" + "coverage", "report", f"--data-file={combined_file}", "--format=markdown" ) session.run( - "coverage", "html", "--data-file=coverage/.coverage", "-d", "coverage/htmlcov" + "coverage", + "html", + f"--data-file={combined_file}", + "-d", + str(coverage_dir / "htmlcov"), ) session.run( "coverage", "xml", - "--data-file=coverage/.coverage", + f"--data-file={combined_file}", "-o", - "coverage/coverage.xml", + str(coverage_dir / "coverage.xml"), "-i", ) session.run( "coverage", "json", - "--data-file=coverage/.coverage", + f"--data-file={combined_file}", "-o", - "coverage/coverage.json", + str(coverage_dir / "coverage.json"), )