Skip to content

Commit

Permalink
Use compare API to get reviewers
Browse files Browse the repository at this point in the history
  • Loading branch information
ryansingman committed Nov 1, 2022
1 parent 46f6957 commit 9105e42
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 111 deletions.
9 changes: 5 additions & 4 deletions action.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,12 @@ runs:
# get list of reviewers for PR
- run: |
git fetch \
&& echo "REVIEWERS=$( \
echo "REVIEWERS=$( \
GITHUB_TOKEN=${{ github.token }} \
python $GITHUB_ACTION_PATH/deploy_bot/get_reviewers.py \
--base-branch=origin/${{ env.DEPLOYMENT_BRANCH }} \
--target-branch=origin/${{ inputs.target }} \
--repository ${{ github.repository }} \
--head-branch=${{ env.DEPLOYMENT_BRANCH }} \
--base-branch=${{ inputs.target }} \
)" >> $GITHUB_ENV
shell: bash
Expand Down
135 changes: 28 additions & 107 deletions deploy_bot/get_reviewers.py
Original file line number Diff line number Diff line change
@@ -1,138 +1,59 @@
import re
import subprocess
from typing import Any, Dict, List
import warnings
import os
from typing import Dict, List, Set

import requests


GITHUB_TOKEN: str = os.getenv("GITHUB_TOKEN")

def find_contributors_to_branch(base_branch: str, target_branch: str) -> List[str]:
"""Finds authors of commits to base branch that are NOT in target branch.

:param base_branch: base branch of diff
:param target_branch: target branch of diff
:return: list of authors of commits in base branch but not in target
"""
commit_hashes: List[str] = find_added_commits(base_branch=base_branch, target_branch=target_branch)

return [find_author_of_commit(commit_hash) for commit_hash in commit_hashes]


def find_author_of_commit(commit_hash: str) -> str:
"""Finds GitHub username of author of commit.
:param commit_hash: commit hash to find author for
:return: GitHub username of author of commit hash
"""
# run `git log` to find author emails
command_output: subprocess.CompletedProcess = subprocess.run(
["git", "log", commit_hash, "-1", "--format='%ae'"],
capture_output=True,
check=True,
)

# parse author email from command output
author_email: List[str] = command_output.stdout.decode().splitlines()[0][1:-1]

# get and return GitHub usernames from emails
return get_github_username_from_email(author_email) or ""


def get_github_username_from_email(author_email: str) -> str:
"""Gets GitHub username from email, using GitHub API.
Note:
This raises a warning if a username cannot be found, or if multiple usernames are found.
:param author_email: email of author to query username for
:return: GitHub username corresponding to email (or None if more or less thann one username is found)
"""
gh_api_response: requests.Response = requests.get(
"https://api.github.com/search/users", params=dict(q=author_email),
)

assert gh_api_response.status_code == 200, "Bad response from GitHub users search API."

response_dict: Dict[str, Any] = gh_api_response.json()

# check that one username was returned
num_usernames: int = response_dict.get("total_count", 0)
if num_usernames == 0:
warnings.warn(f"No username found for email: {author_email} -- cannot tag reviewer.")
return None

elif num_usernames > 1:
warnings.warn(f"Multiple usernames found for email: {author_email} -- cannot tag reviewer.")
return None

# get and return username for email
return response_dict.get("items")[0]["login"]


def find_added_commits(base_branch: str, target_branch: str) -> List[str]:
"""Finds commits to base branch that are NOT in target branch.
def find_contributors_to_branch(repository: str, head_branch: str, base_branch: str) -> Set[str]:
"""Finds authors of commits to head branch that are NOT in base branch.
:param head_branch: head branch of diff
:param base_branch: base branch of diff
:param target_branch: target branch of diff
:return: commit hashes in base branch that are NOT in target
"""
# run `git cherry` to find commits
command_output: subprocess.CompletedProcess = subprocess.run(
["git", "cherry", target_branch, base_branch],
capture_output=True,
check=True,
)

# parse and return commit hashes from command output
return [
get_commit_hash_from_git_cherry_line(line)
for line in command_output.stdout.decode().splitlines()
if is_added_commit(line)
]


def get_commit_hash_from_git_cherry_line(line: str) -> str:
"""Parses git cherry line into commit hash.
:param line: line from git cherry output to parse
:return: commit hash
:return: set of authors of commits in head branch but not in base
"""
try:
return re.search(r"\b([0-9a-f]{5,40})\b", line).group(1)
except AttributeError:
raise RuntimeError("Could not parse commit hash from git cherry -- aborting.")
gh_api_response: requests.Response = requests.get(
f"https://api.github.com/repos/{repository}/compare/{base_branch}...{head_branch}",
headers={
"Authorization": f"Bearer {GITHUB_TOKEN}",
"Accept": "application/vnd.github+json",
}
)

assert gh_api_response.status_code == 200, f"Bad response from GitHub compare API {gh_api_response.url, gh_api_response.text}."

def is_added_commit(line: str) -> bool:
"""Returns true if line is of an added commit (starts with '+').
response_dict: Dict = gh_api_response.json()
commits: List[Dict] = response_dict.get("commits")

:param line: line to determine if is an addition
:return: true if line is an addition
"""
return line.startswith("+")
return {commit.get("author").get("login") for commit in commits}


if __name__ == "__main__":

import argparse

parser: argparse.ArgumentParser = argparse.ArgumentParser(prog="Get reviewers for PR")
parser.add_argument(
"--repository",
help="repository to get diff from",
)
parser.add_argument(
"--head-branch",
dest="head_branch",
help="head branch for comparison",
)
parser.add_argument(
"--base-branch",
dest="base_branch",
help="base branch for comparison",
)
parser.add_argument(
"--target-branch",
dest="target_branch",
help="target branch for comparison",
)

args = parser.parse_args()

# find contributors to branch
contributors: List[str] = find_contributors_to_branch(args.base_branch, args.target_branch)
contributors: Set[str] = find_contributors_to_branch(args.repository, args.head_branch, args.base_branch)

# output contributors as comma delimited list
print(",".join(contributors))
Expand Down

0 comments on commit 9105e42

Please sign in to comment.