-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add module for checking news file in a specific pull request
- Loading branch information
1 parent
aa8bc6f
commit 99dbd36
Showing
7 changed files
with
287 additions
and
1 deletion.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Binary file not shown.
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
""" | ||
Module for checking if the user has made a news file for the specific pull request. | ||
Slight modifications have been made to support our project. | ||
Original Source: https://github.com/python/bedevere/blob/master/LICENSE | ||
Copyright 2017 The Python Software Foundation | ||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
https://www.apache.org/licenses/LICENSE-2.0 | ||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
""" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
import functools | ||
import pathlib | ||
import re | ||
from typing import Any, Dict | ||
|
||
import gidgethub.routing | ||
from gidgethub import sansio | ||
from gidgethub.abc import GitHubAPI | ||
|
||
from ..utils import load_toml_config | ||
from . import utils | ||
|
||
|
||
router = gidgethub.routing.Router() | ||
create_status = functools.partial(utils.create_status, "Check News") | ||
|
||
CONFIG = load_toml_config() | ||
SECTIONS = [_type for _type, _ in CONFIG.get("types").items()] | ||
CHANGELOG_IT_URL = "TODO: URL TO CHANGE-LOGGING PR SECTION IN README" | ||
FILENAME_RE = re.compile( | ||
r"^\d{4}-(0?[1-9]|1[012])-(0?[1-9]|[12][0-9]|3[01])$\." # match `yyyy-mm-dd` or `yyyy-m-d` | ||
r"pr-\d+(?:,\d+)*\." # Issue number(s) | ||
fr"({'|'.join(SECTIONS)})\." # Section type | ||
r"[A-Za-z0-9_=-]+\." # Nonce (URL-safe base64) | ||
r"md", # File extension""" | ||
re.VERBOSE, | ||
) | ||
|
||
SKIP_LABEL_STATUS = create_status(utils.StatusState.SUCCESS, description='"skip changelog" label found') | ||
|
||
|
||
async def check_news(gh: GitHubAPI, pull_request: Dict[str, Any]) -> None: | ||
""" | ||
Check for a news entry. | ||
The routing is handled through the filepath module. | ||
""" | ||
files = await utils.files_for_pr(gh, pull_request) | ||
in_next_dir = file_found = False | ||
|
||
for file in files: | ||
if not utils.is_news_dir(file["file_name"]): | ||
continue | ||
in_next_dir = True | ||
file_path = pathlib.PurePath(file["file_name"]) | ||
if len(file_path.parts) != 3: # news, next, <entry> | ||
continue | ||
file_found = True | ||
if FILENAME_RE.match(file_path.name) and len(file["patch"]) >= 1: | ||
status = create_status( | ||
utils.StatusState.SUCCESS, description=f"News entry found in {utils.NEWS_NEXT_DIR}" | ||
) | ||
break | ||
else: | ||
issue = await utils.issue_for_pr(gh, pull_request) | ||
if utils.skip(issue): | ||
status = SKIP_LABEL_STATUS | ||
else: | ||
if not in_next_dir: | ||
description = f'No news entry in {utils.NEWS_NEXT_DIR} or "skip news" label found' | ||
elif not file_found: | ||
description = "News entry not in an appropriate directory" | ||
else: | ||
description = "News entry file name incorrectly formatted" | ||
status = create_status( | ||
utils.StatusState.FAILURE, description=description, target_url=CHANGELOG_IT_URL | ||
) | ||
|
||
await gh.post(pull_request["statuses_url"], data=status) | ||
|
||
|
||
@router.register("pull_request", action="labeled") | ||
async def label_added(event: sansio.Event, gh: GitHubAPI, *args, **kwargs) -> None: | ||
if utils.label_name(event.data) == utils.SKIP_NEWS_LABEL: | ||
await utils.post_status(gh, event, SKIP_LABEL_STATUS) | ||
|
||
|
||
@router.register("pull_request", action="unlabeled") | ||
async def label_removed(event: sansio.Event, gh: GitHubAPI, *args, **kwargs) -> None: | ||
if utils.no_labels(event.data): | ||
return | ||
elif utils.label_name(event.data) == utils.SKIP_NEWS_LABEL: | ||
pull_request = event.data["pull_request"] | ||
await check_news(gh, pull_request) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
import enum | ||
import sys | ||
from typing import Any, Dict, List | ||
|
||
from gidgethub.abc import GitHubAPI | ||
|
||
|
||
NEWS_NEXT_DIR = "news/next/" | ||
SKIP_NEWS_LABEL = "skip changelog" | ||
|
||
|
||
class StatusState(enum.Enum): | ||
SUCCESS = "success" | ||
ERROR = "error" | ||
FAILURE = "failure" | ||
|
||
|
||
def create_status( | ||
context: str, state: StatusState, *, description: str = None, target_url: str = None | ||
) -> dict: | ||
""" | ||
Create the data for a status. | ||
The argument order is such that you can use functools.partial() to set the | ||
context to avoid repeatedly specifying it throughout a module. | ||
""" | ||
status = { | ||
"context": context, | ||
"state": state.value, | ||
} | ||
if description is not None: | ||
status["description"] = description | ||
if target_url is not None: | ||
status["target_url"] = target_url | ||
|
||
return status | ||
|
||
|
||
async def post_status(gh: GitHubAPI, event, status: Any) -> None: | ||
"""Post a status in reaction to an event.""" | ||
await gh.post(event.data["pull_request"]["statuses_url"], data=status) | ||
|
||
|
||
def skip(issue: Dict[str, Any]) -> bool: | ||
"""See if an issue has a "SKIP_NEWS_LABEL" label.""" | ||
return SKIP_NEWS_LABEL in {label_data["name"] for label_data in issue["labels"]} | ||
|
||
|
||
def label_name(event_data: Dict[str, Any]) -> str: | ||
"""Get the label name from a label-related webhook event.""" | ||
return event_data["label"]["name"] | ||
|
||
|
||
async def files_for_pr(gh: GitHubAPI, pull_request: Dict[str, Any]) -> List[Dict[str, Any]]: | ||
"""Get files for a pull request.""" | ||
# For some unknown reason there isn't any files URL in a pull request payload. | ||
files_url = f'{pull_request["url"]}/files' | ||
data = [] | ||
async for filedata in gh.getiter(files_url): | ||
data.append({"file_name": filedata["filename"], "patch": filedata.get("patch", "")}) | ||
return data | ||
|
||
|
||
async def issue_for_pr(gh: GitHubAPI, pull_request: Dict[str, Any]) -> Any: | ||
"""Get the issue data for a pull request.""" | ||
return await gh.getitem(pull_request["issue_url"]) | ||
|
||
|
||
def is_news_dir(filename: str) -> bool: | ||
"""Return True if file is in the News directory.""" | ||
return filename.startswith(NEWS_NEXT_DIR) | ||
|
||
|
||
def no_labels(event_data: Dict[str, Any]) -> bool: | ||
if "label" not in event_data: | ||
print( | ||
"no 'label' key in payload; " "'unlabeled' event triggered by label deletion?", | ||
file=sys.stderr, | ||
) | ||
return True | ||
else: | ||
return False |