6
mirror of https://github.com/AllSpiceIO/post-dr-comment.git synced 2025-03-28 20:16:53 +00:00

Initialize

This commit is contained in:
Shrikanth Upadhayaya 2024-09-05 12:49:01 -07:00
commit 656b43d1d7
No known key found for this signature in database
10 changed files with 396 additions and 0 deletions

10
.github/dependabot.yml vendored Normal file
View File

@ -0,0 +1,10 @@
version: 2
updates:
- package-ecosystem: pip
directory: /
schedule:
interval: monthly
- package-ecosystem: github-actions
directory: /
schedule:
interval: monthly

31
.github/workflows/test.yml vendored Normal file
View File

@ -0,0 +1,31 @@
name: Lint and test
on:
push:
branches: [main]
pull_request:
branches: ["**"]
jobs:
test:
name: Test
runs-on: ubuntu-22.04
strategy:
matrix:
python-version: ["3.10", "3.11", "3.12"]
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install -r requirements-test.txt
- name: Check formatting
run: ruff format --diff .
- name: Lint with ruff
run: ruff check --target-version=py310 .

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
.venv

8
Dockerfile Normal file
View File

@ -0,0 +1,8 @@
FROM python:3.12-bookworm
COPY requirements.txt /requirements.txt
COPY post_dr_comment.py /post_dr_comment.py
RUN pip install -r /requirements.txt
ENTRYPOINT [ "/post_dr_comment.py" ]

21
LICENSE.txt Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2024 AllSpice
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

87
README.md Normal file
View File

@ -0,0 +1,87 @@
# Post Comment on a Design Review
Post a comment on a Design Review using a markdown file as the source of the
comment on AllSpice Hub using
[AllSpice Actions](https://learn.allspice.io/docs/actions-cicd).
## Usage
Add the following step to your actions:
```yaml
- name: Post Comment on Design Review
uses: https://hub.allspice.io/Actions/post-dr-comment@v0.1
with:
# The path to the markdown file containing the comment body.
comment_path: path/to/comment.md
```
### Important Notes
1. This action works only when used in a workflow triggered by a Design Review,
as it will automatically pick up the associated design review.
2. By default, successive runs of the action will edit the same comment.
3. This action also reads YAML frontmatter from the markdown file to post
attachments to the posted comment.
### Customizing the Comment Content
The action uses a markdown file as the source of the comment body. You can
create a markdown file in your repository and specify its path using the
`comment_path` input.
Example `comment.md`:
```markdown
---
attachments:
- path/to/attachment1.png
- path/to/attachment2.pdf
---
# Comment Title
This is the body of the comment.
- Point 1
- Point 2
- Point 3
[Link to more information](https://example.com)
```
The YAML frontmatter at the beginning of the file (between `---`) can be used
to specify attachments that will be added to the comment. The YAML frontmatter
is optional, and when present, isn't included in the posted comment's body.
### Reusing Existing Comments
By default, the action will reuse the existing comment made by this action in
successive runs. This behavior can be controlled using the
`reuse_existing_comment` input. Set it to 'False' if you want to create a new
comment on each run.
### Debugging
If you encounter any issues or need more detailed information about the
action's execution, you can set the `log_level` input to 'DEBUG' for more
verbose logging.
## SSL
If your instance is running on a self-signed certificate, you can tell the
action to use your certificate by setting the `REQUESTS_CA_BUNDLE` environment
variable.
```yaml
- name: Post Comment on Design Review
uses: https://hub.allspice.io/Actions/post-dr-comment@v0.1
with:
comment_path: path/to/comment.md
env:
REQUESTS_CA_BUNDLE: /path/to/your/certificate.cert
```
For more information about AllSpice Actions and how to use them in your
workflows, please refer to the
[AllSpice Documentation](https://learn.allspice.io/docs/actions-cicd).

43
action.yml Normal file
View File

@ -0,0 +1,43 @@
name: "Post Comment on a Design Review"
description: >
Post a comment on a Design Review using a markdown file as the source of the
comment.
This works only when used in a workflow triggered by a Design Review, as it
will automatically pick up the associated design review. By default,
successive runs of the action will edit the same comment.
This action also reads YAML frontmatter from the markdown file to post
attachments to the posted comment.
inputs:
comment_path:
description: The path to a markdown file containing the comment body.
required: true
reuse_existing_comment:
description: Whether to reuse the existing comment made by this action in successive runs.
required: false
default: "True"
log_level:
description: The log level used by the action. Used for debugging.
required: false
default: "INFO"
runs:
using: "docker"
image: "Dockerfile"
args:
- "--allspice_hub_url"
- ${{ github.server_url }}
- "--repository"
- ${{ github.repository }}
- "--design-review-number"
- ${{ github.event.number }}
- "--comment-path"
- "${{ github.workspace}}/${{ inputs.comment_path }}"
- "--reuse-existing-comment"
- ${{ inputs.reuse_existing_comment }}
- "--log-level"
- ${{ inputs.log_level }}
env:
ALLSPICE_AUTH_TOKEN: ${{ github.token }}

191
post_dr_comment.py Normal file
View File

@ -0,0 +1,191 @@
"""
post_dr_comment.py: Post a comment to an AllSpice Hub Design Review.
"""
import argparse
import logging
import os
import yaml
from typing import Tuple
from allspice import AllSpice, Comment, DesignReview
COMMENT_IDENTIFIER = "<!-- AllSpice Hub Auto-DR Comment -->"
logger = logging.getLogger(__name__)
def parse_bool(input: str | bool) -> bool:
"""
Parse a YAML-like boolean string as a boolean.
"""
if isinstance(input, bool):
return input
if input.lower() in ("yes", "true", "t", "y", "1"):
return True
elif input.lower() in ("no", "false", "f", "n", "0"):
return False
else:
raise argparse.ArgumentTypeError(
"One of: yes, no, true, false, t, f, y, n, 1, 0 expected."
)
def parse_front_matter(comment_body: str) -> Tuple[dict, str]:
"""
Check if the comment body has a front matter and parse it.
Returns the front matter as a dictionary and the comment body without the
front matter.
If the front matter is empty or missing, the front matter dictionary will
be empty.
"""
front_matter = {}
found_front_matter = False
stripped_comment_body = comment_body.strip()
if stripped_comment_body.startswith("---"):
split_comment = stripped_comment_body.split("---", 2)
if len(split_comment) == 3:
found_front_matter = True
try:
front_matter = yaml.safe_load(split_comment[1])
comment_body = split_comment[2].lstrip()
found_front_matter = True
except yaml.YAMLError as e:
logger.error(f"Failed to parse front matter: {e}")
if not found_front_matter:
logger.info("No front matter found in comment body.")
return front_matter, comment_body
return front_matter, stripped_comment_body
def upsert_comment(design_review: DesignReview, comment_body: str) -> Comment:
"""
Upsert a comment on a Design Review.
If a comment with the same identifier already exists, update the existing
comment. Otherwise, create a new comment.
Returns the created or updated comment.
"""
existing_comment = None
comments = design_review.get_comments()
for comment in comments:
if COMMENT_IDENTIFIER in comment.body:
existing_comment = comment
break
if existing_comment:
logger.info("Updating existing comment.")
existing_comment.body = comment_body
existing_comment.commit()
return existing_comment
else:
logger.info("Creating new comment.")
return design_review.create_comment(comment_body)
def upsert_attachments(comment: Comment, attachments: list[str]):
"""
Upsert attachments to a comment.
This clears all existing attachments and then adds new attachments.
"""
existing_attachments = comment.get_attachments()
for attachment in existing_attachments:
comment.delete_attachment(attachment)
for attachment in attachments:
with open(attachment, "rb") as f:
comment.create_attachment(f)
def main():
parser = argparse.ArgumentParser(
description=__doc__,
)
parser.add_argument(
"--allspice-hub-url",
required=False,
default="https://hub.allspice.io",
help="The URL of the AllSpice Hub DR to post the comment to.",
)
parser.add_argument(
"--repository",
required=True,
help="The repository that the design review is associated with.",
)
parser.add_argument(
"--design-review-number",
required=True,
help="The number of the design review to post the comment to.",
)
parser.add_argument(
"--comment-path",
required=True,
help="The path to the Comment Markdown file.",
)
parser.add_argument(
"--reuse-existing-comment",
required=False,
default=True,
type=parse_bool,
help="Whether to reuse an existing comment if it exists.",
)
parser.add_argument(
"--log-level",
required=False,
default="INFO",
help="The logging level to use.",
)
token = os.getenv("ALLSPICE_AUTH_TOKEN")
if not token:
raise ValueError("ALLSPICE_AUTH_TOKEN environment variable not set.")
args = parser.parse_args()
logger.setLevel(args.log_level.upper())
client = AllSpice(
args.allspice_hub_url,
token_text=token,
log_level=args.log_level.upper(),
)
owner, repo = args.repository.split("/")
design_review = DesignReview.request(client, owner, repo, args.design_review_number)
with open(args.comment_path, "r") as f:
comment_body = f.read()
front_matter, comment_body = parse_front_matter(comment_body)
attachments = []
if front_matter:
logger.debug(f"Front matter: {front_matter}")
if "attachments" in front_matter:
attachments = front_matter["attachments"]
if args.reuse_existing_comment:
comment = upsert_comment(design_review, comment_body)
else:
comment = design_review.create_comment(comment_body)
if attachments:
upsert_attachments(comment, attachments)
logger.info("Comment posted successfully.")
if __name__ == "__main__":
main()

1
requirements-test.txt Normal file
View File

@ -0,0 +1 @@
ruff==0.6.3

3
requirements.txt Normal file
View File

@ -0,0 +1,3 @@
py-allspice~=3.5.0
PyYAML~=6.0.2