From e4d550755d86caf347393be5b8770f6fd4febc7d Mon Sep 17 00:00:00 2001 From: Jose Borreguero <borreguero@gmail.com> Date: Tue, 27 Dec 2022 10:03:37 -0500 Subject: [PATCH 1/4] add legend Signed-off-by: Jose Borreguero <borreguero@gmail.com> --- mentionbot.py | 130 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 mentionbot.py diff --git a/mentionbot.py b/mentionbot.py new file mode 100644 index 0000000..825ae0f --- /dev/null +++ b/mentionbot.py @@ -0,0 +1,130 @@ +# Tutorial https://api.slack.com/tutorials/tracks/responding-to-app-mentions + +# third party imports +from slack_bolt import App +from slack_bolt.adapter.socket_mode import SocketModeHandler + +# standard imports +import os +import requests + +status2emoji = { + "success": ":white_check_mark:", + "error": ":ghost:", + "failed": ":x:", + "running": ":hourglass:", +} + + +def legend(): + r"""Explanation of emojis""" + _legend = "\t:white_small_square:".join([f"{status}: {emoji}" for status, emoji in status2emoji.items()]) + block = [ + { + "type": "header", + "text": { + "type": "plain_text", + "text": "LEGEND", + "emoji": True + } + }, + { + "type": "section", + "text": + { + "type": "mrkdwn", + "text": _legend + } + }, + { + "type": "divider" + }, + ] + return block + + +def get_status_blocks(): + blocks = legend() + # + projects = { + "django-remote-submission": 10248, + "drtSANS": 4955, + "Web Monitor Test AMQ Message Generator": 10770, + "Web_Reflectivity": 5380, + } + for project, project_id in projects.items(): + branches_status = { + "next": "error", + "qa": "error", + "main": "error", + } + url = f"https://code.ornl.gov/api/v4/projects/{project_id}/pipelines" + for branch, status in branches_status.items(): + response = requests.get(url, params={"ref": branch}) + if response.status_code == 200: + try: + status = response.json()[0]["status"] + except: + print(project) + print(branch) + print(response.json()) + try: + status_emoji = status2emoji[status] + except: + print(project) + print(branch) + print(response.json()) + print(status) + branches_status[branch] = status_emoji + # build the block + status_report = "\t:white_small_square:".join([f"*{branch}* {branches_status[branch]}" + for branch in ["next", "qa", "main"]]) + blocks += [ + { + "type": "header", + "text": { + "type": "plain_text", + "text": f"{project}", + "emoji": True + } + }, + { + "type": "section", + "text": + { + "type": "mrkdwn", + "text": status_report + } + }, + { + "type": "divider" + }, + ] + return blocks + +# Install the Slack app and get xoxb- token in advance +app = App(token=os.environ["SLACK_BOT_TOKEN"]) + + +@app.command("/hello-socket-mode") +def hello_command(ack, body): + user_id = body["user_id"] + ack(f"Hi, <@{user_id}>!") + + +r""" +@app.event("app_mention") +def event_test(say): + say("Hi there!") +""" + +@app.event("app_mention") +def greetings(ack, say): + ack() + say("Build status summary for Neutron projects:") + status_blocks = {"blocks": get_status_blocks()} + say(status_blocks) + + +if __name__ == "__main__": + SocketModeHandler(app, os.environ["SLACK_APP_TOKEN"]).start() -- GitLab From 0d102fe1e8f2f5f8b5ed0de7738b165e588d7b31 Mon Sep 17 00:00:00 2001 From: Jose Borreguero <borreguero@gmail.com> Date: Tue, 27 Dec 2022 10:04:06 -0500 Subject: [PATCH 2/4] add conda environment for local development Signed-off-by: Jose Borreguero <borreguero@gmail.com> --- conda_enviroment.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 conda_enviroment.yml diff --git a/conda_enviroment.yml b/conda_enviroment.yml new file mode 100644 index 0000000..6bd39a4 --- /dev/null +++ b/conda_enviroment.yml @@ -0,0 +1,11 @@ +name: amaterasu +channels: + - conda-forge + - defaults +dependencies: + - python=3.8 + - pip + - requests + - python-dotenv + - pip: + - slack_bolt -- GitLab From 172ac578b11c0ff6d199537767ea6944c9afbf0e Mon Sep 17 00:00:00 2001 From: Jose Borreguero <borreguero@gmail.com> Date: Tue, 27 Dec 2022 14:56:41 -0500 Subject: [PATCH 3/4] add github repos to mentionbot Signed-off-by: Jose Borreguero <borreguero@gmail.com> --- mentionbot.py | 116 +++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 105 insertions(+), 11 deletions(-) diff --git a/mentionbot.py b/mentionbot.py index 825ae0f..97f7a33 100644 --- a/mentionbot.py +++ b/mentionbot.py @@ -5,20 +5,41 @@ from slack_bolt import App from slack_bolt.adapter.socket_mode import SocketModeHandler # standard imports +import logging import os import requests + +logger = logging.getLogger(__name__) + status2emoji = { "success": ":white_check_mark:", - "error": ":ghost:", "failed": ":x:", + "failure": ":x:", + "error": ":ghost:", + "stale": ":ghost:", + "skipped": ":ghost:", + "timed_out": "ghost", + "cancelled": "ghost", + "completed": "ghost", "running": ":hourglass:", + "in_progress": ":hourglass:", + "queued": ":hourglass:", + "requested": ":hourglass:", + "waiting": ":hourglass:", + "action_required": ":hourglass:", } def legend(): r"""Explanation of emojis""" - _legend = "\t:white_small_square:".join([f"{status}: {emoji}" for status, emoji in status2emoji.items()]) + emojis = { + "success": ":white_check_mark:", + "uncomplete": ":hourglass:", + "failure": ":x:", + "other error": ":ghost:", + } + _legend = "\t:white_small_square:".join([f"{status}: {emoji}" for status, emoji in emojis.items()]) block = [ { "type": "header", @@ -43,9 +64,80 @@ def legend(): return block -def get_status_blocks(): - blocks = legend() - # +def github_status_blocks(): + blocks = [ + { + "type": "header", + "text": { + "type": "plain_text", + "text": "PROJECTS IN GITHUB:", + "emoji": True + } + }, + ] + # Organization, Repository, Workflow-Name + WORKFLOW_ANY = "*" # any workflow is fine for testing + query_github = [ + ("neutrons", "addie", WORKFLOW_ANY), + ("neutrons", "data_workflow", WORKFLOW_ANY), + ("ornlneutronimaging", "iMars3D", WORKFLOW_ANY), + ("neutrons", "mantid_total_scattering", WORKFLOW_ANY), + ("neutrons", "PyRS", WORKFLOW_ANY), + ("neutronimaging", "python_notebooks", WORKFLOW_ANY), + ("neutrons", "reflectivity_ui", WORKFLOW_ANY), + ("neutrons", "RefRed", WORKFLOW_ANY), + ] + for owner, project, workflow_name in query_github: + branch_reports = list() + url = f"https://api.github.com/repos/{owner}/{project}/actions/runs" + for branch in ["next", "qa", "main"]: + response = requests.get(url, params={"branch": branch}) + run_status = "error" # default branch report + if response.ok: + for run in response.json()["workflow_runs"]: + if workflow_name in (WORKFLOW_ANY, run["name"]): + # run["conclusion"] is None for uncompleted jobs + run_status = run["conclusion"] if run["conclusion"] is not None else run["status"] + break + else: + logger.error(f"Request GET failed for {owner}/{project}?parameter=branch={branch}") + branch_reports.append(f"*{branch}* {status2emoji.get(run_status, ':ghost:')}") + project_report = "\t:white_small_square:".join(branch_reports) + blocks += [ + { + "type": "header", + "text": { + "type": "plain_text", + "text": f"{project}", + "emoji": True + } + }, + { + "type": "section", + "text": + { + "type": "mrkdwn", + "text": project_report + } + }, + { + "type": "divider" + }, + ] + return blocks + + +def gitlab_status_blocks(): + blocks = [ + { + "type": "header", + "text": { + "type": "plain_text", + "text": "PROJECTS IN GITLAB:", + "emoji": True + } + }, + ] projects = { "django-remote-submission": 10248, "drtSANS": 4955, @@ -102,6 +194,14 @@ def get_status_blocks(): ] return blocks + +def get_status_blocks(): + blocks = legend() + blocks += gitlab_status_blocks() + blocks += github_status_blocks() + return blocks + + # Install the Slack app and get xoxb- token in advance app = App(token=os.environ["SLACK_BOT_TOKEN"]) @@ -112,12 +212,6 @@ def hello_command(ack, body): ack(f"Hi, <@{user_id}>!") -r""" -@app.event("app_mention") -def event_test(say): - say("Hi there!") -""" - @app.event("app_mention") def greetings(ack, say): ack() -- GitLab From d4913ffb1264b4ebaef7daecbfbb018dd9f3a279 Mon Sep 17 00:00:00 2001 From: Jose Borreguero <borreguero@gmail.com> Date: Tue, 27 Dec 2022 15:46:15 -0500 Subject: [PATCH 4/4] subsitute app with mentionbot Signed-off-by: Jose Borreguero <borreguero@gmail.com> --- app.py | 190 +++++++++++++++++++++++++++++++++++------- mentionbot.py | 224 -------------------------------------------------- 2 files changed, 159 insertions(+), 255 deletions(-) delete mode 100644 mentionbot.py diff --git a/app.py b/app.py index dabeb7e..8c71d55 100644 --- a/app.py +++ b/app.py @@ -1,31 +1,150 @@ -import os -import requests -# Use the package we installed -from dotenv import load_dotenv +# Tutorial https://api.slack.com/tutorials/tracks/responding-to-app-mentions + +# third party imports from slack_bolt import App from slack_bolt.adapter.socket_mode import SocketModeHandler -#load_dotenv('./env/.env') -load_dotenv() +# standard imports +from dotenv import load_dotenv +import logging +import os +import requests -# Initializes your app with your bot token and signing secret -app = App(token=os.environ["SLACK_BOT_TOKEN"], name="Amaterasu") + +logger = logging.getLogger(__name__) +load_dotenv() status2emoji = { "success": ":white_check_mark:", "failed": ":x:", - "running": ":hourglass:", + "failure": ":x:", "error": ":ghost:", + "stale": ":ghost:", + "skipped": ":ghost:", + "timed_out": "ghost", + "cancelled": "ghost", + "completed": "ghost", + "running": ":hourglass:", + "in_progress": ":hourglass:", + "queued": ":hourglass:", + "requested": ":hourglass:", + "waiting": ":hourglass:", + "action_required": ":hourglass:", } -def get_status_blocks(): - blocks = [] - # + +def legend(): + r"""Explanation of emojis""" + emojis = { + "success": ":white_check_mark:", + "uncomplete": ":hourglass:", + "failure": ":x:", + "other error": ":ghost:", + } + _legend = "\t:white_small_square:".join([f"{status}: {emoji}" for status, emoji in emojis.items()]) + block = [ + { + "type": "header", + "text": { + "type": "plain_text", + "text": "LEGEND", + "emoji": True + } + }, + { + "type": "section", + "text": + { + "type": "mrkdwn", + "text": _legend + } + }, + { + "type": "divider" + }, + ] + return block + + +def github_status_blocks(): + blocks = [ + { + "type": "header", + "text": { + "type": "plain_text", + "text": "PROJECTS IN GITHUB:", + "emoji": True + } + }, + ] + # Organization, Repository, Workflow-Name + WORKFLOW_ANY = "*" # any workflow is fine for testing + query_github = [ + ("neutrons", "addie", WORKFLOW_ANY), + ("neutrons", "data_workflow", WORKFLOW_ANY), + ("ornlneutronimaging", "iMars3D", WORKFLOW_ANY), + ("neutrons", "mantid_total_scattering", WORKFLOW_ANY), + ("neutrons", "PyRS", WORKFLOW_ANY), + ("neutronimaging", "python_notebooks", WORKFLOW_ANY), + ("neutrons", "reflectivity_ui", WORKFLOW_ANY), + ("neutrons", "RefRed", WORKFLOW_ANY), + ] + for owner, project, workflow_name in query_github: + branch_reports = list() + url = f"https://api.github.com/repos/{owner}/{project}/actions/runs" + for branch in ["next", "qa", "main"]: + response = requests.get(url, params={"branch": branch}) + run_status = "error" # default branch report + if response.ok: + for run in response.json()["workflow_runs"]: + if workflow_name in (WORKFLOW_ANY, run["name"]): + # run["conclusion"] is None for uncompleted jobs + run_status = run["conclusion"] if run["conclusion"] is not None else run["status"] + break + else: + logger.error(f"Request GET failed for {owner}/{project}?parameter=branch={branch}") + branch_reports.append(f"*{branch}* {status2emoji.get(run_status, ':ghost:')}") + project_report = "\t:white_small_square:".join(branch_reports) + blocks += [ + { + "type": "header", + "text": { + "type": "plain_text", + "text": f"{project}", + "emoji": True + } + }, + { + "type": "section", + "text": + { + "type": "mrkdwn", + "text": project_report + } + }, + { + "type": "divider" + }, + ] + return blocks + + +def gitlab_status_blocks(): + blocks = [ + { + "type": "header", + "text": { + "type": "plain_text", + "text": "PROJECTS IN GITLAB:", + "emoji": True + } + }, + ] projects = { - "drtSANS": 4955, - "Web_Reflectivity": 5380, "django-remote-submission": 10248, + "drtSANS": 4955, "Web Monitor Test AMQ Message Generator": 10770, + "Web_Reflectivity": 5380, } for project, project_id in projects.items(): branches_status = { @@ -52,32 +171,42 @@ def get_status_blocks(): print(status) branches_status[branch] = status_emoji # build the block + status_report = "\t:white_small_square:".join([f"*{branch}* {branches_status[branch]}" + for branch in ["next", "qa", "main"]]) blocks += [ - { - "type": "divider" - }, { "type": "header", "text": { "type": "plain_text", "text": f"{project}", "emoji": True - } - }, - { - "type": "divider" - }, - { - "type": "section", - "text": { - "type": "mrkdwn", - "text": f"*next* {branches_status['next']}\t:white_small_square:\t*qa*{branches_status['qa']}\t:white_small_square:\t*main*{branches_status['main']}" - } - }, + } + }, + { + "type": "section", + "text": + { + "type": "mrkdwn", + "text": status_report + } + }, + { + "type": "divider" + }, ] return blocks -# Add functionality here + +def get_status_blocks(): + blocks = legend() + blocks += gitlab_status_blocks() + blocks += github_status_blocks() + return blocks + + +# Install the Slack app and get xoxb- token in advance +app = App(token=os.environ["SLACK_BOT_TOKEN"], name="Amaterasu") + @app.event("app_mention") def greetings(ack, say): ack() @@ -90,4 +219,3 @@ def greetings(ack, say): if __name__ == "__main__": handler = SocketModeHandler(app, os.environ["SLACK_APP_TOKEN"]) handler.start() - diff --git a/mentionbot.py b/mentionbot.py deleted file mode 100644 index 97f7a33..0000000 --- a/mentionbot.py +++ /dev/null @@ -1,224 +0,0 @@ -# Tutorial https://api.slack.com/tutorials/tracks/responding-to-app-mentions - -# third party imports -from slack_bolt import App -from slack_bolt.adapter.socket_mode import SocketModeHandler - -# standard imports -import logging -import os -import requests - - -logger = logging.getLogger(__name__) - -status2emoji = { - "success": ":white_check_mark:", - "failed": ":x:", - "failure": ":x:", - "error": ":ghost:", - "stale": ":ghost:", - "skipped": ":ghost:", - "timed_out": "ghost", - "cancelled": "ghost", - "completed": "ghost", - "running": ":hourglass:", - "in_progress": ":hourglass:", - "queued": ":hourglass:", - "requested": ":hourglass:", - "waiting": ":hourglass:", - "action_required": ":hourglass:", -} - - -def legend(): - r"""Explanation of emojis""" - emojis = { - "success": ":white_check_mark:", - "uncomplete": ":hourglass:", - "failure": ":x:", - "other error": ":ghost:", - } - _legend = "\t:white_small_square:".join([f"{status}: {emoji}" for status, emoji in emojis.items()]) - block = [ - { - "type": "header", - "text": { - "type": "plain_text", - "text": "LEGEND", - "emoji": True - } - }, - { - "type": "section", - "text": - { - "type": "mrkdwn", - "text": _legend - } - }, - { - "type": "divider" - }, - ] - return block - - -def github_status_blocks(): - blocks = [ - { - "type": "header", - "text": { - "type": "plain_text", - "text": "PROJECTS IN GITHUB:", - "emoji": True - } - }, - ] - # Organization, Repository, Workflow-Name - WORKFLOW_ANY = "*" # any workflow is fine for testing - query_github = [ - ("neutrons", "addie", WORKFLOW_ANY), - ("neutrons", "data_workflow", WORKFLOW_ANY), - ("ornlneutronimaging", "iMars3D", WORKFLOW_ANY), - ("neutrons", "mantid_total_scattering", WORKFLOW_ANY), - ("neutrons", "PyRS", WORKFLOW_ANY), - ("neutronimaging", "python_notebooks", WORKFLOW_ANY), - ("neutrons", "reflectivity_ui", WORKFLOW_ANY), - ("neutrons", "RefRed", WORKFLOW_ANY), - ] - for owner, project, workflow_name in query_github: - branch_reports = list() - url = f"https://api.github.com/repos/{owner}/{project}/actions/runs" - for branch in ["next", "qa", "main"]: - response = requests.get(url, params={"branch": branch}) - run_status = "error" # default branch report - if response.ok: - for run in response.json()["workflow_runs"]: - if workflow_name in (WORKFLOW_ANY, run["name"]): - # run["conclusion"] is None for uncompleted jobs - run_status = run["conclusion"] if run["conclusion"] is not None else run["status"] - break - else: - logger.error(f"Request GET failed for {owner}/{project}?parameter=branch={branch}") - branch_reports.append(f"*{branch}* {status2emoji.get(run_status, ':ghost:')}") - project_report = "\t:white_small_square:".join(branch_reports) - blocks += [ - { - "type": "header", - "text": { - "type": "plain_text", - "text": f"{project}", - "emoji": True - } - }, - { - "type": "section", - "text": - { - "type": "mrkdwn", - "text": project_report - } - }, - { - "type": "divider" - }, - ] - return blocks - - -def gitlab_status_blocks(): - blocks = [ - { - "type": "header", - "text": { - "type": "plain_text", - "text": "PROJECTS IN GITLAB:", - "emoji": True - } - }, - ] - projects = { - "django-remote-submission": 10248, - "drtSANS": 4955, - "Web Monitor Test AMQ Message Generator": 10770, - "Web_Reflectivity": 5380, - } - for project, project_id in projects.items(): - branches_status = { - "next": "error", - "qa": "error", - "main": "error", - } - url = f"https://code.ornl.gov/api/v4/projects/{project_id}/pipelines" - for branch, status in branches_status.items(): - response = requests.get(url, params={"ref": branch}) - if response.status_code == 200: - try: - status = response.json()[0]["status"] - except: - print(project) - print(branch) - print(response.json()) - try: - status_emoji = status2emoji[status] - except: - print(project) - print(branch) - print(response.json()) - print(status) - branches_status[branch] = status_emoji - # build the block - status_report = "\t:white_small_square:".join([f"*{branch}* {branches_status[branch]}" - for branch in ["next", "qa", "main"]]) - blocks += [ - { - "type": "header", - "text": { - "type": "plain_text", - "text": f"{project}", - "emoji": True - } - }, - { - "type": "section", - "text": - { - "type": "mrkdwn", - "text": status_report - } - }, - { - "type": "divider" - }, - ] - return blocks - - -def get_status_blocks(): - blocks = legend() - blocks += gitlab_status_blocks() - blocks += github_status_blocks() - return blocks - - -# Install the Slack app and get xoxb- token in advance -app = App(token=os.environ["SLACK_BOT_TOKEN"]) - - -@app.command("/hello-socket-mode") -def hello_command(ack, body): - user_id = body["user_id"] - ack(f"Hi, <@{user_id}>!") - - -@app.event("app_mention") -def greetings(ack, say): - ack() - say("Build status summary for Neutron projects:") - status_blocks = {"blocks": get_status_blocks()} - say(status_blocks) - - -if __name__ == "__main__": - SocketModeHandler(app, os.environ["SLACK_APP_TOKEN"]).start() -- GitLab