From e3f0333d89f053e075248b852fa3989f14a0e0ca Mon Sep 17 00:00:00 2001 From: Affan Amir Mir Date: Wed, 8 Apr 2026 18:27:25 +0500 Subject: [PATCH 1/3] feat: redesign PR comments with visual progress bars and status icons MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add Unicode progress bar (████░░░░) showing coverage at a glance - Add per-file colored status icons (🟢 ≥90%, 🟡 ≥70%, 🔴 <70%) - Replace "Meets/Below threshold" with clear "Passed/Failed" status - Use Material Design-inspired layout with visual hierarchy - Add emoji icons to metrics table for scannability - Use distinct headers for coverage (🛡️) and quality (🔬) modes - Cleaner footer with marketplace link - Update README previews to match new comment design - Update tests for new template wording Co-Authored-By: Claude Opus 4.6 (1M context) --- README.md | 106 ++++++++++++++++--------------- src/comment.py | 26 +++++--- src/outputs.py | 26 +++++--- templates/comment_coverage.md.j2 | 35 +++++----- templates/comment_quality.md.j2 | 35 +++++----- templates/step_summary.md.j2 | 28 ++++---- tests/test_comment.py | 4 +- 7 files changed, 149 insertions(+), 111 deletions(-) diff --git a/README.md b/README.md index fb013d5..4b74f6e 100644 --- a/README.md +++ b/README.md @@ -60,89 +60,95 @@ Works with **any language** that can produce Cobertura XML, lcov, or JaCoCo cove ## What You Get -### PR Comment (auto-posted, updates on re-run) +### PR Comment -- Passing -> ## :white_check_mark: Diff Coverage: 82.0% +>

🛡️ Diff Coverage Report

> -> > :heavy_check_mark: Meets threshold of 80% +> **82.0%** `████████████████░░░░` > -> | Metric | Value | -> |--------|------:| -> | **Coverage on diff lines** | **82.0%** | -> | Lines changed | 50 | -> | Lines uncovered | 9 | -> | Files changed | 3 | +> > **✅ Passed** — meets threshold of 80% +> +> | | Metric | Value | +> |:---|:---|---:| +> | 📏 | Lines in diff | **50** | +> | 🔍 | Lines uncovered | **9** | +> | 📁 | Files changed | **3** | > >
-> File breakdown (3 files) +>  📂 3 files changed +>
> -> | File | Coverage | Uncovered Lines | -> |------|:--------:|:---------------:| -> | `src/bar.py` | 60.0% | 5, 6, 7, 8, 15, 22 | -> | `src/foo.py` | 85.0% | 13, 27, 42 | -> | `src/baz.py` | 100.0% | | +> | | File | Coverage | Uncovered Lines | +> |:---:|:---|---:|:---| +> | 🔴 | `src/bar.py` | 60.0% | 5, 6, 7, 8, 15, 22 | +> | 🟡 | `src/foo.py` | 85.0% | 13, 27, 42 | +> | 🟢 | `src/baz.py` | 100.0% | — | > >
> > --- -> Posted by diff-cover-action +> 🛡️ diff-cover-action · Updates on re-run -### PR Comment -- Below Threshold +### PR Comment -- Failing -> ## :red_circle: Diff Coverage: 45.0% +>

🛡️ Diff Coverage Report

+> +> **45.0%** `█████████░░░░░░░░░░░` > -> > :x: Below threshold of 80% -- needs 35.0% more coverage +> > **❌ Failed** — below threshold of 80% by 35.0% > -> | Metric | Value | -> |--------|------:| -> | **Coverage on diff lines** | **45.0%** | -> | Lines changed | 120 | -> | Lines uncovered | 66 | -> | Files changed | 8 | +> | | Metric | Value | +> |:---|:---|---:| +> | 📏 | Lines in diff | **120** | +> | 🔍 | Lines uncovered | **66** | +> | 📁 | Files changed | **8** | > >
-> File breakdown (8 files) +>  📂 8 files changed +>
> -> | File | Coverage | Uncovered Lines | -> |------|:--------:|:---------------:| -> | `src/payments/stripe.py` | 0.0% | 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 (+12 more) | -> | `src/auth/login.py` | 25.0% | 15, 16, 17, 30, 31, 32 | -> | `src/api/routes.py` | 40.0% | 22, 23, 55, 56, 57 | -> | `src/models/user.py` | 50.0% | 8, 9, 44 | -> | `src/utils/cache.py` | 60.0% | 18, 19 | -> | `src/services/email.py` | 70.0% | 33 | -> | `src/config.py` | 80.0% | 5 | -> | `src/middleware.py` | 100.0% | | +> | | File | Coverage | Uncovered Lines | +> |:---:|:---|---:|:---| +> | 🔴 | `src/payments/stripe.py` | 0.0% | 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 (+12 more) | +> | 🔴 | `src/auth/login.py` | 25.0% | 15, 16, 17, 30, 31, 32 | +> | 🔴 | `src/api/routes.py` | 40.0% | 22, 23, 55, 56, 57 | +> | 🔴 | `src/models/user.py` | 50.0% | 8, 9, 44 | +> | 🔴 | `src/utils/cache.py` | 60.0% | 18, 19 | +> | 🟡 | `src/services/email.py` | 70.0% | 33 | +> | 🟡 | `src/config.py` | 80.0% | 5 | +> | 🟢 | `src/middleware.py` | 100.0% | — | > >
> > --- -> Posted by diff-cover-action +> 🛡️ diff-cover-action · Updates on re-run ### Diff Quality PR Comment -> ## :large_orange_diamond: Diff Quality: 75.0% +>

🔬 Diff Quality Report

+> +> **75.0%** `███████████████░░░░░` > -> > :x: Below threshold of 90% +> > **❌ Failed** — below threshold of 90% by 15.0% > -> | Metric | Value | -> |--------|------:| -> | **Quality on diff lines** | **75.0%** | -> | Lines changed | 20 | -> | Lines with violations | 4 | -> | Files changed | 1 | +> | | Metric | Value | +> |:---|:---|---:| +> | 📏 | Lines in diff | **20** | +> | ⚠️ | Lines with violations | **4** | +> | 📁 | Files changed | **1** | > >
-> File breakdown (1 file) +>  📂 1 file changed +>
> -> | File | Quality | Violation Lines | -> |------|:-------:|:---------------:| -> | `src/module.py` | 75.0% | 10, 11, 12, 30 | +> | | File | Quality | Violation Lines | +> |:---:|:---|---:|:---| +> | 🟡 | `src/module.py` | 75.0% | 10, 11, 12, 30 | > >
> > --- -> Posted by diff-cover-action +> 🛡️ diff-cover-action · Updates on re-run ### Inline Annotations (appear directly on the PR diff) diff --git a/src/comment.py b/src/comment.py index 6c366fc..3ddf12a 100644 --- a/src/comment.py +++ b/src/comment.py @@ -100,6 +100,22 @@ def _find_existing_comment( return None +def _progress_bar(percent: float, width: int = 20) -> str: + """Generate a Unicode progress bar. Example: ████████████████░░░░ for 80%.""" + filled = round(percent / 100 * width) + filled = max(0, min(width, filled)) + return "\u2588" * filled + "\u2591" * (width - filled) + + +def _status_icon(percent: float) -> str: + """Return a colored circle emoji based on coverage percentage.""" + if percent >= 90: + return "\U0001f7e2" # 🟢 + if percent >= 70: + return "\U0001f7e1" # 🟡 + return "\U0001f534" # 🔴 + + def _render_comment_body( *, report: Report, @@ -115,24 +131,18 @@ def _render_comment_body( autoescape=False, keep_trailing_newline=True, ) + env.filters["progress_bar"] = _progress_bar + env.filters["status_icon"] = _status_icon template_name = f"comment_{mode}.md.j2" template = env.get_template(template_name) - if report.total_percent_covered >= 90: - icon = ":white_check_mark:" - elif report.total_percent_covered >= 70: - icon = ":large_orange_diamond:" - else: - icon = ":red_circle:" - return template.render( report=report, mode=mode, fail_under=fail_under, threshold_met=threshold_met, identifier=identifier, - icon=icon, md_report_content=md_report_content, ) diff --git a/src/outputs.py b/src/outputs.py index fb33bb5..84e9400 100644 --- a/src/outputs.py +++ b/src/outputs.py @@ -48,6 +48,22 @@ def write_outputs( _set_output("exit-code", str(exit_code)) +def _progress_bar(percent: float, width: int = 20) -> str: + """Generate a Unicode progress bar.""" + filled = round(percent / 100 * width) + filled = max(0, min(width, filled)) + return "\u2588" * filled + "\u2591" * (width - filled) + + +def _status_icon(percent: float) -> str: + """Return a colored circle emoji based on coverage percentage.""" + if percent >= 90: + return "\U0001f7e2" # 🟢 + if percent >= 70: + return "\U0001f7e1" # 🟡 + return "\U0001f534" # 🔴 + + def write_step_summary( *, report: Report, @@ -61,20 +77,14 @@ def write_step_summary( autoescape=False, keep_trailing_newline=True, ) + env.filters["progress_bar"] = _progress_bar + env.filters["status_icon"] = _status_icon template = env.get_template("step_summary.md.j2") - if report.total_percent_covered >= 90: - icon = "white_check_mark" - elif report.total_percent_covered >= 70: - icon = "large_orange_diamond" - else: - icon = "red_circle" - content = template.render( report=report, mode=mode, fail_under=fail_under, threshold_met=threshold_met, - icon=icon, ) _append_to_github_file("GITHUB_STEP_SUMMARY", content) diff --git a/templates/comment_coverage.md.j2 b/templates/comment_coverage.md.j2 index c72f554..0bb147e 100644 --- a/templates/comment_coverage.md.j2 +++ b/templates/comment_coverage.md.j2 @@ -1,28 +1,31 @@ -## {{ icon }} Diff Coverage: {{ "%.1f" | format(report.total_percent_covered) }}% + +

🛡️ Diff Coverage Report

+ +**{{ "%.1f" | format(report.total_percent_covered) }}%** `{{ report.total_percent_covered | progress_bar }}` {% if fail_under > 0 %} {% if threshold_met %} -> :heavy_check_mark: Meets threshold of {{ "%.0f" | format(fail_under) }}% +> **✅ Passed** — meets threshold of {{ "%.0f" | format(fail_under) }}% {% else %} -> :x: Below threshold of {{ "%.0f" | format(fail_under) }}% — needs {{ "%.1f" | format(fail_under - report.total_percent_covered) }}% more coverage +> **❌ Failed** — below threshold of {{ "%.0f" | format(fail_under) }}% by {{ "%.1f" | format(fail_under - report.total_percent_covered) }}% {% endif %} {% endif %} -| Metric | Value | -|--------|------:| -| **Coverage on diff lines** | **{{ "%.1f" | format(report.total_percent_covered) }}%** | -| Lines changed | {{ report.total_num_lines }} | -| Lines uncovered | {{ report.total_num_violations }} | -| Files changed | {{ report.files | length }} | +| | Metric | Value | +|:---|:---|---:| +| 📏 | Lines in diff | **{{ report.total_num_lines }}** | +| 🔍 | Lines uncovered | **{{ report.total_num_violations }}** | +| 📁 | Files changed | **{{ report.files | length }}** | {% if report.files %}
-File breakdown ({{ report.files | length }} file{{ "s" if report.files | length != 1 else "" }}) + 📂 {{ report.files | length }} file{{ "s" if report.files | length != 1 else "" }} changed +
-| File | Coverage | Uncovered Lines | -|------|:--------:|:---------------:| +| | File | Coverage | Uncovered Lines | +|:---:|:---|---:|:---| {% for f in report.files | sort(attribute='percent_covered') %} -| `{{ f.path }}` | {{ "%.1f" | format(f.percent_covered) }}% | {{ f.violation_lines[:10] | join(', ') }}{% if f.violation_lines | length > 10 %} (+{{ f.violation_lines | length - 10 }} more){% endif %} | +| {{ f.percent_covered | status_icon }} | `{{ f.path }}` | {{ "%.1f" | format(f.percent_covered) }}% | {{ f.violation_lines[:10] | join(', ') if f.violation_lines else '—' }}{% if f.violation_lines | length > 10 %} (+{{ f.violation_lines | length - 10 }} more){% endif %} | {% endfor %}
@@ -30,7 +33,8 @@ {% if md_report_content %}
-Full diff-cover report + 📋 Full diff-cover report +
{{ md_report_content }} @@ -38,4 +42,5 @@ {% endif %} --- -Posted by diff-cover-action + +🛡️ diff-cover-action · Updates on re-run diff --git a/templates/comment_quality.md.j2 b/templates/comment_quality.md.j2 index 0ecab87..f9b1261 100644 --- a/templates/comment_quality.md.j2 +++ b/templates/comment_quality.md.j2 @@ -1,28 +1,31 @@ -## {{ icon }} Diff Quality: {{ "%.1f" | format(report.total_percent_covered) }}% + +

🔬 Diff Quality Report

+ +**{{ "%.1f" | format(report.total_percent_covered) }}%** `{{ report.total_percent_covered | progress_bar }}` {% if fail_under > 0 %} {% if threshold_met %} -> :heavy_check_mark: Meets threshold of {{ "%.0f" | format(fail_under) }}% +> **✅ Passed** — meets threshold of {{ "%.0f" | format(fail_under) }}% {% else %} -> :x: Below threshold of {{ "%.0f" | format(fail_under) }}% — needs {{ "%.1f" | format(fail_under - report.total_percent_covered) }}% more quality coverage +> **❌ Failed** — below threshold of {{ "%.0f" | format(fail_under) }}% by {{ "%.1f" | format(fail_under - report.total_percent_covered) }}% {% endif %} {% endif %} -| Metric | Value | -|--------|------:| -| **Quality on diff lines** | **{{ "%.1f" | format(report.total_percent_covered) }}%** | -| Lines changed | {{ report.total_num_lines }} | -| Lines with violations | {{ report.total_num_violations }} | -| Files changed | {{ report.files | length }} | +| | Metric | Value | +|:---|:---|---:| +| 📏 | Lines in diff | **{{ report.total_num_lines }}** | +| ⚠️ | Lines with violations | **{{ report.total_num_violations }}** | +| 📁 | Files changed | **{{ report.files | length }}** | {% if report.files %}
-File breakdown ({{ report.files | length }} file{{ "s" if report.files | length != 1 else "" }}) + 📂 {{ report.files | length }} file{{ "s" if report.files | length != 1 else "" }} changed +
-| File | Quality | Violation Lines | -|------|:-------:|:---------------:| +| | File | Quality | Violation Lines | +|:---:|:---|---:|:---| {% for f in report.files | sort(attribute='percent_covered') %} -| `{{ f.path }}` | {{ "%.1f" | format(f.percent_covered) }}% | {{ f.violation_lines[:10] | join(', ') }}{% if f.violation_lines | length > 10 %} (+{{ f.violation_lines | length - 10 }} more){% endif %} | +| {{ f.percent_covered | status_icon }} | `{{ f.path }}` | {{ "%.1f" | format(f.percent_covered) }}% | {{ f.violation_lines[:10] | join(', ') if f.violation_lines else '—' }}{% if f.violation_lines | length > 10 %} (+{{ f.violation_lines | length - 10 }} more){% endif %} | {% endfor %}
@@ -30,7 +33,8 @@ {% if md_report_content %}
-Full diff-quality report + 📋 Full diff-quality report +
{{ md_report_content }} @@ -38,4 +42,5 @@ {% endif %} --- -Posted by diff-cover-action + +🛡️ diff-cover-action · Updates on re-run diff --git a/templates/step_summary.md.j2 b/templates/step_summary.md.j2 index 0c44b46..427eaf6 100644 --- a/templates/step_summary.md.j2 +++ b/templates/step_summary.md.j2 @@ -1,27 +1,29 @@ -## :{{ icon }}: Diff {{ "Coverage" if mode == "coverage" else "Quality" }}: {{ "%.1f" | format(report.total_percent_covered) }}% +

🛡️ Diff {{ "Coverage" if mode == "coverage" else "Quality" }}: {{ "%.1f" | format(report.total_percent_covered) }}%

+ +`{{ report.total_percent_covered | progress_bar }}` {% if fail_under > 0 %} {% if threshold_met %} -> :heavy_check_mark: Meets threshold of {{ "%.0f" | format(fail_under) }}% +> **✅ Passed** — meets threshold of {{ "%.0f" | format(fail_under) }}% {% else %} -> :x: Below threshold of {{ "%.0f" | format(fail_under) }}% +> **❌ Failed** — below threshold of {{ "%.0f" | format(fail_under) }}% {% endif %} {% endif %} -| Metric | Value | -|--------|------:| -| **{{ "Coverage" if mode == "coverage" else "Quality" }} on diff lines** | **{{ "%.1f" | format(report.total_percent_covered) }}%** | -| Lines changed | {{ report.total_num_lines }} | -| {{ "Lines uncovered" if mode == "coverage" else "Lines with violations" }} | {{ report.total_num_violations }} | -| Files changed | {{ report.files | length }} | +| | Metric | Value | +|:---|:---|---:| +| 📏 | Lines in diff | **{{ report.total_num_lines }}** | +| {{ "🔍" if mode == "coverage" else "⚠️" }} | {{ "Lines uncovered" if mode == "coverage" else "Lines with violations" }} | **{{ report.total_num_violations }}** | +| 📁 | Files changed | **{{ report.files | length }}** | {% if report.files %}
-File breakdown + 📂 File details +
-| File | {{ "Coverage" if mode == "coverage" else "Quality" }} | -|------|:--------:| +| | File | {{ "Coverage" if mode == "coverage" else "Quality" }} | +|:---:|:---|---:| {% for f in report.files | sort(attribute='percent_covered') %} -| `{{ f.path }}` | {{ "%.1f" | format(f.percent_covered) }}% | +| {{ f.percent_covered | status_icon }} | `{{ f.path }}` | {{ "%.1f" | format(f.percent_covered) }}% | {% endfor %}
diff --git a/tests/test_comment.py b/tests/test_comment.py index b21ccb0..0a7f134 100644 --- a/tests/test_comment.py +++ b/tests/test_comment.py @@ -95,7 +95,7 @@ def test_render_comment_body() -> None: ) assert "" in body assert "90.0%" in body - assert "Meets threshold" in body + assert "Passed" in body assert "src/foo.py" in body @@ -116,7 +116,7 @@ def test_render_comment_body_below_threshold() -> None: identifier="test", md_report_content="", ) - assert "Below threshold" in body + assert "Failed" in body @responses.activate From b82aabc8f8a3ff7514d2967905005edc7d2773ce Mon Sep 17 00:00:00 2001 From: Affan Amir Mir Date: Wed, 8 Apr 2026 19:39:22 +0500 Subject: [PATCH 2/3] =?UTF-8?q?ci:=20dogfood=20action=20on=20own=20PRs=20?= =?UTF-8?q?=E2=80=94=20enable=20comments=20and=20annotations?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add pull-requests: write and checks: write permissions - Enable post-comment and create-annotations on coverage mode job - The action now posts its own coverage report on every PR Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/workflows/integration-test.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/integration-test.yml b/.github/workflows/integration-test.yml index be1c23a..2ddff43 100644 --- a/.github/workflows/integration-test.yml +++ b/.github/workflows/integration-test.yml @@ -8,7 +8,8 @@ on: permissions: contents: read - pull-requests: read + pull-requests: write + checks: write jobs: test-coverage-mode: @@ -35,8 +36,8 @@ jobs: coverage-files: coverage.xml compare-branch: origin/main fail-under: '0' - post-comment: 'false' - create-annotations: 'false' + post-comment: 'true' + create-annotations: 'true' create-badge: 'true' test-quality-mode: From 9e5356eb38f3aae46a5abe4110c1fecc5fe5a65c Mon Sep 17 00:00:00 2001 From: Affan Amir Mir Date: Wed, 8 Apr 2026 20:34:23 +0500 Subject: [PATCH 3/3] feat: add shields.io badges and compact metric layout to PR comments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Hero coverage badge (for-the-badge style) with dynamic color - Threshold pass/fail badge (flat-square style) - Compact inline metrics replacing table layout - Badge color scale: brightgreen → green → yellowgreen → yellow → orange → red - Progress bar + badge combo for maximum visual density - Updated README previews and test assertions Co-Authored-By: Claude Opus 4.6 (1M context) --- README.md | 64 ++++++++++++++------------------ src/comment.py | 16 ++++++++ src/outputs.py | 16 ++++++++ templates/comment_coverage.md.j2 | 31 ++++++++-------- templates/comment_quality.md.j2 | 31 ++++++++-------- templates/step_summary.md.j2 | 21 +++++------ tests/test_comment.py | 4 +- 7 files changed, 104 insertions(+), 79 deletions(-) diff --git a/README.md b/README.md index 4b74f6e..245ffeb 100644 --- a/README.md +++ b/README.md @@ -60,25 +60,22 @@ Works with **any language** that can produce Cobertura XML, lcov, or JaCoCo cove ## What You Get -### PR Comment -- Passing +### PR Comment — Passing ->

🛡️ Diff Coverage Report

+>
+> 82.0% +>  passed > -> **82.0%** `████████████████░░░░` +> `████████████████░░░░` **82.0%** on changed lines +>
> -> > **✅ Passed** — meets threshold of 80% -> -> | | Metric | Value | -> |:---|:---|---:| -> | 📏 | Lines in diff | **50** | -> | 🔍 | Lines uncovered | **9** | -> | 📁 | Files changed | **3** | +> **50** lines changed   **9** uncovered   **3** files > >
>  📂 3 files changed >
> -> | | File | Coverage | Uncovered Lines | +> |   | File | Coverage | Uncovered Lines | > |:---:|:---|---:|:---| > | 🔴 | `src/bar.py` | 60.0% | 5, 6, 7, 8, 15, 22 | > | 🟡 | `src/foo.py` | 85.0% | 13, 27, 42 | @@ -86,28 +83,26 @@ Works with **any language** that can produce Cobertura XML, lcov, or JaCoCo cove > >
> -> --- -> 🛡️ diff-cover-action · Updates on re-run +> 🛡️ diff-cover-action -### PR Comment -- Failing +### PR Comment — Failing ->

🛡️ Diff Coverage Report

+>
+> 45.0% +>  failed > -> **45.0%** `█████████░░░░░░░░░░░` +> `█████████░░░░░░░░░░░` **45.0%** on changed lines +>
> -> > **❌ Failed** — below threshold of 80% by 35.0% +> > **80%** required — missing **35.0%** more coverage > -> | | Metric | Value | -> |:---|:---|---:| -> | 📏 | Lines in diff | **120** | -> | 🔍 | Lines uncovered | **66** | -> | 📁 | Files changed | **8** | +> **120** lines changed   **66** uncovered   **8** files > >
>  📂 8 files changed >
> -> | | File | Coverage | Uncovered Lines | +> |   | File | Coverage | Uncovered Lines | > |:---:|:---|---:|:---| > | 🔴 | `src/payments/stripe.py` | 0.0% | 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 (+12 more) | > | 🔴 | `src/auth/login.py` | 25.0% | 15, 16, 17, 30, 31, 32 | @@ -120,35 +115,32 @@ Works with **any language** that can produce Cobertura XML, lcov, or JaCoCo cove > >
> -> --- -> 🛡️ diff-cover-action · Updates on re-run +> 🛡️ diff-cover-action ### Diff Quality PR Comment ->

🔬 Diff Quality Report

+>
+> 75.0% +>  failed > -> **75.0%** `███████████████░░░░░` +> `███████████████░░░░░` **75.0%** on changed lines +>
> -> > **❌ Failed** — below threshold of 90% by 15.0% +> > **90%** required — missing **15.0%** more quality coverage > -> | | Metric | Value | -> |:---|:---|---:| -> | 📏 | Lines in diff | **20** | -> | ⚠️ | Lines with violations | **4** | -> | 📁 | Files changed | **1** | +> **20** lines changed   **4** violations   **1** file > >
>  📂 1 file changed >
> -> | | File | Quality | Violation Lines | +> |   | File | Quality | Violation Lines | > |:---:|:---|---:|:---| > | 🟡 | `src/module.py` | 75.0% | 10, 11, 12, 30 | > >
> -> --- -> 🛡️ diff-cover-action · Updates on re-run +> 🛡️ diff-cover-action ### Inline Annotations (appear directly on the PR diff) diff --git a/src/comment.py b/src/comment.py index 3ddf12a..642203a 100644 --- a/src/comment.py +++ b/src/comment.py @@ -116,6 +116,21 @@ def _status_icon(percent: float) -> str: return "\U0001f534" # 🔴 +def _badge_color(percent: float) -> str: + """Map a coverage percentage to a shields.io color name.""" + if percent >= 90: + return "brightgreen" + if percent >= 80: + return "green" + if percent >= 70: + return "yellowgreen" + if percent >= 60: + return "yellow" + if percent >= 40: + return "orange" + return "red" + + def _render_comment_body( *, report: Report, @@ -133,6 +148,7 @@ def _render_comment_body( ) env.filters["progress_bar"] = _progress_bar env.filters["status_icon"] = _status_icon + env.filters["badge_color"] = _badge_color template_name = f"comment_{mode}.md.j2" template = env.get_template(template_name) diff --git a/src/outputs.py b/src/outputs.py index 84e9400..2054234 100644 --- a/src/outputs.py +++ b/src/outputs.py @@ -64,6 +64,21 @@ def _status_icon(percent: float) -> str: return "\U0001f534" # 🔴 +def _badge_color(percent: float) -> str: + """Map a coverage percentage to a shields.io color name.""" + if percent >= 90: + return "brightgreen" + if percent >= 80: + return "green" + if percent >= 70: + return "yellowgreen" + if percent >= 60: + return "yellow" + if percent >= 40: + return "orange" + return "red" + + def write_step_summary( *, report: Report, @@ -79,6 +94,7 @@ def write_step_summary( ) env.filters["progress_bar"] = _progress_bar env.filters["status_icon"] = _status_icon + env.filters["badge_color"] = _badge_color template = env.get_template("step_summary.md.j2") content = template.render( diff --git a/templates/comment_coverage.md.j2 b/templates/comment_coverage.md.j2 index 0bb147e..98d618d 100644 --- a/templates/comment_coverage.md.j2 +++ b/templates/comment_coverage.md.j2 @@ -1,28 +1,31 @@ - -

🛡️ Diff Coverage Report

- -**{{ "%.1f" | format(report.total_percent_covered) }}%** `{{ report.total_percent_covered | progress_bar }}` +{% set pct = "%.1f" | format(report.total_percent_covered) %} +{% set color = report.total_percent_covered | badge_color %} +
+{{ pct }}% {% if fail_under > 0 %} {% if threshold_met %} -> **✅ Passed** — meets threshold of {{ "%.0f" | format(fail_under) }}% + passed {% else %} -> **❌ Failed** — below threshold of {{ "%.0f" | format(fail_under) }}% by {{ "%.1f" | format(fail_under - report.total_percent_covered) }}% + failed +{% endif %} {% endif %} + +`{{ report.total_percent_covered | progress_bar }}` **{{ pct }}%** on changed lines +
+ +{% if fail_under > 0 and not threshold_met %} +> **{{ "%.0f" | format(fail_under) }}%** required — missing **{{ "%.1f" | format(fail_under - report.total_percent_covered) }}%** more coverage {% endif %} -| | Metric | Value | -|:---|:---|---:| -| 📏 | Lines in diff | **{{ report.total_num_lines }}** | -| 🔍 | Lines uncovered | **{{ report.total_num_violations }}** | -| 📁 | Files changed | **{{ report.files | length }}** | +**{{ report.total_num_lines }}** lines changed   **{{ report.total_num_violations }}** uncovered   **{{ report.files | length }}** file{{ "s" if report.files | length != 1 else "" }} {% if report.files %}
 📂 {{ report.files | length }} file{{ "s" if report.files | length != 1 else "" }} changed
-| | File | Coverage | Uncovered Lines | +|   | File | Coverage | Uncovered Lines | |:---:|:---|---:|:---| {% for f in report.files | sort(attribute='percent_covered') %} | {{ f.percent_covered | status_icon }} | `{{ f.path }}` | {{ "%.1f" | format(f.percent_covered) }}% | {{ f.violation_lines[:10] | join(', ') if f.violation_lines else '—' }}{% if f.violation_lines | length > 10 %} (+{{ f.violation_lines | length - 10 }} more){% endif %} | @@ -41,6 +44,4 @@
{% endif %} ---- - -🛡️ diff-cover-action · Updates on re-run +🛡️ diff-cover-action diff --git a/templates/comment_quality.md.j2 b/templates/comment_quality.md.j2 index f9b1261..db12d32 100644 --- a/templates/comment_quality.md.j2 +++ b/templates/comment_quality.md.j2 @@ -1,28 +1,31 @@ - -

🔬 Diff Quality Report

- -**{{ "%.1f" | format(report.total_percent_covered) }}%** `{{ report.total_percent_covered | progress_bar }}` +{% set pct = "%.1f" | format(report.total_percent_covered) %} +{% set color = report.total_percent_covered | badge_color %} +
+{{ pct }}% {% if fail_under > 0 %} {% if threshold_met %} -> **✅ Passed** — meets threshold of {{ "%.0f" | format(fail_under) }}% + passed {% else %} -> **❌ Failed** — below threshold of {{ "%.0f" | format(fail_under) }}% by {{ "%.1f" | format(fail_under - report.total_percent_covered) }}% + failed +{% endif %} {% endif %} + +`{{ report.total_percent_covered | progress_bar }}` **{{ pct }}%** on changed lines +
+ +{% if fail_under > 0 and not threshold_met %} +> **{{ "%.0f" | format(fail_under) }}%** required — missing **{{ "%.1f" | format(fail_under - report.total_percent_covered) }}%** more quality coverage {% endif %} -| | Metric | Value | -|:---|:---|---:| -| 📏 | Lines in diff | **{{ report.total_num_lines }}** | -| ⚠️ | Lines with violations | **{{ report.total_num_violations }}** | -| 📁 | Files changed | **{{ report.files | length }}** | +**{{ report.total_num_lines }}** lines changed   **{{ report.total_num_violations }}** violations   **{{ report.files | length }}** file{{ "s" if report.files | length != 1 else "" }} {% if report.files %}
 📂 {{ report.files | length }} file{{ "s" if report.files | length != 1 else "" }} changed
-| | File | Quality | Violation Lines | +|   | File | Quality | Violation Lines | |:---:|:---|---:|:---| {% for f in report.files | sort(attribute='percent_covered') %} | {{ f.percent_covered | status_icon }} | `{{ f.path }}` | {{ "%.1f" | format(f.percent_covered) }}% | {{ f.violation_lines[:10] | join(', ') if f.violation_lines else '—' }}{% if f.violation_lines | length > 10 %} (+{{ f.violation_lines | length - 10 }} more){% endif %} | @@ -41,6 +44,4 @@
{% endif %} ---- - -🛡️ diff-cover-action · Updates on re-run +🛡️ diff-cover-action diff --git a/templates/step_summary.md.j2 b/templates/step_summary.md.j2 index 427eaf6..5dc8e08 100644 --- a/templates/step_summary.md.j2 +++ b/templates/step_summary.md.j2 @@ -1,26 +1,25 @@ -

🛡️ Diff {{ "Coverage" if mode == "coverage" else "Quality" }}: {{ "%.1f" | format(report.total_percent_covered) }}%

- -`{{ report.total_percent_covered | progress_bar }}` +{% set pct = "%.1f" | format(report.total_percent_covered) %} +{% set color = report.total_percent_covered | badge_color %} +{% set label = "diff_coverage" if mode == "coverage" else "diff_quality" %} +{{ pct }}% {% if fail_under > 0 %} {% if threshold_met %} -> **✅ Passed** — meets threshold of {{ "%.0f" | format(fail_under) }}% + passed {% else %} -> **❌ Failed** — below threshold of {{ "%.0f" | format(fail_under) }}% + failed {% endif %} {% endif %} -| | Metric | Value | -|:---|:---|---:| -| 📏 | Lines in diff | **{{ report.total_num_lines }}** | -| {{ "🔍" if mode == "coverage" else "⚠️" }} | {{ "Lines uncovered" if mode == "coverage" else "Lines with violations" }} | **{{ report.total_num_violations }}** | -| 📁 | Files changed | **{{ report.files | length }}** | +`{{ report.total_percent_covered | progress_bar }}` **{{ pct }}%** on changed lines + +**{{ report.total_num_lines }}** lines changed   **{{ report.total_num_violations }}** {{ "uncovered" if mode == "coverage" else "violations" }}   **{{ report.files | length }}** file{{ "s" if report.files | length != 1 else "" }} {% if report.files %}
 📂 File details
-| | File | {{ "Coverage" if mode == "coverage" else "Quality" }} | +|   | File | {{ "Coverage" if mode == "coverage" else "Quality" }} | |:---:|:---|---:| {% for f in report.files | sort(attribute='percent_covered') %} | {{ f.percent_covered | status_icon }} | `{{ f.path }}` | {{ "%.1f" | format(f.percent_covered) }}% | diff --git a/tests/test_comment.py b/tests/test_comment.py index 0a7f134..63758e5 100644 --- a/tests/test_comment.py +++ b/tests/test_comment.py @@ -95,7 +95,7 @@ def test_render_comment_body() -> None: ) assert "" in body assert "90.0%" in body - assert "Passed" in body + assert "threshold-passed-success" in body assert "src/foo.py" in body @@ -116,7 +116,7 @@ def test_render_comment_body_below_threshold() -> None: identifier="test", md_report_content="", ) - assert "Failed" in body + assert "threshold-failed-critical" in body @responses.activate