From 1f9eadc169c4c5a4f558a4a7f9e2c10e9ecfd266 Mon Sep 17 00:00:00 2001 From: David De Sloovere Date: Fri, 12 Jun 2026 06:28:06 +0000 Subject: [PATCH 1/6] feat: add devcontainer configuration files for Python development environment --- .devcontainer/devcontainer-lock.json | 19 +++++++++++++++++++ .devcontainer/devcontainer.json | 23 +++++++++++++++++++++++ .gitattributes | 1 + 3 files changed, 43 insertions(+) create mode 100644 .devcontainer/devcontainer-lock.json create mode 100644 .devcontainer/devcontainer.json create mode 100644 .gitattributes diff --git a/.devcontainer/devcontainer-lock.json b/.devcontainer/devcontainer-lock.json new file mode 100644 index 0000000..09f7eb0 --- /dev/null +++ b/.devcontainer/devcontainer-lock.json @@ -0,0 +1,19 @@ +{ + "features": { + "ghcr.io/devcontainers-extra/features/mypy:2": { + "version": "2.0.18", + "resolved": "ghcr.io/devcontainers-extra/features/mypy@sha256:534691ad5dc1dbdf2d2fe9cd541117758b8e381d56f1f3ad73bea12b93ba70c2", + "integrity": "sha256:534691ad5dc1dbdf2d2fe9cd541117758b8e381d56f1f3ad73bea12b93ba70c2" + }, + "ghcr.io/hspaans/devcontainer-features/pytest:2": { + "version": "2.0.0", + "resolved": "ghcr.io/hspaans/devcontainer-features/pytest@sha256:6ce02b88a2d4e894c9f4d3588df31723ed8978ed1da4c9561aec768d1aaf295b", + "integrity": "sha256:6ce02b88a2d4e894c9f4d3588df31723ed8978ed1da4c9561aec768d1aaf295b" + }, + "ghcr.io/jsburckhardt/devcontainer-features/ruff:1": { + "version": "1.0.0", + "resolved": "ghcr.io/jsburckhardt/devcontainer-features/ruff@sha256:ecde5b4c67565ea73aca624e13931c3104e01ca079b37913ba7317e0c7508d2f", + "integrity": "sha256:ecde5b4c67565ea73aca624e13931c3104e01ca079b37913ba7317e0c7508d2f" + } + } +} diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..a0c66f6 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,23 @@ +{ + "name": "Python Development", + "image": "ghcr.io/metinsenturk/devcontainer-templates/simple-python:latest", + "workspaceFolder": "/workspace", + "workspaceMount": "source=${localWorkspaceFolder},target=/workspace,type=bind,consistency=cached", + "remoteUser": "vscode", + + "customizations": { + "vscode": { + "extensions": [ + "ms-python.python", + "ms-python.isort", + "ms-python.autopep8", + "ms-python.mypy-type-checker" + ] + } + }, + "features": { + "ghcr.io/jsburckhardt/devcontainer-features/ruff:1": {}, + "ghcr.io/hspaans/devcontainer-features/pytest:2": {}, + "ghcr.io/devcontainers-extra/features/mypy:2": {} + } +} diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..6313b56 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto eol=lf From 025dd7cb6616ac7f5dd673f1881f7bf4f5519cdb Mon Sep 17 00:00:00 2001 From: David De Sloovere Date: Fri, 12 Jun 2026 06:30:29 +0000 Subject: [PATCH 2/6] docs: update CONTRIBUTING.md to mention dev container usage for Python setup --- CONTRIBUTING.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7d22549..7ca23d6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -25,6 +25,9 @@ ruff format src/ tests/ entrypoint.py mypy src/ entrypoint.py --ignore-missing-imports ``` +You can also use the *dev container* if you don't have a local Python environment set up. + + ## Project Structure ``` From de2036702a39a650ba6010633f773e01b1acceab Mon Sep 17 00:00:00 2001 From: David De Sloovere Date: Fri, 12 Jun 2026 06:36:59 +0000 Subject: [PATCH 3/6] fix: tables rendered with single new line Markdown tables not formatted correctly after header row Fixes #11 --- templates/comment_coverage.md.j2 | 4 ++-- templates/comment_quality.md.j2 | 4 ++-- templates/step_summary.md.j2 | 4 ++-- tests/test_comment.py | 2 ++ 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/templates/comment_coverage.md.j2 b/templates/comment_coverage.md.j2 index 4b9783c..a90ea7c 100644 --- a/templates/comment_coverage.md.j2 +++ b/templates/comment_coverage.md.j2 @@ -30,8 +30,8 @@
|   | File | Coverage | Uncovered Lines | -|:---:|:---|---:|:---| -{% for f in report.files | sort(attribute='percent_covered') %} +|:---:|:---|---:|:---| {% +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 %} | {% endfor %} diff --git a/templates/comment_quality.md.j2 b/templates/comment_quality.md.j2 index d11faea..bf7d510 100644 --- a/templates/comment_quality.md.j2 +++ b/templates/comment_quality.md.j2 @@ -30,8 +30,8 @@
|   | File | Quality | Violation Lines | -|:---:|:---|---:|:---| -{% for f in report.files | sort(attribute='percent_covered') %} +|:---:|:---|---:|:---| {% +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 %} | {% endfor %} diff --git a/templates/step_summary.md.j2 b/templates/step_summary.md.j2 index 5dc8e08..db06ad1 100644 --- a/templates/step_summary.md.j2 +++ b/templates/step_summary.md.j2 @@ -20,8 +20,8 @@
|   | File | {{ "Coverage" if mode == "coverage" else "Quality" }} | -|:---:|:---|---:| -{% for f in report.files | sort(attribute='percent_covered') %} +|:---:|:---|---:| {% +for f in report.files | sort(attribute='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 4c8ffa0..239cdad 100644 --- a/tests/test_comment.py +++ b/tests/test_comment.py @@ -98,6 +98,8 @@ def test_render_comment_body() -> None: assert "90.0%" in body assert "threshold-passed-success" in body assert "src/foo.py" in body + assert "|:---:|:---|---:|:---|\n| 🟡 | `src/foo.py` | 85.0% | 13, 27 |" in body + def test_render_comment_body_with_title() -> None: From 2a18b51141b597c172e90d08931a583b30288d03 Mon Sep 17 00:00:00 2001 From: David De Sloovere Date: Fri, 12 Jun 2026 06:38:20 +0000 Subject: [PATCH 4/6] fix space --- templates/comment_coverage.md.j2 | 2 +- templates/comment_quality.md.j2 | 2 +- templates/step_summary.md.j2 | 2 +- tests/test_comment.py | 1 - 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/templates/comment_coverage.md.j2 b/templates/comment_coverage.md.j2 index a90ea7c..d1e26fd 100644 --- a/templates/comment_coverage.md.j2 +++ b/templates/comment_coverage.md.j2 @@ -30,7 +30,7 @@
|   | 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 %} | {% endfor %} diff --git a/templates/comment_quality.md.j2 b/templates/comment_quality.md.j2 index bf7d510..57c8e6f 100644 --- a/templates/comment_quality.md.j2 +++ b/templates/comment_quality.md.j2 @@ -30,7 +30,7 @@
|   | 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 %} | {% endfor %} diff --git a/templates/step_summary.md.j2 b/templates/step_summary.md.j2 index db06ad1..f08559c 100644 --- a/templates/step_summary.md.j2 +++ b/templates/step_summary.md.j2 @@ -20,7 +20,7 @@
|   | 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) }}% | {% endfor %} diff --git a/tests/test_comment.py b/tests/test_comment.py index 239cdad..2091522 100644 --- a/tests/test_comment.py +++ b/tests/test_comment.py @@ -101,7 +101,6 @@ def test_render_comment_body() -> None: assert "|:---:|:---|---:|:---|\n| 🟡 | `src/foo.py` | 85.0% | 13, 27 |" in body - def test_render_comment_body_with_title() -> None: report = Report( report_name="XML", From 34df9f5fefd95514e068b3528b69b69168195cdf Mon Sep 17 00:00:00 2001 From: David De Sloovere Date: Fri, 12 Jun 2026 06:42:53 +0000 Subject: [PATCH 5/6] extra test --- tests/test_comment.py | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/tests/test_comment.py b/tests/test_comment.py index 2091522..3f70baf 100644 --- a/tests/test_comment.py +++ b/tests/test_comment.py @@ -74,7 +74,7 @@ def test_is_fork_pr_missing_file() -> None: assert _is_fork_pr("/nonexistent/event.json", "owner/repo") is False -def test_render_comment_body() -> None: +def test_render_comment_body_coverage() -> None: report = Report( report_name="XML", diff_name="origin/main...HEAD", @@ -100,6 +100,32 @@ def test_render_comment_body() -> None: assert "src/foo.py" in body assert "|:---:|:---|---:|:---|\n| 🟡 | `src/foo.py` | 85.0% | 13, 27 |" in body +def test_render_comment_body_quality() -> None: + report = Report( + report_name="XML", + diff_name="origin/main...HEAD", + files=[ + FileReport(path="src/foo.py", percent_covered=85.0, violation_lines=[13, 27]), + ], + total_num_lines=10, + total_num_violations=2, + total_percent_covered=95.0, + ) + body = _render_comment_body( + report=report, + mode="quality", + fail_under=80.0, + threshold_met=True, + identifier="test-id", + title="", + md_report_content="", + ) + assert "" in body + assert "95.0%" in body + assert "threshold-passed-success" in body + assert "src/foo.py" in body + assert "|:---:|:---|---:|:---|\n| 🟡 | `src/foo.py` | 85.0% | 13, 27 |" in body + def test_render_comment_body_with_title() -> None: report = Report( From 88c6fb3da3505e4c60ef04b7ea477c119b7feb6f Mon Sep 17 00:00:00 2001 From: David De Sloovere Date: Fri, 12 Jun 2026 06:43:27 +0000 Subject: [PATCH 6/6] format --- tests/test_comment.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_comment.py b/tests/test_comment.py index 3f70baf..f57e77a 100644 --- a/tests/test_comment.py +++ b/tests/test_comment.py @@ -100,6 +100,7 @@ def test_render_comment_body_coverage() -> None: assert "src/foo.py" in body assert "|:---:|:---|---:|:---|\n| 🟡 | `src/foo.py` | 85.0% | 13, 27 |" in body + def test_render_comment_body_quality() -> None: report = Report( report_name="XML",