From 8d0e1d6fe0be550416d4e8a46b51f3b1161ea663 Mon Sep 17 00:00:00 2001 From: "Sondre B. Risanger" <168830227+sondrebr@users.noreply.github.com> Date: Wed, 20 May 2026 11:24:27 +0200 Subject: [PATCH 01/11] Fix typo in GitLabEventInfo 'notable' should be 'noteable' --- tools/event_info.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tools/event_info.py b/tools/event_info.py index 2d1a902c..2ae27b43 100644 --- a/tools/event_info.py +++ b/tools/event_info.py @@ -252,10 +252,10 @@ def event_type(self): # We therefore need to check what type of comment it is to get the issue numbers and URLs. @cached_property def issue_number(self): - notable_type = self._object_attributes["notable_type"] - if notable_type == "MergeRequest": + noteable_type = self._object_attributes["noteable_type"] + if noteable_type == "MergeRequest": issue_iid = self._request_body["merge_request"]["iid"] - elif notable_type == "Issue": + elif noteable_type == "Issue": issue_iid = self._request_body["issue"]["iid"] else: # Comments may also come from commits etc. - default to -1 @@ -264,10 +264,10 @@ def issue_number(self): @cached_property def issue_url(self): - notable_type = self._object_attributes["notable_type"] - if notable_type == "MergeRequest": + noteable_type = self._object_attributes["noteable_type"] + if noteable_type == "MergeRequest": issue_url = self._request_body["merge_request"]["url"] - elif notable_type == "Issue": + elif noteable_type == "Issue": issue_url = self._request_body["issue"]["url"] else: # Comments may also come from commits etc. - default to empty string From c3bb627b02c1188eba8d8fc8db76a58b01578f87 Mon Sep 17 00:00:00 2001 From: "Sondre B. Risanger" <168830227+sondrebr@users.noreply.github.com> Date: Tue, 26 May 2026 15:48:34 +0200 Subject: [PATCH 02/11] Update PyGHee requirement --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 35c6ecf4..ad11ebcf 100644 --- a/requirements.txt +++ b/requirements.txt @@ -16,5 +16,5 @@ python-gitlab==6.5.0;python_version=="3.9" # Last version with Python 3.9 suppor python-gitlab==8.3.0;python_version>="3.10" # Most recent version on 2026-05-04 Waitress>=3.0.1 # required to fix vulnerabilities detected by scorecards cryptography>=44.0.1 # required to fix vulnerabilities detected by scorecards -PyGHee @ git+https://github.com/boegel/PyGHee.git@c5e10632a45db5ca94f5cbf87ac7a90a2064e8fd # Pin commit with GL support +PyGHee @ git+https://github.com/boegel/PyGHee.git@514bdf6b7db1ed2a965ccdabaf45f9e9b1d825b7 # Pin commit with GL support retry From 37b3428b38b9668571b56068d64c335aae09f690 Mon Sep 17 00:00:00 2001 From: "Sondre B. Risanger" <168830227+sondrebr@users.noreply.github.com> Date: Tue, 26 May 2026 15:55:18 +0200 Subject: [PATCH 03/11] Add EventInfo type Some docstrings already referred to subclasses of BaseEventInfo as EventInfo without EventInfo actually being defined anywhere --- tools/event_info.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tools/event_info.py b/tools/event_info.py index 2ae27b43..1b6d1bb2 100644 --- a/tools/event_info.py +++ b/tools/event_info.py @@ -11,6 +11,7 @@ # Standard library imports from functools import cached_property +from typing import Union # Third party imports (anything installed into the local Python environment) # (none) @@ -318,6 +319,10 @@ def repo_name(self): return self._request_body["project"]["path_with_namespace"] +# Type for subclasses of BaseEventInfo +EventInfo = Union[GitHubEventInfo, GitLabEventInfo] + + def create_event_info_instance(event_info): """ Creates an EventInfo instance for the configured Git hosting platform. @@ -326,7 +331,7 @@ def create_event_info_instance(event_info): event_info (dict): The event info dictionary created by PyGHee Returns: - Instance of BaseEventInfo subclass + EventInfo instance or None """ git_host = get_git_hosting_platform() if git_host == GITHUB: From 2fa766620c7d5e0746659e303be1e29e6c6d325b Mon Sep 17 00:00:00 2001 From: "Sondre B. Risanger" <168830227+sondrebr@users.noreply.github.com> Date: Thu, 28 May 2026 11:25:19 +0200 Subject: [PATCH 04/11] Add get_app_name function --- eessi_bot_event_handler.py | 4 ++-- tools/git.py | 22 ++++++++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/eessi_bot_event_handler.py b/eessi_bot_event_handler.py index 2967ad5d..4606c3cd 100644 --- a/eessi_bot_event_handler.py +++ b/eessi_bot_event_handler.py @@ -38,7 +38,7 @@ from tools.commands import EESSIBotCommand, EESSIBotCommandError, \ contains_any_bot_command, get_bot_command from tools.event_info import create_event_info_instance -from tools.git import connect_to_git_hosting_platform, get_git_hosting_platform +from tools.git import connect_to_git_hosting_platform, get_app_name, get_git_hosting_platform from tools.permissions import check_command_permission from tools.pr_comments import ChatLevels, create_comment @@ -209,7 +209,7 @@ def handle_issue_comment_event(self, event_info, log_file=None): # log level is set to debug self.log(f"Comment in {issue_url} (owned by @{owner}) {action} by @{sender}") - app_name = self.cfg[config.SECTION_GITHUB][config.GITHUB_SETTING_APP_NAME] + app_name = get_app_name(self.cfg) command_response_fmt = self.cfg[config.SECTION_BOT_CONTROL][config.BOT_CONTROL_SETTING_COMMAND_RESPONSE_FMT] # currently, only commands in new comments are supported diff --git a/tools/git.py b/tools/git.py index 25a61a57..f6af96a7 100644 --- a/tools/git.py +++ b/tools/git.py @@ -71,3 +71,25 @@ def connect_to_git_hosting_platform(): gitlab.connect() else: logging.error(f"Git host not supported: '{git_host}'") + + +# TODO: We might consider merging these settings later, for example as an 'app_name' setting in the 'git' section +def get_app_name(cfg=None): + """ + Get the configured app/bot name. + + Args: + cfg (ConfigParser): Instance of ConfigParser containing the configuration. + May be passed by caller to avoid re-reading the configuration file. + + Returns: + (str): The configured app/bot name or None + """ + if not cfg: + cfg = config.read_config() + git_host = get_git_hosting_platform(cfg) + if git_host == GITHUB: + return cfg.get(config.SECTION_GITHUB, config.GITHUB_SETTING_APP_NAME) + elif git_host == GITLAB: + return cfg.get(config.SECTION_GITLAB, config.GITLAB_SETTING_BOT_NAME) + return None From 13fc608199163fe685a5dbf05a049e0ae0a10328 Mon Sep 17 00:00:00 2001 From: "Sondre B. Risanger" <168830227+sondrebr@users.noreply.github.com> Date: Thu, 28 May 2026 11:28:54 +0200 Subject: [PATCH 05/11] Use EventInfo properties in handle_issue_comment_event --- eessi_bot_event_handler.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/eessi_bot_event_handler.py b/eessi_bot_event_handler.py index 4606c3cd..bae10fd4 100644 --- a/eessi_bot_event_handler.py +++ b/eessi_bot_event_handler.py @@ -188,7 +188,7 @@ def handle_issue_comment_event(self, event_info, log_file=None): comments for any bot command and execute it if one is found. Args: - event_info (dict): event received by event_handler + event_info (EventInfo): event received by event_handler log_file (string): path to log messages to Returns: @@ -197,13 +197,12 @@ def handle_issue_comment_event(self, event_info, log_file=None): Raises: Exception: raises any exception that is not of type EESSIBotCommandError """ - request_body = event_info['raw_request_body'] - issue_url = request_body['issue']['url'] - action = request_body['action'] - sender = request_body['sender']['login'] - owner = request_body['comment']['user']['login'] - repo_name = request_body['repository']['full_name'] - pr_number = request_body['issue']['number'] + issue_url = event_info.issue_url + action = event_info.action + sender = event_info.event_triggered_by + owner = event_info.comment_created_by + repo_name = event_info.repo_name + pr_number = event_info.issue_number # TODO add request body text (['comment']['body']) to log message when # log level is set to debug @@ -217,7 +216,7 @@ def handle_issue_comment_event(self, event_info, log_file=None): # only scan for commands in newly created comments if action == 'created': - comment_received = request_body['comment']['body'] + comment_received = event_info.comment_body self.log(f"comment action '{action}' is handled") else: # NOTE we do not respond to an updated PR comment with yet another From 3092b7408f5fdfd1f6f8bde8435191d87b2b1bcb Mon Sep 17 00:00:00 2001 From: "Sondre B. Risanger" <168830227+sondrebr@users.noreply.github.com> Date: Thu, 28 May 2026 12:12:20 +0200 Subject: [PATCH 06/11] Rename PRComment to PRCommentInfo --- tasks/build.py | 2 +- tests/test_task_build.py | 4 ++-- tools/job_metadata.py | 2 +- tools/pr_comments.py | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tasks/build.py b/tasks/build.py index 6c191013..a33cff2d 100644 --- a/tasks/build.py +++ b/tasks/build.py @@ -1078,7 +1078,7 @@ def submit_build_jobs(pr, event_info, action_filter, build_params): pr_comment = create_pr_comment(job, job_id, app_name, pr, symlink, build_params) job_id_to_comment_map[job_id] = pr_comment - pr_comment = pr_comments.PRComment(pr.base.repo.full_name, pr.number, pr_comment.id) + pr_comment = pr_comments.PRCommentInfo(pr.base.repo.full_name, pr.number, pr_comment.id) # create _bot_job.metadata file in the job's working directory job_metadata.create_metadata_file(job, job_id, pr_comment) diff --git a/tests/test_task_build.py b/tests/test_task_build.py index cab91a35..4de5a2f0 100644 --- a/tests/test_task_build.py +++ b/tests/test_task_build.py @@ -30,7 +30,7 @@ from tools import run_cmd, run_subprocess from tools.build_params import EESSIBotBuildParams from tools.job_metadata import create_metadata_file, read_metadata_file -from tools.pr_comments import PRComment, get_submitted_job_comment +from tools.pr_comments import PRCommentInfo, get_submitted_job_comment # Local tests imports (reusing code from other tests) from tests.test_tools_pr_comments import MockIssueComment @@ -462,7 +462,7 @@ def test_create_read_metadata_file(mocked_github, tmp_path): job_id = "123" repo_name = "test_repo" - pr_comment = PRComment(repo_name, pr_number, 77) + pr_comment = PRCommentInfo(repo_name, pr_number, 77) create_metadata_file(job, job_id, pr_comment) expected_file = f"_bot_job{job_id}.metadata" diff --git a/tools/job_metadata.py b/tools/job_metadata.py index e4031faf..9ecda4a7 100644 --- a/tools/job_metadata.py +++ b/tools/job_metadata.py @@ -90,7 +90,7 @@ def create_metadata_file(job, job_id, pr_comment): Args: job (named tuple): key data about job that has been submitted job_id (string): id of submitted job - pr_comment (PRComment): contains repo_name, pr_number and pr_comment_id + pr_comment (PRCommentInfo): contains repo_name, pr_number and pr_comment_id Returns: None (implicitly) diff --git a/tools/pr_comments.py b/tools/pr_comments.py index 0585887f..08138bcb 100644 --- a/tools/pr_comments.py +++ b/tools/pr_comments.py @@ -30,7 +30,7 @@ from tools import config -PRComment = namedtuple('PRComment', ('repo_name', 'pr_number', 'pr_comment_id')) +PRCommentInfo = namedtuple('PRCommentInfo', ('repo_name', 'pr_number', 'pr_comment_id')) class ChatLevels(Enum): From e294ac3a5064979e24afaf120eb903f5062cc48d Mon Sep 17 00:00:00 2001 From: "Sondre B. Risanger" <168830227+sondrebr@users.noreply.github.com> Date: Thu, 28 May 2026 12:25:31 +0200 Subject: [PATCH 07/11] Add PRComment classes --- tools/pr_comments.py | 177 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 176 insertions(+), 1 deletion(-) diff --git a/tools/pr_comments.py b/tools/pr_comments.py index 08138bcb..ae5c815e 100644 --- a/tools/pr_comments.py +++ b/tools/pr_comments.py @@ -10,6 +10,7 @@ # author: Jonas Qvigstad (@jonas-lq) # author: Thomas Roeblitz (@trz42) # author: Sam Moors (@smoors) +# author: Sondre Bergsvaag Risanger (@sondrebr) # # license: GPLv2 # @@ -19,6 +20,7 @@ from enum import Enum import re import sys +from typing import Union # Third party imports (anything installed into the local Python environment) from pyghee.utils import log @@ -26,8 +28,9 @@ from retry.api import retry_call # Local application imports (anything from EESSI/eessi-bot-software-layer) -from connections import github +from connections import github, gitlab from tools import config +from tools.git import get_git_hosting_platform, GITHUB, GITLAB PRCommentInfo = namedtuple('PRCommentInfo', ('repo_name', 'pr_number', 'pr_comment_id')) @@ -192,3 +195,175 @@ def update_pr_comment(event_info, update): pull_request = repo.get_pull(pr_number) issue_comment = pull_request.get_issue_comment(issue_id) issue_comment.edit(comment_new + update) + + +class BasePRComment(): + """ + Base class to use for handling PR comments, which works differently for GitHub vs. GitLab. + """ + def __init__(self, repo_name, pr_number, body=None, id=None): + if self.__class__ is BasePRComment: + err_msg = "Do not use this base class directly. " + err_msg += "Please use one of its subclasses instead." + raise NotImplementedError(err_msg) + + # 'body' should be provided when creating a new comment + # 'id' should be provided when dealing with an existing comment + if (body and id) or not (body or id): + err_msg = "Exactly one of 'body' and 'id' must be " + err_msg += "set when initializing a comment class." + raise Exception(err_msg) + + self.body = body + self.id = id + self.repo_name = repo_name + self.pr_number = pr_number + self._pr_obj = None + self._comment_obj = None + + @property + def html_url(self): + raise NotImplementedError() + + def get(self): + raise NotImplementedError() + + def create(self): + raise NotImplementedError() + + def edit(self): + raise NotImplementedError() + + def append(self): + raise NotImplementedError() + + +class GitHubPRComment(BasePRComment): + """ + PRComment class for use with GitHub. + """ + def __init__(self, repo_name, pr_number, body=None, id=None): + super().__init__(repo_name, pr_number, body, id) + gh = github.get_instance() + repo = gh.get_repo(self.repo_name) + self._pr_obj = repo.get_pull(self.pr_number) + + @property + def html_url(self): + if self._comment_obj: + return self._comment_obj.html_url + return None + + def get(self): + if not self.id: + raise Exception("'id' must be set to get a comment.") + self._comment_obj = retry_call(self._pr_obj.get_issue_comment, fargs=[self.id], + exceptions=Exception, tries=5, delay=1, backoff=2, max_delay=30) + if self._comment_obj: + self.body = self._comment_obj.body + + def create(self): + if not self.body: + raise Exception("'body' must be set to create a comment.") + if self.id: + # Return early if 'id' is set to avoid creating duplicate comments + return + self._comment_obj = retry_call(self._pr_obj.create_issue_comment, fargs=[self.body], + exceptions=Exception, tries=3, delay=1, backoff=2, max_delay=10) + if self._comment_obj: + self.id = self._comment_obj.id + + def edit(self, new_body): + if not self.id: + raise Exception("'id' must be set to edit a comment.") + # Ensure comment object is present + if not self._comment_obj: + self.get() + self.body = new_body + retry_call(self._comment_obj.edit, fargs=[self.body], exceptions=Exception, + tries=5, delay=1, backoff=2, max_delay=30) + + def append(self, text_to_append): + if not self.id: + raise Exception("'id' must be set to append to a comment.") + # Ensure comment object is present and up to date + self.get() + self.edit(self.body + text_to_append) + + +class GitLabPRComment(BasePRComment): + """ + PRComment class for use with GitLab. + """ + def __init__(self, repo_name, pr_number, body=None, id=None): + super().__init__(repo_name, pr_number, body, id) + gl = gitlab.get_instance() + proj = gl.projects.get(self.repo_name) + self._pr_obj = proj.mergerequests.get(self.pr_number) + + @property + def html_url(self): + if self._comment_obj: + # GitLab comment object does not include a comment URL + return f"{self._pr_obj.web_url}#note_{self._comment_obj.id}" + return None + + def get(self): + if not self.id: + raise Exception("'id' must be set to get a comment.") + self._comment_obj = self._pr_obj.notes.get(self.id) + self.body = self._comment_obj.body + + def create(self): + if not self.body: + raise Exception("'body' must be set to create a comment.") + if self.id: + # Return early if 'id' is set to avoid creating duplicate comments + return + self._comment_obj = self._pr_obj.notes.create({"body": self.body}) + if self._comment_obj: + self.id = self._comment_obj.id + + def edit(self, new_body): + if not self.id: + raise Exception("'id' must be set to edit a comment.") + # Ensure comment object is present + if not self._comment_obj: + self.get() + self.body = new_body + self._comment_obj.body = self.body + self._comment_obj.save() + + def append(self, text_to_append): + if not self.id: + raise Exception("'id' must be set to append to a comment.") + # Ensure comment object and body are present and up to date + self.get() + self.edit(self.body + text_to_append) + + +# Type for subclasses of BasePRComment +PRComment = Union[GitHubPRComment, GitLabPRComment] + + +def create_pr_comment_instance(repo_name, pr_number, body=None, id=None): + """ + Creates a PRComment instance for the configured Git hosting platform. + + Args: + repo_name (string): The name of the repository + pr_number (int): The number of the pull request in the repository + body (string): The comment body. Required when creating a new comment. + Cannot be set at the same time as 'id'. + id (int): The ID of the comment. Required when getting and/or updating + an existing comment. Cannot be set at the same time as 'body'. + + Returns: + PRComment instance or None + """ + git_host = get_git_hosting_platform() + if git_host == GITHUB: + return GitHubPRComment(repo_name=repo_name, pr_number=pr_number, body=body, id=id) + elif git_host == GITLAB: + return GitLabPRComment(repo_name=repo_name, pr_number=pr_number, body=body, id=id) + return None From e8736a7da491a21b2c272ea289cd22cb0d4f8d45 Mon Sep 17 00:00:00 2001 From: "Sondre B. Risanger" <168830227+sondrebr@users.noreply.github.com> Date: Thu, 28 May 2026 12:37:07 +0200 Subject: [PATCH 08/11] Use new PRComment classes in create_comment --- eessi_bot_event_handler.py | 2 +- tests/test_app.cfg | 3 +++ tools/pr_comments.py | 17 ++++++++--------- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/eessi_bot_event_handler.py b/eessi_bot_event_handler.py index bae10fd4..2f8e8175 100644 --- a/eessi_bot_event_handler.py +++ b/eessi_bot_event_handler.py @@ -571,7 +571,7 @@ def handle_bot_command_build(self, event_info, bot_command): else: for job_id, issue_comment in submitted_jobs.items(): build_msg += f"\n - submitted job `{job_id}`" - if issue_comment: + if issue_comment and issue_comment.html_url: build_msg += f", for details & status see {issue_comment.html_url}" else: request_body = event_info['raw_request_body'] diff --git a/tests/test_app.cfg b/tests/test_app.cfg index d0fff239..db1e1982 100644 --- a/tests/test_app.cfg +++ b/tests/test_app.cfg @@ -11,6 +11,9 @@ # sample config file for tests (some functions run config.read_config() # which reads app.cfg by default) +[git] +hosting_platform = github + [buildenv] job_handover_protocol = hold_release diff --git a/tools/pr_comments.py b/tools/pr_comments.py index ae5c815e..5732cb39 100644 --- a/tools/pr_comments.py +++ b/tools/pr_comments.py @@ -46,7 +46,7 @@ class ChatLevels(Enum): def create_comment(repo_name, pr_number, comment, req_chatlevel): """ - Create a comment to a pull request on GitHub + Create a comment to a pull request Args: repo_name (string): name of the repository @@ -55,8 +55,7 @@ def create_comment(repo_name, pr_number, comment, req_chatlevel): req_chatlevel (member of ChatLevels Enum): minimum required chattiness level for creating the PR comment Returns: - github.IssueComment.IssueComment instance or None (note, github refers to - PyGithub, not the github from the internal connections module) + PRComment instance or None """ fn = sys._getframe().f_code.co_name @@ -65,12 +64,12 @@ def create_comment(repo_name, pr_number, comment, req_chatlevel): config.BOT_CONTROL_SETTING_CHATLEVEL, ChatLevels.BASIC.name).upper() if ChatLevels[chatlevel].value >= req_chatlevel.value: - gh = github.get_instance() - repo = gh.get_repo(repo_name) - pull_request = repo.get_pull(pr_number) - issue_comment = retry_call(pull_request.create_issue_comment, fargs=[comment], - exceptions=Exception, tries=3, delay=1, backoff=2, max_delay=10) - return issue_comment + pr_comment = create_pr_comment_instance(repo_name, pr_number, body=comment) + pr_comment.create() + # If 'id' is not set, something went wrong + if not pr_comment.id: + return None + return pr_comment else: log(f"{fn}(): not creating PR comment: " From cf6d4edf94bee1012c9fbe90d1d2106e159a86a8 Mon Sep 17 00:00:00 2001 From: "Sondre B. Risanger" <168830227+sondrebr@users.noreply.github.com> Date: Thu, 28 May 2026 12:51:56 +0200 Subject: [PATCH 09/11] Check if command is supported on configured platform Commands might for example be supported on GitHub, but not GitLab. Responding with a message explicitly stating that the command is unsupported is more useful to the user than letting the command handler run until it fails. --- eessi_bot_event_handler.py | 14 +++++++++----- tools/commands.py | 23 +++++++++++++++++++++++ 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/eessi_bot_event_handler.py b/eessi_bot_event_handler.py index 2f8e8175..0dc2f2b7 100644 --- a/eessi_bot_event_handler.py +++ b/eessi_bot_event_handler.py @@ -36,7 +36,7 @@ from tools import config from tools.args import event_handler_parse from tools.commands import EESSIBotCommand, EESSIBotCommandError, \ - contains_any_bot_command, get_bot_command + contains_any_bot_command, get_bot_command, get_supported_commands, ALL_COMMANDS from tools.event_info import create_event_info_instance from tools.git import connect_to_git_hosting_platform, get_app_name, get_git_hosting_platform from tools.permissions import check_command_permission @@ -496,7 +496,7 @@ def handle_bot_command(self, event_info, bot_command, log_file=None): specific bot_command given. Args: - event_info (dict): event received by event_handler + event_info (EventInfo): event received by event_handler bot_command (EESSIBotCommand): command to be handled log_file (string): path to log messages to @@ -511,9 +511,13 @@ def handle_bot_command(self, event_info, bot_command, log_file=None): cmd = bot_command.command handler_name = f"handle_bot_command_{cmd}" if hasattr(self, handler_name): - handler = getattr(self, handler_name) - self.log(f"Handling bot command {cmd}") - return handler(event_info, bot_command) + if cmd in get_supported_commands(self.cfg): + handler = getattr(self, handler_name) + self.log(f"Handling bot command {cmd}") + return handler(event_info, bot_command) + else: + self.log(f"Command '{cmd}' is not supported on the configured Git hosting platform.") + raise EESSIBotCommandError(f"Unsupported command `{cmd}`; use `bot: help` for usage information") else: self.log(f"No handler for command '{cmd}'") raise EESSIBotCommandError(f"unknown command `{cmd}`; use `bot: help` for usage information") diff --git a/tools/commands.py b/tools/commands.py index bd80e339..0a32f0da 100644 --- a/tools/commands.py +++ b/tools/commands.py @@ -19,6 +19,29 @@ # Local application imports (anything from EESSI/eessi-bot-software-layer) from tools.filter import EESSIBotActionFilter, EESSIBotActionFilterError from tools.build_params import EESSIBotBuildParams +from tools.git import get_git_hosting_platform, GITHUB, GITLAB + + +ALL_COMMANDS = ["help", "build", "show_config", "status", "cancel"] +SUPPORTED_COMMANDS_PER_GIT_HOST = { + GITHUB: ["help", "build", "show_config", "status", "cancel"], + GITLAB: [], +} + + +def get_supported_commands(cfg=None): + """ + Returns the supported commands for the configured Git hosting platform. + + Args: + cfg (ConfigParser): Instance of ConfigParser containing the configuration. + May be passed by caller to avoid re-reading the configuration file. + + Returns: + supported_commands (list of strings): The supported commands + """ + git_host = get_git_hosting_platform(cfg) + return SUPPORTED_COMMANDS_PER_GIT_HOST[git_host] def contains_any_bot_command(body): From d969279f53c995f558643d72c4821243ad303033 Mon Sep 17 00:00:00 2001 From: "Sondre B. Risanger" <168830227+sondrebr@users.noreply.github.com> Date: Thu, 28 May 2026 12:58:35 +0200 Subject: [PATCH 10/11] Show unsupported commands in `bot: help` response --- eessi_bot_event_handler.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/eessi_bot_event_handler.py b/eessi_bot_event_handler.py index 0dc2f2b7..b5f66561 100644 --- a/eessi_bot_event_handler.py +++ b/eessi_bot_event_handler.py @@ -528,17 +528,25 @@ def handle_bot_command_help(self, event_info, bot_command): commands. Args: - event_info (dict): event received by event_handler + event_info (EventInfo): event received by event_handler bot_command (EESSIBotCommand): command to be handled Returns: (string): basic information about sending commands to the bot """ + # Create comma-separated lists of supported and unsupported commands + supported_commands = get_supported_commands(self.cfg) + unsupported_commands = [cmd for cmd in ALL_COMMANDS if cmd not in supported_commands] + supported_commands_str = ", ".join([f"`{cmd}`" for cmd in supported_commands]) + unsupported_commands_str = ", ".join([f"`{cmd}`" for cmd in unsupported_commands]) + help_msg = "\n **How to send commands to bot instances**" 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`, `status`, `cancel`" + help_msg += "\n - Currently supported COMMANDs are: " + supported_commands_str + if unsupported_commands_str: + help_msg += "\n - The following COMMANDs are not yet supported: " + unsupported_commands_str help_msg += "\n" help_msg += "\n For more information, see https://www.eessi.io/docs/bot" return help_msg From d3720bd687c43fb5ef5a3e45250bfbcdd56ebea2 Mon Sep 17 00:00:00 2001 From: "Sondre B. Risanger" <168830227+sondrebr@users.noreply.github.com> Date: Thu, 28 May 2026 12:59:27 +0200 Subject: [PATCH 11/11] Add GitLab support for `bot: help` --- eessi_bot_event_handler.py | 3 +++ tools/commands.py | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/eessi_bot_event_handler.py b/eessi_bot_event_handler.py index b5f66561..c747474a 100644 --- a/eessi_bot_event_handler.py +++ b/eessi_bot_event_handler.py @@ -367,6 +367,9 @@ def handle_issue_comment_event(self, event_info, log_file=None): self.log(f"issue_comment event (url {issue_url}) handled!") + # PyGHee gets the event type by subscripting event_info, i.e., it gets 'note' for GL comment events + handle_note_event = handle_issue_comment_event + def handle_installation_event(self, event_info, log_file=None): """ Handle events of type installation. Main action is to log the event. diff --git a/tools/commands.py b/tools/commands.py index 0a32f0da..7fd9a4da 100644 --- a/tools/commands.py +++ b/tools/commands.py @@ -25,7 +25,7 @@ ALL_COMMANDS = ["help", "build", "show_config", "status", "cancel"] SUPPORTED_COMMANDS_PER_GIT_HOST = { GITHUB: ["help", "build", "show_config", "status", "cancel"], - GITLAB: [], + GITLAB: ["help"], }