Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
SHELL := /bin/bash

.PHONY: setup install install-dev lint test run clean-setup clean-lint all clean
.PHONY: setup install dev lint test run clean-setup clean-lint all clean

setup: install-dev
setup: dev
uv run pre-commit install
uv run pre-commit install --hook-type commit-msg

Expand Down
17 changes: 4 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,6 @@ Permissions needed for the Github Token:
`Pull requests:read`
`Pull requests:write`

If you have given `ANNOTATIONS_DATA_BRANCH` branch then Github Token also requires content write permissions.
Read more on how to use this [document](./docs/annotations.md).

`Contents:read`
`Contents:write`

**install:**

```bash
Expand All @@ -36,8 +30,8 @@ codecov
- `GITHUB_REPOSITORY`: The name of the GitHub repository where the action is running.
- `COVERAGE_PATH`: The path to the coverage report file. (JSON format)
- `GITHUB_TOKEN`: The GitHub token used for authentication.
- `GITHUB_PR_NUMBER`: The number of the pull request where the action is running. (Optional)
- `GITHUB_REF`: The branch to run the action on. If not provided, it will be used to get the PR number. (Optional)
- `GITHUB_PR_NUMBER`: The number of the pull request where the coverage report comment to be generated. (Optional)
- `GITHUB_REF`: The branch name if pr number is not provided, it will be used to get the PR number. (Optional)

Note: Either `GITHUB_PR_NUMBER` or `GITHUB_REF` is required. `GITHUB_PR_NUMBER` takes precedence if both mentioned.

Expand All @@ -46,9 +40,6 @@ Note: Either `GITHUB_PR_NUMBER` or `GITHUB_REF` is required. `GITHUB_PR_NUMBER`
- `MINIMUM_GREEN`: The minimum coverage percentage for green status. Default is 100.
- `MINIMUM_ORANGE`: The minimum coverage percentage for orange status. Default is 70.
- `BRANCH_COVERAGE`: Show branch coverage in the report. Default is False.
- `SKIP_COVERAGE`: Skip coverage reporting as github comment and generate only annotaions. Default is False.
- `ANNOTATIONS_DATA_BRANCH`: The branch to store the annotations. Read more about this [document](./docs/annotations.md).
- `ANNOTATIONS_OUTPUT_PATH`: The path where the annotaions should be stored. Should be a path to folder.
- `ANNOTATE_MISSING_LINES`: Whether to annotate missing lines in the coverage report. Default is False.
- `ANNOTATION_TYPE`: The type of annotation to use for missing lines. 'notice' or 'warning' or 'error'. Default is 'warning'.
- `MAX_FILES_IN_COMMENT`: The maximum number of files to include in the coverage report comment. Default is 25.
Expand All @@ -68,7 +59,7 @@ Note: Either `GITHUB_PR_NUMBER` or `GITHUB_REF` is required. `GITHUB_PR_NUMBER`
project coverage, this may be expected. For consistent results, it is recommended to enable branch
coverage when your report includes it.

## Setting up Local Environment using uv
## Dev Setup

To get started, follow these steps:

Expand Down Expand Up @@ -109,7 +100,7 @@ To get started, follow these steps:

---
> **NOTE:**
> This project is almost copy of
> This project is inspired from
> [py-cov-action/python-coverage-comment-action](<https://github.com/py-cov-action/python-coverage-comment-action.git>),
> [LICENSE](<https://github.com/py-cov-action/python-coverage-comment-action/blob/main/LICENSE>) with few modifications.
---
5 changes: 0 additions & 5 deletions codecov/badge.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
"""
This module should contain only the things relevant to the badge being computed
by shields.io
"""

import decimal
import urllib.parse

Expand Down
20 changes: 0 additions & 20 deletions codecov/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,8 @@ class Config:
TEST_FRAMEWORK: TestFramework = TestFramework.PYTEST
# TODO: Remove branch coverage and just use the report
BRANCH_COVERAGE: bool = False
SKIP_COVERAGE: bool = False # Deprecated
ANNOTATE_MISSING_LINES: bool = False
ANNOTATION_TYPE: AnnotationType = AnnotationType.WARNING
ANNOTATIONS_OUTPUT_PATH: pathlib.Path | None = None
ANNOTATIONS_DATA_BRANCH: str | None = None
MAX_FILES_IN_COMMENT: int = 25
SKIP_COVERED_FILES_IN_REPORT: bool = True
COMPLETE_PROJECT_REPORT: bool = False
Expand All @@ -69,12 +66,6 @@ def __post_init__(self) -> None:
if self.GITHUB_PR_NUMBER is None and self.GITHUB_REF is None:
raise ValueError('Either GITHUB_PR_NUMBER or GITHUB_REF must be provided')

if self.SKIP_COVERAGE and not self.ANNOTATE_MISSING_LINES:
raise ValueError(
'No action taken as both SKIP_COVERAGE and ANNOTATE_MISSING_LINES are set to False. \
Neither comments nor annotations will be generated.'
)

# Clean methods
@classmethod
def clean_minimum_green(cls, value: str) -> decimal.Decimal:
Expand All @@ -88,10 +79,6 @@ def clean_minimum_orange(cls, value: str) -> decimal.Decimal:
def clean_annotate_missing_lines(cls, value: str) -> bool:
return str_to_bool(value)

@classmethod
def clean_skip_coverage(cls, value: str) -> bool:
return str_to_bool(value)

@classmethod
def clean_branch_coverage(cls, value: str) -> bool:
return str_to_bool(value)
Expand Down Expand Up @@ -124,13 +111,6 @@ def clean_max_files_in_comment(cls, value: str) -> int:
def clean_coverage_path(cls, value: str) -> pathlib.Path:
return resolve_path(value)

@classmethod
def clean_annotations_output_path(cls, value: str) -> pathlib.Path:
path = pathlib.Path(value)
if path.exists() or path.is_dir():
return path
raise ValueError

@classmethod
def clean_test_framework(cls, value: str) -> TestFramework:
return TestFramework(value)
Expand Down
9 changes: 4 additions & 5 deletions codecov/coverage/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,12 @@
import json
import pathlib
from abc import ABC, abstractmethod
from typing import Any
from typing import Any, ClassVar

from codecov.config import Config, TestFramework
from codecov.exceptions import ConfigurationException
from codecov.log import log

COVERAGE_HANDLER_REGISTRY: dict[TestFramework, type['BaseCoverageHandler']] = {}


@dataclasses.dataclass
class FileDiffCoverage:
Expand All @@ -35,9 +33,10 @@ class DiffCoverage:

class BaseCoverageHandler(ABC):
TEST_FRAMEWORK: TestFramework
REGISTRY: ClassVar[dict[TestFramework, type['BaseCoverageHandler']]] = {}

def __init_subclass__(cls) -> None:
COVERAGE_HANDLER_REGISTRY[cls.TEST_FRAMEWORK] = cls
cls.REGISTRY[cls.TEST_FRAMEWORK] = cls
super().__init_subclass__()

def convert_to_decimal(self, value: float | decimal.Decimal, precision: int = 2) -> decimal.Decimal:
Expand Down Expand Up @@ -86,7 +85,7 @@ def get_diff_coverage(
@classmethod
def get_coverage_handler(cls, test_framework: TestFramework) -> type['BaseCoverageHandler']:
try:
return COVERAGE_HANDLER_REGISTRY[test_framework]
return cls.REGISTRY[test_framework]
except KeyError as exc:
log.error('No coverage handler found for test framework: %s', test_framework.value)
raise ConfigurationException from exc
4 changes: 0 additions & 4 deletions codecov/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,6 @@ class CannotGetUser(GithubBaseException):
pass


class CannotGetBranch(GithubBaseException):
pass


class CannotPostComment(GithubBaseException):
pass

Expand Down
102 changes: 1 addition & 101 deletions codecov/github.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,16 @@

from codecov.exceptions import (
ApiError,
CannotGetBranch,
CannotGetPullRequest,
CannotGetUser,
CannotPostComment,
Conflict,
Forbidden,
NotFound,
Unauthorized,
ValidationFailed,
)
from codecov.github_client import GitHubClient
from codecov.groups import Annotation
from codecov.log import log

COMMIT_MESSAGE = 'Update annotations data'


@dataclasses.dataclass
class User:
Expand All @@ -29,17 +23,9 @@ class User:


class Github:
def __init__( # pylint: disable=too-many-arguments, too-many-positional-arguments
self,
client: GitHubClient,
repository: str,
pr_number: int | None = None,
ref: str | None = None,
annotations_data_branch: str = None,
):
def __init__(self, client: GitHubClient, repository: str, pr_number: int | None = None, ref: str | None = None):
self.client = client
self.repository: str = repository
self.annotations_data_branch: str | None = annotations_data_branch

self.user: User = self._init_user()
self.pr_number, self.base_ref = self._init_pr_number(pr_number=pr_number, ref=ref)
Expand Down Expand Up @@ -188,92 +174,6 @@ def post_comment(self, contents: str, marker: str) -> None:
)
raise CannotPostComment from exc

def write_annotations_to_branch(self, annotations: list[Annotation]) -> None:
if not self.annotations_data_branch:
log.debug('No annotations data branch provided. Exiting.')
return

log.debug('Getting the annotations data branch.')
try:
data_branch = self.client.repos(self.repository).branches(self.annotations_data_branch).get()
if data_branch.protected:
log.debug('Branch "%s/%s" is protected.', self.repository, self.annotations_data_branch)
raise NotFound
except Forbidden as exc:
log.error(
'Insufficient permissions to write annotations to the branch "%s/%s". Please verify the token permissions and ensure it has content read and write access.',
self.repository,
self.annotations_data_branch,
)
raise CannotGetBranch from exc
except NotFound as exc:
log.error(
'Branch "%s/%s" either does not exist or is protected.', self.repository, self.annotations_data_branch
)
raise CannotGetBranch from exc

log.info('Writing annotations to branch.')
file_name = f'{self.pr_number}-annotations.json'
file_sha: str | None = None
try:
file = self.client.repos(self.repository).contents(file_name).get(ref=self.annotations_data_branch)
file_sha = file.sha
except NotFound:
log.debug(
'File "%s" does not exist in branch "%s/%s", creating new file.',
file_name,
self.repository,
self.annotations_data_branch,
)
except Forbidden as exc:
log.error(
'Insufficient permissions to write annotations to the branch "%s/%s". Please verify the token permissions and ensure it has content read and write access.',
self.repository,
self.annotations_data_branch,
)
raise CannotGetBranch from exc

try:
log.debug('Writing annotations to file to branch.')
encoded_content = Annotation.encode(annotations)
self.client.repos(self.repository).contents(file_name).put(
message=COMMIT_MESSAGE,
branch=self.annotations_data_branch,
sha=file_sha,
committer={
'name': self.user.name,
'email': self.user.email,
},
content=encoded_content,
)
except NotFound as exc:
log.error(
'Branch "%s/%s" either does not exist or is protected.', self.repository, self.annotations_data_branch
)
raise CannotGetBranch from exc
except Forbidden as exc:
log.error(
'Insufficient permissions to write annotations to the branch "%s/%s". Please verify the token permissions and ensure it has content read and write access.',
self.repository,
self.annotations_data_branch,
)
raise CannotGetBranch from exc
except Conflict as exc:
log.error(
'Conflict while adding #%s pull request annotation to branch "%s/%s".',
self.pr_number,
self.repository,
self.annotations_data_branch,
)
raise CannotGetBranch from exc
except ValidationFailed as exc:
log.error(
'Validation failed for committer name or email, or the endpoint was spammed while writing annotation to branch "%s/%s".',
self.repository,
self.annotations_data_branch,
)
raise CannotGetBranch from exc


class GithubDiffParser:
def __init__(self, diff: str):
Expand Down
14 changes: 0 additions & 14 deletions codecov/groups.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
import base64
import dataclasses
import functools
import itertools
import json
import pathlib
from collections.abc import Iterable
from typing import Self


@dataclasses.dataclass(frozen=True)
Expand All @@ -15,13 +12,6 @@ class Group:
line_end: int


class AnnotationEncoder(json.JSONEncoder):
def default(self, o):
if isinstance(o, Annotation):
return o.to_dict()
return super().default(o)


@dataclasses.dataclass
class Annotation:
file: pathlib.Path
Expand All @@ -47,10 +37,6 @@ def to_dict(self):
'message': self.message,
}

@classmethod
def encode(cls, annotations: list[Self]) -> str:
return base64.b64encode(json.dumps(annotations, cls=AnnotationEncoder).encode()).decode()


def create_missing_coverage_annotations(
annotation_type: str,
Expand Down
Loading
Loading