Skip to content

Commit 6ba9625

Browse files
authored
Merge pull request #240 from EESSI/develop
release v0.3.0
2 parents 66ba1f9 + 91e39ff commit 6ba9625

File tree

4 files changed

+132
-6
lines changed

4 files changed

+132
-6
lines changed

RELEASE_NOTES

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,19 @@
11
This file contains a description of the major changes to the EESSI
22
build-and-deploy bot. For more detailed information, please see the git log.
33

4+
v0.3.0 (30 January 2024)
5+
--------------------------
6+
7+
This is a minor release of the EESSI build-and-deploy bot.
8+
9+
Bug fixes:
10+
* refreshes the token to access GitHub well before it expires (#238)
11+
12+
Improvements:
13+
* adds a new bot command 'status' which provides an overview (table) of all
14+
finished builds (#237)
15+
16+
417
v0.2.0 (26 November 2023)
518
--------------------------
619

connections/github.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
#
1212

1313
# Standard library imports
14-
from datetime import datetime, timezone
14+
from datetime import datetime, timedelta, timezone
1515
import time
1616

1717
# Third party imports (anything installed into the local Python environment)
@@ -99,8 +99,6 @@ def get_instance():
9999
Instance of Github
100100
"""
101101
global _gh, _token
102-
# TODO Possibly renew token already if expiry date is soon, not only
103-
# after it has expired.
104102

105103
# Check if PyGithub version is < 1.56
106104
if hasattr(github, 'GithubRetry'):
@@ -110,7 +108,10 @@ def get_instance():
110108
# Pygithub 1.x
111109
time_now = datetime.utcnow()
112110

113-
if not _gh or (_token and time_now > _token.expires_at):
111+
# Renew token already if expiry date is less then 30 min away.
112+
refresh_time = timedelta(minutes=30)
113+
114+
if not _gh or (_token and time_now > (_token.expires_at - refresh_time)):
114115
_gh = connect()
115116
return _gh
116117

eessi_bot_event_handler.py

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@
2525
# Local application imports (anything from EESSI/eessi-bot-software-layer)
2626
from connections import github
2727
import tasks.build as build
28-
from tasks.build import check_build_permission, get_architecture_targets, get_repo_cfg, submit_build_jobs
28+
from tasks.build import check_build_permission, get_architecture_targets, get_repo_cfg, \
29+
request_bot_build_issue_comments, submit_build_jobs
2930
import tasks.deploy as deploy
3031
from tasks.deploy import deploy_built_artefacts
3132
from tools import config
@@ -416,7 +417,7 @@ def handle_bot_command_help(self, event_info, bot_command):
416417
help_msg += "\n - Commands must be sent with a **new** comment (edits of existing comments are ignored)."
417418
help_msg += "\n - A comment may contain multiple commands, one per line."
418419
help_msg += "\n - Every command begins at the start of a line and has the syntax `bot: COMMAND [ARGUMENTS]*`"
419-
help_msg += "\n - Currently supported COMMANDs are: `help`, `build`, `show_config`"
420+
help_msg += "\n - Currently supported COMMANDs are: `help`, `build`, `show_config`, `status`"
420421
help_msg += "\n"
421422
help_msg += "\n For more information, see https://www.eessi.io/docs/bot"
422423
return help_msg
@@ -476,6 +477,42 @@ def handle_bot_command_show_config(self, event_info, bot_command):
476477
issue_comment = self.handle_pull_request_opened_event(event_info, pr)
477478
return f"\n - added comment {issue_comment.html_url} to show configuration"
478479

480+
def handle_bot_command_status(self, event_info, bot_command):
481+
"""
482+
Handles bot command 'status' by querying the github API
483+
for the comments in a pr.
484+
485+
Args:
486+
event_info (dict): event received by event_handler
487+
bot_command (EESSIBotCommand): command to be handled
488+
489+
Returns:
490+
github.IssueComment.IssueComment (note, github refers to
491+
PyGithub, not the github from the internal connections module)
492+
"""
493+
self.log("processing bot command 'status'")
494+
gh = github.get_instance()
495+
repo_name = event_info['raw_request_body']['repository']['full_name']
496+
pr_number = event_info['raw_request_body']['issue']['number']
497+
status_table = request_bot_build_issue_comments(repo_name, pr_number)
498+
499+
comment_status = ''
500+
comment_status += "\nThis is the status of all the `bot: build` commands:"
501+
comment_status += "\n|arch|result|date|status|url|"
502+
comment_status += "\n|----|------|----|------|---|"
503+
for x in range(0, len(status_table['date'])):
504+
comment_status += f"\n|{status_table['arch'][x]}|"
505+
comment_status += f"{status_table['result'][x]}|"
506+
comment_status += f"{status_table['date'][x]}|"
507+
comment_status += f"{status_table['status'][x]}|"
508+
comment_status += f"{status_table['url'][x]}|"
509+
510+
self.log(f"Overview of finished builds: comment '{comment_status}'")
511+
repo = gh.get_repo(repo_name)
512+
pull_request = repo.get_pull(pr_number)
513+
issue_comment = pull_request.create_issue_comment(comment_status)
514+
return issue_comment
515+
479516
def start(self, app, port=3000):
480517
"""
481518
Logs startup information to shell and log file and starts the app using

tasks/build.py

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -760,3 +760,78 @@ def check_build_permission(pr, event_info):
760760
else:
761761
log(f"{fn}(): GH account '{build_labeler}' is authorized to build")
762762
return True
763+
764+
765+
def request_bot_build_issue_comments(repo_name, pr_number):
766+
"""
767+
Query the github API for the issue_comments in a pr.
768+
769+
Archs:
770+
repo_name (string): name of the repository (format USER_OR_ORGANISATION/REPOSITORY)
771+
pr_number (int): number og the pr
772+
773+
Returns:
774+
status_table (dict): dictionary with 'arch', 'date', 'status', 'url' and 'result'
775+
for all the finished builds;
776+
"""
777+
status_table = {'arch': [], 'date': [], 'status': [], 'url': [], 'result': []}
778+
cfg = config.read_config()
779+
780+
# for loop because github has max 100 items per request.
781+
# if the pr has more than 100 comments we need to use per_page
782+
# argument at the moment the for loop is for a max of 400 comments could bump this up
783+
for x in range(1, 5):
784+
curl_cmd = f'curl -L https://api.github.com/repos/{repo_name}/issues/{pr_number}/comments?per_page=100&page={x}'
785+
curl_output, curl_error, curl_exit_code = run_cmd(curl_cmd, "fetch all comments")
786+
787+
comments = json.loads(curl_output)
788+
789+
for comment in comments:
790+
# iterate through the comments to find the one where the status of the build was in
791+
if config.read_config()["submitted_job_comments"]['initial_comment'][:20] in comment['body']:
792+
793+
# get archictecture from comment['body']
794+
first_line = comment['body'].split('\n')[0]
795+
arch_map = get_architecture_targets(cfg)
796+
for arch in arch_map.keys():
797+
target_arch = '/'.join(arch.split('/')[1:])
798+
if target_arch in first_line:
799+
status_table['arch'].append(target_arch)
800+
801+
# get date, status, url and result from the markdown table
802+
comment_table = comment['body'][comment['body'].find('|'):comment['body'].rfind('|')+1]
803+
804+
# Convert markdown table to a dictionary
805+
lines = comment_table.split('\n')
806+
rows = []
807+
keys = []
808+
for i, row in enumerate(lines):
809+
values = {}
810+
if i == 0:
811+
for key in row.split('|'):
812+
keys.append(key.strip())
813+
elif i == 1:
814+
continue
815+
else:
816+
for j, value in enumerate(row.split('|')):
817+
if j > 0 and j < len(keys) - 1:
818+
values[keys[j]] = value.strip()
819+
rows.append(values)
820+
821+
# add date, status, url to status_table if
822+
for row in rows:
823+
if row['job status'] == 'finished':
824+
status_table['date'].append(row['date'])
825+
status_table['status'].append(row['job status'])
826+
status_table['url'].append(comment['html_url'])
827+
if 'FAILURE' in row['comment']:
828+
status_table['result'].append(':cry: FAILURE')
829+
elif 'SUCCESS' in value['comment']:
830+
status_table['result'].append(':grin: SUCCESS')
831+
elif 'UNKNOWN' in row['comment']:
832+
status_table['result'].append(':shrug: UNKNOWN')
833+
else:
834+
status_table['result'].append(row['comment'])
835+
if len(comments) != 100:
836+
break
837+
return status_table

0 commit comments

Comments
 (0)