Integrate with the github project board
Implement a feature allowing the bot to move issues around on the project board: * When a change's commit message includes "WIP" or "DNM", the issue will be moved to the "In Progress" column. * When a change's commit message does *not* include "WIP" or "DNM", assume that the change is ready for review, and move it to the "Submitted on Gerrit" column.
This commit is contained in:
parent
09fa974779
commit
727e8eb44d
1
AUTHORS
1
AUTHORS
@ -1,2 +1,3 @@
|
||||
Ian H Pittwood <pittwoodian@gmail.com>
|
||||
Ian Pittwood <pittwoodian@gmail.com>
|
||||
Ian Howell <ian.howell0@gmail.com>
|
||||
|
@ -39,10 +39,12 @@ def main():
|
||||
'2. Check associated Github Issue for a link to the change. If no such link exists, comment it.\n'
|
||||
'3. If the associated issue was closed, re-open it and comment on it describing why it was '
|
||||
're-opened and a link to the Gerrit change that was found.\n'
|
||||
'4. If the Gerrit change\'s commit message contains a "WIP" or "DNM" tag, add the "wip" label and '
|
||||
'to the issue remove other process labels such as "ready for review".\n'
|
||||
'4. If the Gerrit change\'s commit message contains a "WIP" or "DNM" tag, add the "wip" label '
|
||||
'to the issue, remove other process labels (e.g. "ready for review"), and move the issue '
|
||||
'to the "In Progress" column of the project board.\n'
|
||||
'5. If no "WIP" or "DNM" tag is found in the change\'s commit message, add the "ready for review" '
|
||||
'label to the issue and remove other process labels such as "ready for review".',
|
||||
'label to the issue, remove other process labels (e.g "wip"), and move the issue '
|
||||
'to the "Submitted on Gerrit" column of the project board.',
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter
|
||||
)
|
||||
parser.add_argument('-g', '--gerrit-url', action='store', required=True, type=str,
|
||||
@ -70,8 +72,9 @@ def main():
|
||||
default=False, help='Enabled DEBUG level logging.')
|
||||
parser.add_argument('--log-file', action='store', required=False, type=str,
|
||||
help='Specifies a file to output logs to. Defaults to `sys.stdout`.')
|
||||
parser.add_argument('gerrit_project_name', action='store', type=str, help='Target Gerrit project.')
|
||||
parser.add_argument('github_project_name', action='store', type=str, help='Target Github project.')
|
||||
parser.add_argument('gerrit_repo_name', action='store', type=str, help='Target Gerrit repo.')
|
||||
parser.add_argument('github_repo_name', action='store', type=str, help='Target Github repo.')
|
||||
parser.add_argument('github_project_id', action='store', type=int, help='Target Github project board ID.')
|
||||
ns = parser.parse_args()
|
||||
args = validate(ns)
|
||||
verbose = args.pop('verbose')
|
||||
|
@ -15,6 +15,8 @@ import logging
|
||||
import github
|
||||
import pytz as pytz
|
||||
from github.Repository import Repository
|
||||
from github.Project import Project
|
||||
from github.Issue import Issue
|
||||
|
||||
from gerrit_to_github_issues import gerrit
|
||||
from gerrit_to_github_issues import github_issues
|
||||
@ -22,16 +24,19 @@ from gerrit_to_github_issues import github_issues
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def update(gerrit_url: str, gerrit_project_name: str, github_project_name: str, github_user: str, github_password: str,
|
||||
github_token: str, change_age: str = None, skip_approvals: bool = False):
|
||||
gh, repo = github_issues.get_repo(github_project_name, github_user, github_password, github_token)
|
||||
def update(gerrit_url: str, gerrit_project_name: str, github_project_id: int,
|
||||
github_repo_name: str, github_user: str, github_password: str, github_token: str,
|
||||
change_age: str = None, skip_approvals: bool = False):
|
||||
gh = github_issues.get_client(github_user, github_password, github_token)
|
||||
repo = gh.get_repo(github_repo_name)
|
||||
project_board = gh.get_project(github_project_id)
|
||||
change_list = gerrit.get_changes(gerrit_url, gerrit_project_name, change_age=change_age)
|
||||
for change in change_list['data']:
|
||||
if 'commitMessage' in change:
|
||||
process_change(gh, change, repo, skip_approvals)
|
||||
process_change(gh, change, repo, project_board, skip_approvals)
|
||||
|
||||
|
||||
def process_change(gh: github.Github, change: dict, repo: Repository, skip_approvals: bool = False):
|
||||
def process_change(gh: github.Github, change: dict, repo: Repository, project_board: Project, skip_approvals: bool = False):
|
||||
issue_numbers_dict = github_issues.parse_issue_number(change['commitMessage'])
|
||||
issue_numbers_dict = github_issues.remove_duplicated_issue_numbers(issue_numbers_dict)
|
||||
if not issue_numbers_dict:
|
||||
@ -47,8 +52,13 @@ def process_change(gh: github.Github, change: dict, repo: Repository, skip_appro
|
||||
bot_comment = github_issues.get_bot_comment(issue, gh.get_user().login, change['number'])
|
||||
if issue.state == 'closed' and not bot_comment:
|
||||
LOG.debug(f'Issue #{issue_number} was closed, reopening...')
|
||||
|
||||
# NOTE(howell): Reopening a closed issue will move it from the
|
||||
# "Done" column to the "In Progress" column on the project
|
||||
# board via Github automation.
|
||||
issue.edit(state='open')
|
||||
issue.create_comment('Issue reopened due to new activity on Gerrit.\n\n')
|
||||
|
||||
labels = [str(l.name) for l in list(issue.get_labels())]
|
||||
if 'WIP' in change['commitMessage'] or 'DNM' in change['commitMessage']:
|
||||
if 'wip' not in labels:
|
||||
@ -60,6 +70,7 @@ def process_change(gh: github.Github, change: dict, repo: Repository, skip_appro
|
||||
issue.remove_from_labels('ready for review')
|
||||
except github.GithubException:
|
||||
LOG.debug(f'`ready for review` tag does not exist on issue #{issue_number}')
|
||||
move_issue(project_board, issue, 'In Progress')
|
||||
else:
|
||||
if 'ready for review' not in labels:
|
||||
LOG.debug(f'add `ready for review` to #{issue_number}')
|
||||
@ -70,6 +81,7 @@ def process_change(gh: github.Github, change: dict, repo: Repository, skip_appro
|
||||
issue.remove_from_labels('wip')
|
||||
except github.GithubException:
|
||||
LOG.debug(f'`wip` tag does not exist on issue #{issue_number}')
|
||||
move_issue(project_board, issue, 'Submitted on Gerrit')
|
||||
comment_msg = get_issue_comment(change, key, skip_approvals)
|
||||
if not bot_comment:
|
||||
if key == 'closes':
|
||||
@ -121,3 +133,26 @@ def get_issue_comment(change: dict, key: str, skip_approvals: bool = False) -> s
|
||||
dt = datetime.datetime.now(pytz.timezone('America/Chicago')).strftime('%Y-%m-%d %H:%M:%S %Z').strip()
|
||||
comment_str += f'\n\n*Last Updated: {dt}*'
|
||||
return comment_str
|
||||
|
||||
|
||||
def move_issue(project_board, issue, to_col_name):
|
||||
for col in project_board.get_columns():
|
||||
if col.name == to_col_name:
|
||||
to_col = col
|
||||
else:
|
||||
for c in col.get_cards():
|
||||
if c.get_content() == issue:
|
||||
card = c
|
||||
|
||||
if not to_col:
|
||||
LOG.warning(f'Column with name "{to_col_name}" could not be found for project "{project_board.name}"')
|
||||
return
|
||||
|
||||
if not card:
|
||||
LOG.warning(f'Issue with name "{issue.name}" could not be found for project "{project_board.name}"')
|
||||
return
|
||||
|
||||
if card.move("top", to_col):
|
||||
LOG.info('Moved issue "{issue.name}" to column "{to_col_name}"')
|
||||
else:
|
||||
LOG.warning('Failed to move issue "{issue.name}" to column "{to_col_name}"')
|
||||
|
@ -61,14 +61,13 @@ def remove_duplicated_issue_numbers(issue_dict: dict) -> dict:
|
||||
return issue_dict
|
||||
|
||||
|
||||
def get_repo(repo_name: str, github_user: str, github_pw: str, github_token: str) -> (github.Github, Repository):
|
||||
def get_client(github_user: str, github_pw: str, github_token: str) -> github.Github
|
||||
if github_token:
|
||||
gh = github.Github(github_token)
|
||||
elif github_user and github_pw:
|
||||
gh = github.Github(github_user, github_pw)
|
||||
else:
|
||||
raise errors.GithubConfigurationError
|
||||
return gh, gh.get_repo(repo_name)
|
||||
|
||||
|
||||
def get_bot_comment(issue: Issue, bot_name: str, ps_number: str) -> IssueComment:
|
||||
|
Loading…
Reference in New Issue
Block a user