aboutsummaryrefslogtreecommitdiff
path: root/labbot
diff options
context:
space:
mode:
Diffstat (limited to 'labbot')
-rw-r--r--labbot/__main__.py61
-rw-r--r--labbot/addons/approve-merge.py10
-rw-r--r--labbot/addons/merge-label.py4
-rw-r--r--labbot/bot.py42
-rw-r--r--labbot/config.py34
-rw-r--r--labbot/logger.py19
-rw-r--r--labbot/py.typed0
7 files changed, 133 insertions, 37 deletions
diff --git a/labbot/__main__.py b/labbot/__main__.py
index 9f0e3ba..57ffbcd 100644
--- a/labbot/__main__.py
+++ b/labbot/__main__.py
@@ -1,9 +1,16 @@
import click
+import logging
from typing import List
from importlib import import_module
-from labbot.config import read_config, write_config
-from labbot.bot import Bot
+import labbot.bot
+import labbot.config
+import labbot.logger
+
+DEFAULT_ADDONS = [
+ "merge-label",
+ "approve-merge"
+]
@click.group()
def main():
@@ -11,13 +18,15 @@ def main():
@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]")
+@click.option("--access_token", prompt="Access Token for the Bot account", hide_input=True, help="Access Token to interact with the API")
+@click.option("--secret", prompt="Webhook Secret (can be empty)", default="", help="Secret to receive webhook requests (can be empty)")
def setup(**data):
instance_name = data.pop("name", "").replace(" ", "_").lower()
+ data["addons"] = DEFAULT_ADDONS
+ data["addon_path"] = ""
- if not read_config(instance_name):
- write_config(instance_name, data)
+ if not labbot.config.read_instance_config(instance_name):
+ labbot.config.write_instance_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")
@@ -30,11 +39,11 @@ def config(name, **data):
data = {k:v for k,v in data.items() if v}
- conf = read_config(name)
+ conf = labbot.config.read_instance_config(name)
if conf:
if data:
conf.update(data)
- write_config(name, conf)
+ labbot.config.write_instance_config(name, conf)
click.echo("configured")
else:
click.echo(f"nothing to change")
@@ -43,38 +52,40 @@ def config(name, **data):
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.option("--port", default=8080, show_default=True, help="change the webhook port")
+@click.option("--debug", is_flag=True, default=False, help="enable debug logging")
@click.argument('name')
-def run(name, port):
- conf = read_config(name)
+def run(name, port, debug: bool):
+ conf = labbot.config.read_instance_config(name)
if not conf:
click.echo(f"{name} is not an instance")
return
- instance = Bot(
+ if debug:
+ logger_level = logging.DEBUG
+ else:
+ logger_level = logging.INFO
+
+ labbot.logger.init(logger_level)
+
+ instance = labbot.bot.Bot(
+ name=name,
+ config=conf,
secret=conf["secret"],
access_token=conf["access_token"]
)
- load_addons(instance, [
- "merge-label",
- "approve-merge"
- ])
-
- click.echo(f"Started {name}")
-
instance.run(
port=port
)
-
+@main.command(name="list", help="List all available instances")
+def list_instances():
+ print("Available Instances:")
+ for ins in labbot.config.list_instances():
+ print(f"- {ins}")
if __name__ == "__main__":
main() \ No newline at end of file
diff --git a/labbot/addons/approve-merge.py b/labbot/addons/approve-merge.py
index 0fdeaa9..3c0ef30 100644
--- a/labbot/addons/approve-merge.py
+++ b/labbot/addons/approve-merge.py
@@ -2,6 +2,9 @@
automatically merges merge requests if a certain number of approvals
is reached.
"""
+import logging
+
+log = logging.getLogger(__name__)
required_approval_count = 2
@@ -9,12 +12,13 @@ async def merge_label_hook(event, gl, *args, **kwargs):
title = event.object_attributes["title"]
state = event.object_attributes["state"]
merge_status = event.object_attributes["merge_status"]
+ iid = event.object_attributes['iid']
if state != "opened" or title.lower().startswith("draft"):
return
- approvals_url = f"/projects/{event.project_id}/merge_requests/{event.object_attributes['iid']}/approvals"
- merge_url = f"/projects/{event.project_id}/merge_requests/{event.object_attributes['iid']}/merge"
+ approvals_url = f"/projects/{event.project_id}/merge_requests/{iid}/approvals"
+ merge_url = f"/projects/{event.project_id}/merge_requests/{iid}/merge"
data = await gl.getitem(approvals_url)
@@ -24,6 +28,8 @@ async def merge_label_hook(event, gl, *args, **kwargs):
if merge_status == "can_be_merged":
print("test")
await gl.put(merge_url)
+ else:
+ log.debug(f"Cannot merge !{iid} because of its merge_statuc `{merge_status}`")
def setup(bot):
bot.register(merge_label_hook, "Merge Request Hook")
diff --git a/labbot/addons/merge-label.py b/labbot/addons/merge-label.py
index 9379b59..4798d4a 100644
--- a/labbot/addons/merge-label.py
+++ b/labbot/addons/merge-label.py
@@ -5,6 +5,10 @@ specified labels.
import os
import re
+import logging
+
+log = logging.getLogger(__name__)
+
title_regex = r"^(?:#|)(\d+)\s*"
word_regex = r"^#(\d+)$"
diff --git a/labbot/bot.py b/labbot/bot.py
index 3ea1929..1361656 100644
--- a/labbot/bot.py
+++ b/labbot/bot.py
@@ -1,13 +1,51 @@
+import os.path
+import sys
from gidgetlab.aiohttp import GitLabBot
+from importlib import import_module
+import logging
+
+import labbot
+import labbot.config
+
+log = logging.getLogger(__name__)
class Bot:
def __init__(self, *args, **kwargs):
- self.instance = GitLabBot("lab-bot", **kwargs)
- pass
+ self.name = kwargs.get("name", "lab-bot")
+ self.secret = kwargs.get("secret", "")
+ self.config = kwargs.get("config", labbot.config.read_instance_config(self.name))
+
+ self.addons = self.config.get("addons", [])
+ self.addon_paths = []
+
+ self.core_addon_path = os.path.join(os.path.dirname(labbot.__file__), "addons")
+ self.addon_paths.append(self.core_addon_path)
+ if "addon_path" in self.config:
+ self.addon_paths.append(self.config.get("addon_path"))
+
+ self.instance = GitLabBot(self.name, **kwargs)
+
+ for path in self.addon_paths:
+ sys.path.insert(0, path)
+
+ for addon in self.addons:
+ self.load_addon(addon)
+
+ for path in self.addon_paths:
+ sys.path.remove(path)
+
+
+ def load_addon(self, addon: str):
+ try:
+ import_module(f"{addon}").setup(self)
+ log.info(f"Loaded {addon}")
+ except ModuleNotFoundError:
+ log.error(f"No addon named `{addon}`")
def register(self, func, *args, **kwargs):
return self.instance.router.register(*args, **kwargs)(func)
def run(self, *args, **kwargs):
+ log.info(f"Started {self.name}")
self.instance.run(*args, **kwargs) \ No newline at end of file
diff --git a/labbot/config.py b/labbot/config.py
index 3e8bcba..10858ea 100644
--- a/labbot/config.py
+++ b/labbot/config.py
@@ -1,27 +1,45 @@
import os
import json
-from appdirs import user_config_dir
+from typing import List
+from appdirs import user_config_dir # type: ignore
+
+CONFIG_FILE = "config.json"
+
+def list_instances() -> List[str]:
+ instances = []
+ with os.scandir(config_dir()) as it:
+ for entry in it:
+ if entry.is_dir() and os.path.isfile(f"{entry.path}/{CONFIG_FILE}"):
+ instances.append(entry.name)
+ return instances
def config_dir() -> str:
- path = user_config_dir("labbot")
+ path: str = 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")
+def instance_config_dir(name) -> str:
+ path = os.path.join(config_dir(), name)
+
+ return path
+
+def write_instance_config(name: str, data: dict) -> None:
+ instance_path = instance_config_dir(name)
+ os.makedirs(instance_path, exist_ok=True)
+ conf_path = os.path.join(instance_path, CONFIG_FILE)
with open(conf_path, "w") as file:
json.dump(data, file)
- if data != read_config(name):
+ if data != read_instance_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")
+def read_instance_config(name: str) -> dict:
+ conf_path = os.path.join(instance_config_dir(name), CONFIG_FILE)
try:
with open(conf_path, "r") as file:
return json.load(file)
except FileNotFoundError:
- return {} \ No newline at end of file
+ return {}
diff --git a/labbot/logger.py b/labbot/logger.py
new file mode 100644
index 0000000..067bcac
--- /dev/null
+++ b/labbot/logger.py
@@ -0,0 +1,19 @@
+import sys
+import logging
+
+def init(level: int):
+ logger = logging.getLogger()
+ logger.setLevel(level)
+
+ formatter = logging.Formatter(
+ "[{asctime}] [{levelname}] {name}: {message}", datefmt="%Y-%m-%d %H:%M:%S", style="{"
+ )
+
+ if not sys.stdout.closed:
+ stdout_handler = logging.StreamHandler(sys.stdout)
+ stdout_handler.setFormatter(formatter)
+
+ logger.addHandler(stdout_handler)
+
+
+ logging.captureWarnings(True) \ No newline at end of file
diff --git a/labbot/py.typed b/labbot/py.typed
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/labbot/py.typed