From 8f7d0aa7f257a9e16612b1cc51600819e85793b2 Mon Sep 17 00:00:00 2001 From: Pawel Lebioda Date: Mon, 3 Aug 2026 21:57:20 +0200 Subject: [PATCH 1/3] Add code coverage reporting to CI Measure test coverage with pytest-cov and surface it on GitHub without any external service. The Test workflow now records coverage and a separate workflow_run workflow posts/updates a coverage comment on pull requests, which keeps write permissions working for fork PRs. Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/coverage.yml | 28 ++++++++++++++++++++++++++++ .github/workflows/test.yml | 21 ++++++++++++++++++++- pyproject.toml | 9 ++++++++- 3 files changed, 56 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/coverage.yml diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml new file mode 100644 index 0000000..4c7a8c0 --- /dev/null +++ b/.github/workflows/coverage.yml @@ -0,0 +1,28 @@ +name: Coverage comment + +# Runs after the Test workflow finishes and posts / updates the coverage +# comment on the pull request. This lives in a separate workflow_run workflow +# so it has write permissions even for pull requests opened from forks (which +# only get a read-only token in the Test workflow itself). + +on: + workflow_run: + workflows: ["Test"] + types: + - completed + +jobs: + post-comment: + name: Post coverage comment + runs-on: ubuntu-latest + if: github.event.workflow_run.event == 'pull_request' + permissions: + pull-requests: write + contents: write + actions: read + steps: + - name: Post coverage comment + uses: py-cov-action/python-coverage-comment-action@v3 + with: + GITHUB_TOKEN: ${{ github.token }} + GITHUB_PR_RUN_ID: ${{ github.event.workflow_run.id }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b177e2e..33d742a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -9,6 +9,12 @@ jobs: pytest: name: Pytest runs-on: ubuntu-latest + permissions: + # Needed on push to master so the action can store coverage data and the + # badge on its data branch. Pull requests from forks get a read-only + # token regardless; the comment is posted by the separate coverage + # workflow (coverage.yml). + contents: write steps: - uses: actions/checkout@v4 @@ -21,4 +27,17 @@ jobs: run: pip install -e ".[dev]" - name: Run pytest - run: pytest + run: pytest --cov=mergai --cov-report=term-missing + + - name: Coverage comment + id: coverage_comment + uses: py-cov-action/python-coverage-comment-action@v3 + with: + GITHUB_TOKEN: ${{ github.token }} + + - name: Store coverage comment for the reporting workflow + uses: actions/upload-artifact@v4 + if: steps.coverage_comment.outputs.COMMENT_FILE_WRITTEN == 'true' + with: + name: python-coverage-comment-action + path: python-coverage-comment-action.txt diff --git a/pyproject.toml b/pyproject.toml index 2219975..69a3a0e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -44,11 +44,18 @@ dev = [ "pip-audit>=2.7.0", "deptry>=0.12.0", "pytest>=7.0.0", + "pytest-cov>=5.0.0", ] [tool.pytest.ini_options] testpaths = ["tests"] +[tool.coverage.run] +# relative_files is required by python-coverage-comment-action so the +# recorded paths match across the CI checkout and the reporting workflow. +relative_files = true +source = ["mergai"] + [tool.black] line-length = 88 target-version = ['py310'] @@ -98,4 +105,4 @@ exclude = [ [tool.deptry] # Ignore dev tools that are CLI-based and not imported in code [tool.deptry.per_rule_ignores] -DEP002 = ["pre-commit", "ruff", "mypy", "types-PyYAML", "pip-audit", "deptry", "pytest"] \ No newline at end of file +DEP002 = ["pre-commit", "ruff", "mypy", "types-PyYAML", "pip-audit", "deptry", "pytest", "pytest-cov"] \ No newline at end of file From 6b3e030fbdb0e6f93f012497013da491c15fee01 Mon Sep 17 00:00:00 2001 From: Pawel Lebioda Date: Tue, 4 Aug 2026 08:37:29 +0200 Subject: [PATCH 2/3] Post coverage comment directly from the Test workflow Grant the Test job pull-requests: write so the coverage action can post the comment on the same run, and drop the separate workflow_run workflow. This gives immediate feedback on same-repo pull requests. Fork PRs receive a read-only token from GitHub and will not get a comment. Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/coverage.yml | 28 ---------------------------- .github/workflows/test.yml | 17 +++++------------ 2 files changed, 5 insertions(+), 40 deletions(-) delete mode 100644 .github/workflows/coverage.yml diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml deleted file mode 100644 index 4c7a8c0..0000000 --- a/.github/workflows/coverage.yml +++ /dev/null @@ -1,28 +0,0 @@ -name: Coverage comment - -# Runs after the Test workflow finishes and posts / updates the coverage -# comment on the pull request. This lives in a separate workflow_run workflow -# so it has write permissions even for pull requests opened from forks (which -# only get a read-only token in the Test workflow itself). - -on: - workflow_run: - workflows: ["Test"] - types: - - completed - -jobs: - post-comment: - name: Post coverage comment - runs-on: ubuntu-latest - if: github.event.workflow_run.event == 'pull_request' - permissions: - pull-requests: write - contents: write - actions: read - steps: - - name: Post coverage comment - uses: py-cov-action/python-coverage-comment-action@v3 - with: - GITHUB_TOKEN: ${{ github.token }} - GITHUB_PR_RUN_ID: ${{ github.event.workflow_run.id }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 33d742a..04d66cc 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -10,11 +10,12 @@ jobs: name: Pytest runs-on: ubuntu-latest permissions: - # Needed on push to master so the action can store coverage data and the - # badge on its data branch. Pull requests from forks get a read-only - # token regardless; the comment is posted by the separate coverage - # workflow (coverage.yml). + # contents: write lets the action store coverage data and the badge on + # its data branch (on push to master). pull-requests: write lets it post + # the coverage comment on pull requests. Note: fork PRs get a read-only + # token from GitHub regardless, so they will not receive a comment. contents: write + pull-requests: write steps: - uses: actions/checkout@v4 @@ -30,14 +31,6 @@ jobs: run: pytest --cov=mergai --cov-report=term-missing - name: Coverage comment - id: coverage_comment uses: py-cov-action/python-coverage-comment-action@v3 with: GITHUB_TOKEN: ${{ github.token }} - - - name: Store coverage comment for the reporting workflow - uses: actions/upload-artifact@v4 - if: steps.coverage_comment.outputs.COMMENT_FILE_WRITTEN == 'true' - with: - name: python-coverage-comment-action - path: python-coverage-comment-action.txt From c918c4c97e1815d78dc72569ef96c1c48fb58dbf Mon Sep 17 00:00:00 2001 From: Pawel Lebioda Date: Tue, 4 Aug 2026 08:45:04 +0200 Subject: [PATCH 3/3] Add coverage badge to README The badge and its linked HTML report are served from the python-coverage-comment-action-data branch, which the coverage action creates on the first push to master. Co-Authored-By: Claude Opus 4.8 (1M context) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2a68b59..e3f0c0b 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # mergai -[![Format Check](https://github.com/Percona-Lab/mergai/actions/workflows/format-check.yml/badge.svg)](https://github.com/Percona-Lab/mergai/actions/workflows/format-check.yml) [![Lint](https://github.com/Percona-Lab/mergai/actions/workflows/lint.yml/badge.svg)](https://github.com/Percona-Lab/mergai/actions/workflows/lint.yml) [![Dependency Check](https://github.com/Percona-Lab/mergai/actions/workflows/deps-check.yml/badge.svg)](https://github.com/Percona-Lab/mergai/actions/workflows/deps-check.yml) [![Security Scan](https://github.com/Percona-Lab/mergai/actions/workflows/security.yml/badge.svg)](https://github.com/Percona-Lab/mergai/actions/workflows/security.yml) +[![Format Check](https://github.com/Percona-Lab/mergai/actions/workflows/format-check.yml/badge.svg)](https://github.com/Percona-Lab/mergai/actions/workflows/format-check.yml) [![Lint](https://github.com/Percona-Lab/mergai/actions/workflows/lint.yml/badge.svg)](https://github.com/Percona-Lab/mergai/actions/workflows/lint.yml) [![Dependency Check](https://github.com/Percona-Lab/mergai/actions/workflows/deps-check.yml/badge.svg)](https://github.com/Percona-Lab/mergai/actions/workflows/deps-check.yml) [![Security Scan](https://github.com/Percona-Lab/mergai/actions/workflows/security.yml/badge.svg)](https://github.com/Percona-Lab/mergai/actions/workflows/security.yml) [![Coverage](https://raw.githubusercontent.com/Percona-Lab/mergai/python-coverage-comment-action-data/badge.svg)](https://htmlpreview.github.io/?https://github.com/Percona-Lab/mergai/blob/python-coverage-comment-action-data/htmlcov/index.html) A CLI tool for AI-assisted merge conflict resolution, designed for maintaining long-running forks that regularly sync with upstream repositories.