Skip to content

Commit

Permalink
Merge pull request #213 from py-cov-action/monorepo
Browse files Browse the repository at this point in the history
Add SUBPROJECT_ID setting
  • Loading branch information
kieferro authored Aug 15, 2023
2 parents 705239c + 07b9b64 commit 64aebb0
Show file tree
Hide file tree
Showing 17 changed files with 267 additions and 37 deletions.
115 changes: 111 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ jobs:
### Basic usage without external contributors

If you don't expect external contributors, you don't need all the shenanigans
with the artifacts and the 2nd workflow. This is likely to be the most straightfoward
with the artifacts and the 2nd workflow. This is likely to be the most straightforward
way to configure it for private repositories. It might look like this:

```yaml
Expand Down Expand Up @@ -198,7 +198,6 @@ jobs:
run: make test # This is the part where you put your own test command

- name: Coverage comment
id: coverage_comment
uses: py-cov-action/python-coverage-comment-action@v3
with:
GITHUB_TOKEN: ${{ github.token }}
Expand Down Expand Up @@ -327,14 +326,25 @@ jobs:
COMMENT_ARTIFACT_NAME: python-coverage-comment-action

# Name of the file in which the body of the comment to post on the PR is stored.
# You typically don't have to change this unless you're already using this name for something else.
# In monorepo setting, see SUBPROJECT_ID.
COMMENT_FILENAME: python-coverage-comment-action.txt

# This setting is only necessary if you plan to run the action multiple times
# in the same repository. It will be appended to the value of all the
# settings that need to be unique, so as for the action to avoid mixing
# up results of multiple runs.
# Affects `COMMENT_FILENAME`, `COVERAGE_DATA_BRANCH`.
# Ideally, use dashes (`-`) rather than underscrores (`_`) to split words,
# for consistency
SUBPROJECT_ID: null / "lib-name"

# An alternative template for the comment for pull requests. See details below.
COMMENT_TEMPLATE: The coverage rate is `{{ coverage.info.percent_covered | pct }}`{{ marker }}

# Name of the branch in which coverage data will be stored on the repository.
# Please make sure that this branch is not protected.
# Default is 'python-coverage-comment-action-data'. Please make sure that this
# branch is not protected.
# In monorepo setting, see SUBPROJECT_ID.
COVERAGE_DATA_BRANCH: python-coverage-comment-action-data

# Deprecated, see https://docs.github.com/en/actions/monitoring-and-troubleshooting-workflows/enabling-debug-logging
Expand Down Expand Up @@ -383,6 +393,103 @@ coverage (percentage) of the whole project from the PR build:
"Coverage: {{ coverage.info.percent_covered | pct }}{{ marker }}"
```

## Monorepo setting

In case you want to use the action multiple times with different parts of your
source (so you have multiple codebases into a single repo), you'll
need to use SUBPROJECT_ID with a different value for each launch. You may
still use the same step for storing all files as artifacts. You'll end up with
a different comment for each launch. Feel free to use the `COMMENT_TEMPLATE` if
you want each comment to clearly state what it relates to.

```yaml
# .github/workflows/ci.yml
name: CI
on:
pull_request:
push:
branches:
- "main"
jobs:
test:
name: Run tests & display coverage
runs-on: ubuntu-latest
permissions:
pull-requests: write
contents: write
steps:
- uses: actions/checkout@v3
- name: Test project 1
run: make -C project_1 test
- name: Test project 2
run: make -C project_2 test
- name: Coverage comment (project 1)
id: coverage_comment_1
uses: py-cov-action/python-coverage-comment-action@v3
with:
COVERAGE_PATH: project_1
SUBPROJECT_ID: project-1
GITHUB_TOKEN: ${{ github.token }}
- name: Coverage comment (project 2)
id: coverage_comment_2
uses: py-cov-action/python-coverage-comment-action@v3
with:
COVERAGE_PATH: project_2/src
SUBPROJECT_ID: project-2
GITHUB_TOKEN: ${{ github.token }}
- name: Store Pull Request comment to be posted
uses: actions/upload-artifact@v3
if: steps.coverage_comment_1.outputs.COMMENT_FILE_WRITTEN == 'true' || steps.coverage_comment_2.outputs.COMMENT_FILE_WRITTEN == 'true'
with:
name: python-coverage-comment-action
# Note the star
path: python-coverage-comment-action*.txt
```

```yaml
# .github/workflows/coverage.yml
name: Post coverage comment
on:
workflow_run:
workflows: ["CI"]
types:
- completed
jobs:
test:
name: Run tests & display coverage
runs-on: ubuntu-latest
if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success'
permissions:
pull-requests: write
contents: write
actions: read
steps:
- name: Post comment
uses: py-cov-action/python-coverage-comment-action@v3
with:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_PR_RUN_ID: ${{ github.event.workflow_run.id }}
SUBPROJECT_ID: project-1
COVERAGE_PATH: project_1
- name: Post comment
uses: py-cov-action/python-coverage-comment-action@v3
with:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_PR_RUN_ID: ${{ github.event.workflow_run.id }}
SUBPROJECT_ID: project-2
COVERAGE_PATH: project_2/src
```

# Other topics

## Pinning
Expand Down
23 changes: 20 additions & 3 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ inputs:
Name of the branch in which coverage data will be stored on the repository.
Default is 'python-coverage-comment-action-data'. Please make sure that this
branch is not protected.
In monorepo setting, see SUBPROJECT_ID.
default: python-coverage-comment-action-data
required: false
COVERAGE_PATH:
Expand All @@ -38,13 +39,25 @@ inputs:
COMMENT_ARTIFACT_NAME:
description: >
Name of the artifact in which the body of the comment to post on the PR is stored.
You typically don't have to change this unless you're already using this name for something else.
default: python-coverage-comment-action
required: false
COMMENT_FILENAME:
description: >
Name of the file in which the body of the comment to post on the PR is stored.
In monorepo setting, see SUBPROJECT_ID.
default: python-coverage-comment-action.txt
required: false
SUBPROJECT_ID:
description: >
This setting is only necessary if you plan to run the action multiple
times in the same repository. It will be appended to the value of all the
settings that need to be unique, so as for the action to avoid mixing up
results of multiple runs. Ideally, use dashes (`-`) rather than
underscrores (`_`) to split words, for consistency.
Affects `COMMENT_FILENAME`, `COVERAGE_DATA_BRANCH`.
default: null
required: false
MINIMUM_GREEN:
description: >
If the coverage percentage is above or equal to this value, the badge
Expand All @@ -67,7 +80,7 @@ inputs:
default: false
ANNOTATION_TYPE:
description: >
Only needed if ANNOTATE_MISSING_LINES is set to true. This parameter allows you to choose between
Only relevant if ANNOTATE_MISSING_LINES is set to true. This parameter allows you to choose between
notice, warning and error as annotation type. For more information look here:
https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#setting-a-notice-message
default: warning
Expand All @@ -78,8 +91,11 @@ inputs:
outputs:
COMMENT_FILE_WRITTEN:
description: >
If "true", a comment file was written to COMMENT_FILENAME. If "false",
no comment file was written (because the comment was already posted).
This output is only set when running in PR mode. It's a boolean indicating
whether a comment file was written to COMMENT_FILENAME or not. If so,
you'll need to run the action in workflow_run mode to post it. If
"false", no comment file was written (likely because the comment was
already posted to the PR).
runs:
using: docker
image: Dockerfile
Expand All @@ -91,6 +107,7 @@ runs:
COVERAGE_PATH: ${{ inputs.COVERAGE_PATH }}
COMMENT_ARTIFACT_NAME: ${{ inputs.COMMENT_ARTIFACT_NAME }}
COMMENT_FILENAME: ${{ inputs.COMMENT_FILENAME }}
SUBPROJECT_ID: ${{ inputs.SUBPROJECT_ID }}
MINIMUM_GREEN: ${{ inputs.MINIMUM_GREEN }}
MINIMUM_ORANGE: ${{ inputs.MINIMUM_ORANGE }}
MERGE_COVERAGE_FILES: ${{ inputs.MERGE_COVERAGE_FILES }}
Expand Down
3 changes: 3 additions & 0 deletions coverage_comment/communication.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ def get_readme_and_log(
html_report_url: str,
markdown_report: str,
is_public: bool,
subproject_id: str | None = None,
) -> tuple[files.WriteFile, str]:
readme_markdown = template.get_readme_markdown(
is_public=is_public,
Expand All @@ -18,6 +19,7 @@ def get_readme_and_log(
direct_image_url=image_urls["direct"],
endpoint_image_url=image_urls["endpoint"],
dynamic_image_url=image_urls["dynamic"],
subproject_id=subproject_id,
)
log_message = template.get_log_message(
is_public=is_public,
Expand All @@ -26,6 +28,7 @@ def get_readme_and_log(
direct_image_url=image_urls["direct"],
endpoint_image_url=image_urls["endpoint"],
dynamic_image_url=image_urls["dynamic"],
subproject_id=subproject_id,
)
readme = files.WriteFile(
path=pathlib.Path("README.md"),
Expand Down
2 changes: 1 addition & 1 deletion coverage_comment/github.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ def get_pr_number_from_workflow_run(
raise CannotDeterminePR(f"No open PR found for branch {full_branch}")


def get_my_login(github: github_client.GitHub):
def get_my_login(github: github_client.GitHub) -> str:
try:
response = github.user.get()
except github_client.Forbidden:
Expand Down
22 changes: 13 additions & 9 deletions coverage_comment/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,19 +120,22 @@ def generate_comment(
previous_coverage_data_file = storage.get_datafile_contents(
github=gh,
repository=config.GITHUB_REPOSITORY,
branch=config.COVERAGE_DATA_BRANCH,
branch=config.FINAL_COVERAGE_DATA_BRANCH,
)
previous_coverage = None
if previous_coverage_data_file:
previous_coverage = files.parse_datafile(contents=previous_coverage_data_file)

marker = template.get_marker(marker_id=config.SUBPROJECT_ID)
try:
comment = template.get_comment_markdown(
coverage=coverage,
diff_coverage=diff_coverage,
previous_coverage_rate=previous_coverage,
base_template=template.read_template_file("comment.md.j2"),
custom_template=config.COMMENT_TEMPLATE,
marker=marker,
subproject_id=config.SUBPROJECT_ID,
)
except template.MissingMarker:
log.error(
Expand Down Expand Up @@ -167,7 +170,7 @@ def generate_comment(
repository=config.GITHUB_REPOSITORY,
pr_number=config.GITHUB_PR_NUMBER,
contents=comment,
marker=template.MARKER,
marker=marker,
)
except github.CannotPostComment:
log.debug("Exception when posting comment", exc_info=True)
Expand All @@ -178,7 +181,7 @@ def generate_comment(
"on PR comments for external PRs)."
)
comment_file.store_file(
filename=config.COMMENT_FILENAME,
filename=config.FINAL_COMMENT_FILENAME,
content=comment,
)
github.set_output(github_output=config.GITHUB_OUTPUT, COMMENT_FILE_WRITTEN=True)
Expand Down Expand Up @@ -224,7 +227,7 @@ def post_comment(config: settings.Config, github_session: httpx.Client) -> int:
repository=config.GITHUB_REPOSITORY,
artifact_name=config.COMMENT_ARTIFACT_NAME,
run_id=config.GITHUB_PR_RUN_ID,
filename=config.COMMENT_FILENAME,
filename=config.FINAL_COMMENT_FILENAME,
)
except github.NoArtifact:
log.info(
Expand All @@ -240,7 +243,7 @@ def post_comment(config: settings.Config, github_session: httpx.Client) -> int:
repository=config.GITHUB_REPOSITORY,
pr_number=pr_number,
contents=comment,
marker=template.MARKER,
marker=template.get_marker(marker_id=config.SUBPROJECT_ID),
)
log.info("Comment posted in PR")

Expand Down Expand Up @@ -294,14 +297,14 @@ def save_coverage_data_files(
storage.get_raw_file_url,
is_public=is_public,
repository=config.GITHUB_REPOSITORY,
branch=config.COVERAGE_DATA_BRANCH,
branch=config.FINAL_COVERAGE_DATA_BRANCH,
)
readme_url = storage.get_repo_file_url(
branch=config.COVERAGE_DATA_BRANCH,
branch=config.FINAL_COVERAGE_DATA_BRANCH,
repository=config.GITHUB_REPOSITORY,
)
html_report_url = storage.get_html_report_url(
branch=config.COVERAGE_DATA_BRANCH,
branch=config.FINAL_COVERAGE_DATA_BRANCH,
repository=config.GITHUB_REPOSITORY,
)
readme_file, log_message = communication.get_readme_and_log(
Expand All @@ -310,12 +313,13 @@ def save_coverage_data_files(
image_urls=files.get_urls(url_getter=url_getter),
html_report_url=html_report_url,
markdown_report=markdown_report,
subproject_id=config.SUBPROJECT_ID,
)
operations.append(readme_file)
storage.commit_operations(
operations=operations,
git=git,
branch=config.COVERAGE_DATA_BRANCH,
branch=config.FINAL_COVERAGE_DATA_BRANCH,
)

log.info(log_message)
Expand Down
15 changes: 15 additions & 0 deletions coverage_comment/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ class Config:
COVERAGE_PATH: pathlib.Path = pathlib.Path(".")
COMMENT_ARTIFACT_NAME: str = "python-coverage-comment-action"
COMMENT_FILENAME: pathlib.Path = pathlib.Path("python-coverage-comment-action.txt")
SUBPROJECT_ID: str | None = None
GITHUB_OUTPUT: pathlib.Path | None = None
MINIMUM_GREEN: decimal.Decimal = decimal.Decimal("100")
MINIMUM_ORANGE: decimal.Decimal = decimal.Decimal("70")
Expand Down Expand Up @@ -119,6 +120,20 @@ def GITHUB_PR_NUMBER(self) -> int | None:
return int(self.GITHUB_REF.split("/")[2])
return None

@property
def FINAL_COMMENT_FILENAME(self):
filename = self.COMMENT_FILENAME
if self.SUBPROJECT_ID:
new_name = f"{filename.stem}-{self.SUBPROJECT_ID}{filename.suffix}"
return filename.parent / new_name
return filename

@property
def FINAL_COVERAGE_DATA_BRANCH(self):
return self.COVERAGE_DATA_BRANCH + (
f"-{self.SUBPROJECT_ID}" if self.SUBPROJECT_ID else ""
)

# We need to type environ as a MutableMapping because that's what
# os.environ is, and just saying `dict[str, str]` is not enough to make
# mypy happy
Expand Down
Loading

0 comments on commit 64aebb0

Please sign in to comment.