Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: show github-annotation format #164

Merged
merged 10 commits into from
Apr 29, 2023
Merged
2 changes: 2 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## Unreleased

* pycobertura show now support output format: github-annotation

## 3.0.0 (2022-10-08)

* BACKWARD INCOMPATIBLE:
Expand Down
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,18 @@ total:

```

The following shows how to generate GitHub annotations given a coverage file.

```shell
$ pycobertura show --format github-annotation tests/cobertura.xml
::error file=dummy/dummy.py,line=5,endLine=6,title=pycobertura::not covered
::error file=dummy/dummy4.py,line=1,endLine=6,title=pycobertura::not covered
```

If you run it in GitHub Actions/Apps, the above log generates check annotations.

![Example output of github-annotation formatted pycobertura show command](images/example_github_annotation_show.png)

### Command `diff`

You can also use the `diff` command to show the difference between two coverage
Expand Down
Binary file added images/example_github_annotation_show.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions pycobertura/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from pycobertura.cobertura import Cobertura
from pycobertura.reporters import (
GitHubAnnotationReporter,
HtmlReporter,
TextReporter,
CsvReporter,
Expand All @@ -28,6 +29,7 @@
"markdown": MarkdownReporter,
"json": JsonReporter,
"yaml": YamlReporter,
"github-annotation": GitHubAnnotationReporter,
}


Expand Down
48 changes: 47 additions & 1 deletion pycobertura/reporters.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from jinja2 import Environment, PackageLoader
from pycobertura.cobertura import CoberturaDiff
from pycobertura.utils import green, red, stringify
from pycobertura.utils import green, red, stringify, rangify
from pycobertura.templates import filters
from tabulate import tabulate
from ruamel import yaml
Expand Down Expand Up @@ -443,3 +443,49 @@ def generate(self):
)

return template.render(**render_kwargs)


class GitHubAnnotationReporter(Reporter):
def __init__(
self,
cobertura,
ignore_regex=None,
annotation_level: str = "error",
title: str = "pycobertura",
message: str = "not covered",
):
super().__init__(cobertura, ignore_regex)
self.annotation_level = annotation_level
self.title = title
self.message = message

def generate(self):
file_names = self.cobertura.files(ignore_regex=self.ignore_regex)
result_strs = []
for file_name in file_names:
for range_start, range_end in rangify(
self.cobertura.missed_lines(file_name)
):
result_strs.append(
self.to_github_annotation_message(
file_name=file_name,
start_line_num=range_start,
end_line_num=range_end,
annotation_level=self.annotation_level,
title=self.title,
message=self.message,
)
)
result = "\n".join(result_strs)
return result

@staticmethod
def to_github_annotation_message(
file_name: str,
start_line_num: int,
end_line_num: int,
annotation_level: str,
title: str,
message: str,
):
return f"::{annotation_level} file={file_name},line={start_line_num},endLine={end_line_num},title={title}::{message}" # noqa
34 changes: 34 additions & 0 deletions tests/test_reporters.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import pytest

from .utils import make_cobertura


Expand Down Expand Up @@ -661,3 +663,35 @@ def test_html_report_delta__show_missing_False():
</div>
</body>
</html>"""

@pytest.mark.parametrize(
"report, expected_default_output, expected_custom_output",
[
(
"tests/cobertura.xml",
"""\
::error file=search/BinarySearch.java,line=24,endLine=24,title=pycobertura::not covered
::error file=search/LinearSearch.java,line=19,endLine=24,title=pycobertura::not covered""",
"""\
::notice file=search/BinarySearch.java,line=24,endLine=24,title=JCov::missing coverage
::notice file=search/LinearSearch.java,line=19,endLine=24,title=JCov::missing coverage""",
),
(
"tests/cobertura-generated-by-istanbul-from-coffeescript.xml",
"::error file=app.coffee,line=10,endLine=10,title=pycobertura::not covered",
"::notice file=app.coffee,line=10,endLine=10,title=JCov::missing coverage",
),
],
)
def test_github_annotation_report(report, expected_default_output, expected_custom_output):
from pycobertura.reporters import GitHubAnnotationReporter

cobertura = make_cobertura(report)
default_report = GitHubAnnotationReporter(cobertura)

assert default_report.generate() == expected_default_output
custom_report = GitHubAnnotationReporter(
cobertura, annotation_level="notice", title="JCov", message="missing coverage"
)

assert custom_report.generate() == expected_custom_output