Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New bot command bot: status #237

Merged
merged 16 commits into from
Jan 26, 2024
40 changes: 38 additions & 2 deletions eessi_bot_event_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@
# Local application imports (anything from EESSI/eessi-bot-software-layer)
from connections import github
import tasks.build as build
from tasks.build import check_build_permission, get_architecture_targets, get_repo_cfg, submit_build_jobs
from tasks.build import check_build_permission, get_architecture_targets, get_repo_cfg, \
submit_build_jobs, request_bot_build_issue_comments
laraPPr marked this conversation as resolved.
Show resolved Hide resolved
import tasks.deploy as deploy
from tasks.deploy import deploy_built_artefacts
from tools import config
Expand Down Expand Up @@ -416,7 +417,7 @@ def handle_bot_command_help(self, event_info, bot_command):
help_msg += "\n - Commands must be sent with a **new** comment (edits of existing comments are ignored)."
help_msg += "\n - A comment may contain multiple commands, one per line."
help_msg += "\n - Every command begins at the start of a line and has the syntax `bot: COMMAND [ARGUMENTS]*`"
help_msg += "\n - Currently supported COMMANDs are: `help`, `build`, `show_config`"
help_msg += "\n - Currently supported COMMANDs are: `help`, `build`, `show_config`, `status`"
help_msg += "\n"
help_msg += "\n For more information, see https://www.eessi.io/docs/bot"
return help_msg
Expand Down Expand Up @@ -476,6 +477,41 @@ def handle_bot_command_show_config(self, event_info, bot_command):
issue_comment = self.handle_pull_request_opened_event(event_info, pr)
return f"\n - added comment {issue_comment.html_url} to show configuration"

def handle_bot_command_status(self, event_info, bot_command):
"""
Handles bot command 'status' by running the handler for events of
type pull_request with the action opened.
trz42 marked this conversation as resolved.
Show resolved Hide resolved

Args:
event_info (dict): event received by event_handler
bot_command (EESSIBotCommand): command to be handled

Returns:
(string): table which collects the status of each target
"""
self.log("processing bot command 'status'")
gh = github.get_instance()
repo_name = event_info['raw_request_body']['repository']['full_name']
pr_number = event_info['raw_request_body']['issue']['number']
status_table = request_bot_build_issue_comments(repo_name, pr_number)

comment_status = ''
comment_status += "\nThis is the status of all the `bot: build` commands:"
comment_status += "\n|arch|result|date|status|url|"
comment_status += "\n|----|------|----|------|---|"
for x in range(0, len(status_table['date'])):
comment_status += f"\n|{status_table['arch'][x]}|"
comment_status += f"{status_table['result'][x]}|"
comment_status += f"{status_table['date'][x]}|"
comment_status += f"{status_table['status'][x]}|"
comment_status += f"{status_table['url'][x]}|"

self.log(f"PR opened: comment '{comment_status}'")
repo = gh.get_repo(repo_name)
pull_request = repo.get_pull(pr_number)
issue_comment = pull_request.create_issue_comment(comment_status)
return issue_comment

def start(self, app, port=3000):
"""
Logs startup information to shell and log file and starts the app using
Expand Down
64 changes: 64 additions & 0 deletions tasks/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -760,3 +760,67 @@ def check_build_permission(pr, event_info):
else:
log(f"{fn}(): GH account '{build_labeler}' is authorized to build")
return True


def request_bot_build_issue_comments(repo_name, pr_number):
trz42 marked this conversation as resolved.
Show resolved Hide resolved
status_table = {'arch': [], 'date': [], 'status': [], 'url': [], 'result': []}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wonder a bit if it would be better to let status_table be a list of dictionaries instead of a dictionary of lists where we assume that lists are always identically formed (same length, elements at same index belong to each other).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm open to that. Because it actually takes some effort to put it in a dictionary with lists.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ack. We can think about that later in case it becomes necessary.

cfg = config.read_config()

# for loop because github has max 100 items per request.
# if the pr has more than 100 comments we need to use per_page
# argument at the moment the for loop is for a max of 400 comments could bump this up
for x in range(1, 5):
curl_cmd = f'curl -L https://api.github.com/repos/{repo_name}/issues/{pr_number}/comments?per_page=100&page={x}'
curl_output, curl_error, curl_exit_code = run_cmd(curl_cmd, "fetch all comments")

comments = json.loads(curl_output)

for comment in comments:
# iterate through the comments to find the one where the status of the build was in
if config.read_config()["submitted_job_comments"]['initial_comment'][:20] in comment['body']:

# get archictecture from comment['body']
first_line = comment['body'][:comment['body'].find('/n')]
laraPPr marked this conversation as resolved.
Show resolved Hide resolved
arch_map = get_architecture_targets(cfg)
for arch in arch_map.keys():
target_arch = '/'.join(arch.split('/')[1:])
if target_arch in first_line:
status_table['arch'].append(target_arch)
trz42 marked this conversation as resolved.
Show resolved Hide resolved

# get date, status, url and result from the markdown table
comment_table = comment['body'][comment['body'].find('|'):comment['body'].rfind('|')+1]

# Convert markdown table to a dictionary
lines = comment_table.split('\n')
laraPPr marked this conversation as resolved.
Show resolved Hide resolved
rows = []
keys = []
for i, row in enumerate(lines):
values = {}
if i == 0:
for key in row.split('|'):
keys.append(key.strip())
elif i == 1:
continue
else:
for j, value in enumerate(row.split('|')):
if j > 0 and j < len(keys) - 1:
values[keys[j]] = value.strip()
rows.append(values)

# add date, status, url to status_table if
for row in rows:
if row['job status'] == 'finished':
status_table['date'].append(row['date'])
status_table['status'].append(row['job status'])
status_table['url'].append(comment['html_url'])
if 'FAILURE' in row['comment']:
status_table['result'].append(':cry: FAILURE')
elif 'SUCCES' in row['comment']:
status_table['result'].append(':grin: SUCCESS')
elif 'UNKNOWN' in row['comment']:
status_table['result'].append(':shrug: UNKNOWN')
else:
status_table['result'].append(row['comment'])
if len(comments) != 100:
break
return status_table