pr_labeler: add nag comment for issues and PRs without description (#353)

* pr_labeler: add issue member to IssueLabelerCtx

Pull requests also support the Github issue API. This allows more easily
writing code that works with both issues and pull requests without extra
conditionals.

* pr_labeler: add dependency on jinja2

* pr_labeler: comment on PRs/issues w/o descriptions

This change adds a job to pr_labeler to leave a comment on PRs and
issues without body text.

It also includes necessary plumbing for rendering jinja2 templates and
idempotently leaving comments on issues.

Fixes: https://github.com/ansible/ansible-documentation/issues/333

* pr_labeler: improve nag comment wording

Co-authored-by: Don Naro <dnaro@redhat.com>

---------

Co-authored-by: Don Naro <dnaro@redhat.com>
This commit is contained in:
Maxwell G
2023-09-05 14:28:24 -05:00
committed by GitHub
parent 3ee73a85d6
commit 9e32d15f29
4 changed files with 62 additions and 4 deletions

View File

@@ -0,0 +1,7 @@
Thanks for your contribution, @{{ ctx.member.user.login }}! Please make sure that your {{ ctx.TYPE }} includes sufficient and meaningful details in the description.
{% if ctx.TYPE == "pull request" %}
PR descriptions provide important context and allow other developers and our future selves to understand a change's rationale and what it actually fixes or accomplishes.
{% else %}
Issue descriptions are important so others can fully understand and reproduce the issue.
{% endif %}
<!--- boilerplate: no_body_nag --->

View File

@@ -10,7 +10,7 @@ from collections.abc import Collection
from contextlib import suppress
from functools import cached_property
from pathlib import Path
from typing import Any, Union
from typing import Any, ClassVar, Union
import github
import github.Auth
@@ -19,6 +19,7 @@ import github.PullRequest
import github.Repository
import typer
from codeowners import CodeOwners, OwnerTuple
from jinja2 import Environment, FileSystemLoader, StrictUndefined, select_autoescape
OWNER = "ansible"
REPO = "ansible-documentation"
@@ -28,6 +29,12 @@ LABELS_BY_CODEOWNER: dict[OwnerTuple, list[str]] = {
HERE = Path(__file__).resolve().parent
ROOT = HERE.parent.parent
CODEOWNERS = (ROOT / ".github/CODEOWNERS").read_text("utf-8")
JINJA2_ENV = Environment(
loader=FileSystemLoader(HERE / "data"),
autoescape=select_autoescape(),
trim_blocks=True,
undefined=StrictUndefined,
)
IssueOrPrCtx = Union["IssueLabelerCtx", "PRLabelerCtx"]
IssueOrPr = Union["github.Issue.Issue", "github.PullRequest.PullRequest"]
@@ -62,6 +69,9 @@ class LabelerCtx:
repo: github.Repository.Repository
dry_run: bool
event_info: dict[str, Any]
issue: github.Issue.Issue
TYPE: ClassVar[str]
@property
def member(self) -> IssueOrPr:
@@ -90,6 +100,8 @@ class LabelerCtx:
class IssueLabelerCtx(LabelerCtx):
issue: github.Issue.Issue
TYPE = "issue"
@property
def member(self) -> IssueOrPr:
return self.issue
@@ -103,6 +115,8 @@ class IssueLabelerCtx(LabelerCtx):
class PRLabelerCtx(LabelerCtx):
pr: github.PullRequest.PullRequest
TYPE = "pull request"
@property
def member(self) -> IssueOrPr:
return self.pr
@@ -121,11 +135,31 @@ def create_comment(ctx: IssueOrPrCtx, body: str) -> None:
ctx.pr.create_issue_comment(body)
def get_data_file(name: str) -> str:
def get_data_file(name: str, **kwargs: Any) -> str:
"""
Get a data file
Template a data file
"""
return (HERE / "data" / name).read_text("utf-8")
return JINJA2_ENV.get_template(name).render(**kwargs).rstrip("\n")
def create_boilerplate_comment(ctx: IssueOrPrCtx, name: str, **kwargs) -> None:
"""
Add a boilerplate comment if it hasn't already been added
"""
tmpl = get_data_file(name, ctx=ctx, **kwargs)
tmpl_lines = tmpl.splitlines()
last = tmpl_lines[-1]
if not (last.startswith("<!--- boilerplate: ") and last.endswith(" --->")):
raise ValueError(
"Last line must of the template"
" must have an identifying boilerplate comment"
)
for comment in ctx.issue.get_comments():
if comment.body.splitlines()[-1] == last:
log(ctx, name, "boilerplate was already commented")
return
log(ctx, "Templating", name, "boilerplate")
create_comment(ctx, tmpl)
def handle_codeowner_labels(ctx: PRLabelerCtx) -> None:
@@ -174,6 +208,15 @@ def new_contributor_welcome(ctx: IssueOrPrCtx) -> None:
create_comment(ctx, get_data_file("docs_team_info.md"))
def no_body_nag(ctx: IssueOrPrCtx) -> None:
"""
Complain if a non-bot user creates a PR or issue without body text
"""
if ctx.member.user.login.endswith("[bot]") or (ctx.member.body or "").strip():
return
create_boilerplate_comment(ctx, "no_body_nag.md")
APP = typer.Typer()
@@ -200,6 +243,7 @@ def process_pr(
pr=pr,
dry_run=dry_run,
event_info=get_event_info(),
issue=pr.as_issue(),
)
if pr.state != "open":
log(ctx, "Refusing to process closed ticket")
@@ -208,6 +252,7 @@ def process_pr(
handle_codeowner_labels(ctx)
add_label_if_new(ctx, "needs_triage")
new_contributor_welcome(ctx)
no_body_nag(ctx)
@APP.command(name="issue")
@@ -233,6 +278,7 @@ def process_issue(
add_label_if_new(ctx, "needs_triage")
new_contributor_welcome(ctx)
no_body_nag(ctx)
if __name__ == "__main__":

View File

@@ -1,3 +1,4 @@
codeowners
jinja2
pygithub
typer

View File

@@ -30,6 +30,10 @@ filelock==3.12.3
# via virtualenv
idna==3.4
# via requests
jinja2==3.1.2
# via -r tests/../hacking/pr_labeler/requirements.txt
markupsafe==2.1.3
# via jinja2
mypy==1.5.1
# via -r tests/typing.in
mypy-extensions==1.0.0