aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJan200101 <sentrycraft123@gmail.com>2022-03-29 11:35:51 +0200
committerJan200101 <sentrycraft123@gmail.com>2022-03-29 11:38:27 +0200
commit0ffe0731e6aca624695dd528761d2e459256ee67 (patch)
tree124b14d70b237289b1104aca76a0386557cebf7c
downloadlab-bot-0ffe0731e6aca624695dd528761d2e459256ee67.tar.gz
lab-bot-0ffe0731e6aca624695dd528761d2e459256ee67.zip
Initial commit0.0.0
-rw-r--r--.gitignore6
-rw-r--r--LICENSE22
-rw-r--r--MANIFEST.in3
-rw-r--r--README.md3
-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
-rw-r--r--requirements.txt3
-rwxr-xr-xsetup.py58
11 files changed, 289 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..7236703
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,6 @@
+env
+build
+dist
+*.egg-info
+__pycache__
+*.pyc
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..1b5a336
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,22 @@
+MIT License
+
+Copyright (c) 2022 Jan Drögehoff
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644
index 0000000..bb910eb
--- /dev/null
+++ b/MANIFEST.in
@@ -0,0 +1,3 @@
+include README.md
+include LICENSE
+include requirements.txt
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..0a334f8
--- /dev/null
+++ b/README.md
@@ -0,0 +1,3 @@
+# lab-bot
+
+a modular gitlab bot oriented towards automation
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
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..12b02db
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,3 @@
+gidgetlab[aiohttp]
+appdirs
+click
diff --git a/setup.py b/setup.py
new file mode 100755
index 0000000..3c5c1e9
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,58 @@
+# -*- coding: utf-8 -*-
+import setuptools
+import re
+
+requirements = []
+with open('requirements.txt') as f:
+ requirements = f.read().splitlines()
+
+version = None
+with open('labbot/__init__.py') as f:
+ version = re.search(r'^__version__\s*=\s*[\'"]([^\'"]*)[\'"]', f.read(), re.MULTILINE).group(1)
+
+if not version:
+ raise RuntimeError('version is not set')
+
+long_description = ""
+with open("README.md", "r") as fh:
+ long_description = fh.read()
+
+
+setuptools.setup(
+ name="lab-bot",
+ version=version,
+ author="Jan Drögehoff",
+ author_email="jandroegehoff@gmail.com",
+ description="a modular gitlab bot oriented towards automation",
+ long_description=long_description,
+ long_description_content_type="text/markdown",
+ url="",
+ packages=[
+ "labbot",
+ "labbot.addons"
+ ],
+ license="MIT",
+ install_requires=requirements,
+ include_package_data=True,
+ classifiers=[
+ "Development Status :: 3 - Alpha",
+
+ "Programming Language :: Python :: 3",
+
+ "License :: OSI Approved :: MIT License",
+
+ "Operating System :: POSIX",
+ "Operating System :: POSIX :: BSD",
+ "Operating System :: POSIX :: Linux",
+
+ "Topic :: Software Development :: Libraries :: Python Modules",
+ ],
+ python_requires=">=3.6",
+
+ entry_points={
+ 'console_scripts': [
+ 'lab-bot=labbot.__main__:main',
+ ],
+ },
+
+)