aboutsummaryrefslogtreecommitdiff
path: root/labbot
diff options
context:
space:
mode:
Diffstat (limited to 'labbot')
-rw-r--r--labbot/__init__.py2
-rw-r--r--labbot/__main__.py79
-rw-r--r--labbot/addons/merge-label.py73
-rw-r--r--labbot/bot.py13
-rw-r--r--labbot/config.py27
5 files changed, 194 insertions, 0 deletions
diff --git a/labbot/__init__.py b/labbot/__init__.py
new file mode 100644
index 0000000..9561ba9
--- /dev/null
+++ b/labbot/__init__.py
@@ -0,0 +1,2 @@
+
+__version__ = "0.0.0"
diff --git a/labbot/__main__.py b/labbot/__main__.py
new file mode 100644
index 0000000..5e0a3ae
--- /dev/null
+++ b/labbot/__main__.py
@@ -0,0 +1,79 @@
+import click
+from typing import List
+from importlib import import_module
+
+from labbot.config import read_config, write_config
+from labbot.bot import Bot
+
+@click.group()
+def main():
+ pass
+
+@main.command(help="Create a new instance")
+@click.option("--name", prompt=True, help="Name the instance will be given")
+@click.option("--access_token", prompt=True, hide_input=True, help="Access Token to interact with the API")
+@click.option("--secret", prompt=True, default="", help="Secret to receive webhook requests [can be empty]")
+def setup(**data):
+ instance_name = data.pop("name", "").replace(" ", "_").lower()
+
+ if not read_config(instance_name):
+ write_config(instance_name, data)
+ click.echo(f"You can start your instance by running `lab-bot run {instance_name}`")
+ else:
+ click.echo(f"an instance with the name {instance_name} already exists")
+
+@main.command(help="Configure an existing instance")
+@click.argument('name')
+@click.option("--access_token", required=False)
+@click.option("--secret", required=False)
+def config(name, **data):
+
+ data = {k:v for k,v in data.items() if v}
+
+ conf = read_config(name)
+ if conf:
+ if data:
+ conf.update(data)
+ write_config(name, conf)
+ click.echo("configured")
+ else:
+ click.echo(f"nothing to change")
+ else:
+ click.echo(f"{name} is not an instance")
+ pass
+
+
+def load_addons(instance: Bot, addons: List[str]):
+ for addon in addons:
+ import_module(f"labbot.addons.{addon}").setup(instance)
+ click.echo(f"Imported {addon}")
+
+@main.command(help="Run an instance")
+@click.option("--port", default=8080)
+@click.argument('name')
+def run(name, port):
+ conf = read_config(name)
+
+ if not conf:
+ click.echo(f"{name} is not an instance")
+ return
+
+ instance = Bot(
+ secret=conf["secret"],
+ access_token=conf["access_token"]
+ )
+
+ load_addons(instance, [
+ "merge-label"
+ ])
+
+ click.echo(f"Started {name}")
+
+ instance.run(
+ port=port
+ )
+
+
+
+if __name__ == "__main__":
+ main() \ No newline at end of file
diff --git a/labbot/addons/merge-label.py b/labbot/addons/merge-label.py
new file mode 100644
index 0000000..e980e6c
--- /dev/null
+++ b/labbot/addons/merge-label.py
@@ -0,0 +1,73 @@
+import os
+import re
+
+title_regex = r"^(?:#|)(\d+)\s*"
+word_regex = r"^#(\d+)$"
+relation_keywords = [
+ "related"
+]
+relation_distance = 2
+
+state_label = {
+ "opened": "Code-Review",
+ "merged": "Testing",
+ "closed": "In Progress",
+}
+async def merge_request_hook(event, gl, *args, **kwargs):
+ state = event.object_attributes["state"]
+ related_issues = []
+
+ match = re.search(title_regex, event.object_attributes["title"])
+ if match:
+ related_issues.append(match.group(1))
+
+ for line in event.object_attributes["description"].split("\\n"):
+ line = line.lower()
+ line_list = line.split(" ")
+
+ for keyword in relation_keywords:
+ try:
+ keyword_index = line_list.index(keyword)
+
+ min_pos = keyword_index - relation_distance
+ if min_pos < 0:
+ min_pos = 0
+
+ max_pos = keyword_index + relation_distance
+ if max_pos >= len(line_list):
+ max_pos = len(line_list)-1
+
+ for word in line_list[min_pos:max_pos+1]:
+ match = re.search(word_regex, word)
+ if match:
+ related_issues.append(match.group(1))
+
+ except ValueError:
+ pass
+
+ for issue in related_issues:
+ if not issue: continue
+
+ base_url = f"/projects/{event.project_id}/issues/{issue}"
+
+ delete_labels = list(state_label.values())
+
+ try:
+ label = state_label[state]
+ if label in delete_labels:
+ delete_labels.remove(label)
+
+ remove_labels = ",".join(delete_labels)
+
+ await gl.put(base_url, data={
+ "add_labels": label,
+ "remove_labels": remove_labels,
+ })
+
+ except KeyError:
+ # unknown state
+ pass
+
+def setup(bot):
+ bot.register(merge_request_hook, "Merge Request Hook")
+ pass \ No newline at end of file
diff --git a/labbot/bot.py b/labbot/bot.py
new file mode 100644
index 0000000..3ea1929
--- /dev/null
+++ b/labbot/bot.py
@@ -0,0 +1,13 @@
+from gidgetlab.aiohttp import GitLabBot
+
+class Bot:
+
+ def __init__(self, *args, **kwargs):
+ self.instance = GitLabBot("lab-bot", **kwargs)
+ pass
+
+ def register(self, func, *args, **kwargs):
+ return self.instance.router.register(*args, **kwargs)(func)
+
+ def run(self, *args, **kwargs):
+ self.instance.run(*args, **kwargs) \ No newline at end of file
diff --git a/labbot/config.py b/labbot/config.py
new file mode 100644
index 0000000..3e8bcba
--- /dev/null
+++ b/labbot/config.py
@@ -0,0 +1,27 @@
+import os
+import json
+from appdirs import user_config_dir
+
+def config_dir() -> str:
+ path = user_config_dir("labbot")
+ os.makedirs(path, exist_ok=True)
+
+ return path
+
+def write_config(name: str, data: dict) -> None:
+ conf_path = os.path.join(config_dir(), f"{name}.json")
+
+ with open(conf_path, "w") as file:
+ json.dump(data, file)
+
+ if data != read_config(name):
+ raise ValueError("Config could not be saved properly")
+
+def read_config(name: str) -> dict:
+ conf_path = os.path.join(config_dir(), f"{name}.json")
+
+ try:
+ with open(conf_path, "r") as file:
+ return json.load(file)
+ except FileNotFoundError:
+ return {} \ No newline at end of file