From db520e8579542f1dea4fdd83fe5b6f5a4a1d1dbd Mon Sep 17 00:00:00 2001 From: David Perl Date: Mon, 3 Aug 2026 14:37:54 +0200 Subject: [PATCH 1/2] ci: fail new pyright errors --- .gitea/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index 48adc9cd..76262d70 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -47,7 +47,7 @@ jobs: run: | source .venv/bin/activate uv pip install -e diff_quality_basedpyright - diff-quality --violations=basedpyright + diff-quality --violations=basedpyright --fail-under=100 test: runs-on: ubuntu-latest -- 2.54.0 From 71d9d07f3900d259716a9e1fe23bb6596b76e689 Mon Sep 17 00:00:00 2001 From: David Perl Date: Mon, 3 Aug 2026 14:53:35 +0200 Subject: [PATCH 2/2] ci: fail diff cover below threshold --- .gitea/workflows/ci.yml | 85 +++++++++++++++++++++++++++++++++++------ pyproject.toml | 1 + 2 files changed, 75 insertions(+), 11 deletions(-) diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index 76262d70..41c49264 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -109,10 +109,15 @@ jobs: test-with-coverage: runs-on: ubuntu-latest + outputs: + diff-coverage: ${{ steps.diff_cover.outputs.percent }} steps: - name: Checkout code uses: https://github.com/actions/checkout@v5 + with: + # diff-cover needs the base branch present to diff against + fetch-depth: 0 - name: Setup uses: ./.gitea/actions/install @@ -129,29 +134,72 @@ jobs: source .venv/bin/activate pytest --cov=aare --cov-config=./pyproject.toml --cov-branch --cov-report=xml --no-cov-on-fail ./tests/unit - - name: Build coverage summary + # No --fail-under here: the gate lives in coverage-analysis so that the PR + # comment is posted before the build is failed. + - name: Diff coverage + id: diff_cover + env: + BASE_BRANCH: ${{ github.base_ref || 'main' }} run: | source .venv/bin/activate + diff-cover coverage.xml \ + --compare-branch="origin/${BASE_BRANCH}" \ + --format "markdown:diff-cover.md,json:diff-cover.json" + percent=$(python -c "import json; print(json.load(open('diff-cover.json'))['total_percent_covered'])") + echo "percent=${percent}" >> "$GITHUB_OUTPUT" + echo "Diff coverage: ${percent}%" + + # Two artefacts on purpose: the full report is too big to post. The F5 WAF + # in front of gitea.psi.ch rejects large POST bodies with an HTML 403, so + # the comment body is capped and the detail lives in the artifact. + - name: Build coverage summary + env: + BASE_BRANCH: ${{ github.base_ref || 'main' }} + DIFF_COVERAGE: ${{ steps.diff_cover.outputs.percent }} + COMMENT_DIFF_BUDGET: "6000" + run: | + source .venv/bin/activate + total=$(coverage report --format=total) + { - echo "" - echo "### Coverage report" + echo "# Coverage report" echo - echo "**Total line + branch coverage: $(coverage report --format=total)%**" + echo "| Metric | Value |" + echo "| --- | --- |" + echo "| Total line + branch coverage | ${total}% |" + echo "| Diff coverage vs \`${BASE_BRANCH}\` | ${DIFF_COVERAGE}% (minimum 80%) |" echo - echo "
Per-file breakdown" + cat diff-cover.md + echo + echo "## Per-file breakdown" echo coverage report --format=markdown - echo - echo "
" } > coverage-summary.md + # Headline numbers only. The F5 WAF in front of gitea.psi.ch rejects + # the comment POST with an HTML 403 for anything sizeable -- an empty + # body posts, 6 kB does not, and the threshold between is unknown. The + # per-line and per-file detail lives in the coverage artifact instead. + # Kept free of markup and backticks as well, since that is untested + # independently of size. + { + echo "Coverage report (automated)" + echo + echo "Total line + branch coverage: ${total}%" + echo "Diff coverage vs ${BASE_BRANCH}: ${DIFF_COVERAGE}% (minimum 80%)" + echo + echo "Full report: coverage artifact on this run." + } > coverage-comment.md + # Only the rendered summary is uploaded. coverage.xml (~1 MB) is rejected # with an HTML 403 by the proxy in front of gitea.psi.ch. - name: Upload coverage uses: https://github.com/actions/upload-artifact@v3 with: name: coverage - path: coverage-summary.md + path: | + coverage-summary.md + coverage-comment.md if-no-files-found: error coverage-analysis: @@ -172,13 +220,28 @@ jobs: id: fc with: issue-number: ${{ github.event.pull_request.number }} - comment-author: "github-actions[bot]" - body-includes: "" + body-includes: "Coverage report (automated)" - name: Create or update comment uses: https://github.com/peter-evans/create-or-update-comment@v5 with: comment-id: ${{ steps.fc.outputs.comment-id }} issue-number: ${{ github.event.pull_request.number }} - body-path: coverage-summary.md + body-path: coverage-comment.md edit-mode: replace + + # Last, so the report is always visible on the PR even when this fails. + - name: Enforce diff coverage + env: + DIFF_COVERAGE: ${{ needs.test-with-coverage.outputs.diff-coverage }} + MINIMUM: "80" + run: | + if [ -z "${DIFF_COVERAGE}" ]; then + echo "::error::test-with-coverage produced no diff coverage value" + exit 1 + fi + echo "Diff coverage: ${DIFF_COVERAGE}% (minimum ${MINIMUM}%)" + if awk -v p="${DIFF_COVERAGE}" -v m="${MINIMUM}" 'BEGIN { exit !(p + 0 < m + 0) }'; then + echo "::error::Diff coverage ${DIFF_COVERAGE}% is below the required ${MINIMUM}%" + exit 1 + fi diff --git a/pyproject.toml b/pyproject.toml index 005125bc..3408b544 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -43,6 +43,7 @@ test = [ "pytest-qt", "pytest-asyncio", "pytest-timeout", + "diff-cover", "ruff>=0.15" ] -- 2.54.0