From 8555c8a0f941b85a6e8122f7373c0074b5dbc134 Mon Sep 17 00:00:00 2001
From: Stefan Haun <tux@netz39.de>
Date: Thu, 5 Nov 2020 20:55:22 +0100
Subject: [PATCH 01/35] Add endpoint for validation

---
 OAS3.yml | 44 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 44 insertions(+)

diff --git a/OAS3.yml b/OAS3.yml
index 16c65a6..85e9891 100644
--- a/OAS3.yml
+++ b/OAS3.yml
@@ -200,6 +200,36 @@ paths:
         '404':
           $ref: '#/components/responses/NotFound'
 
+  /validate:
+    parameters:
+      - in: header
+        name: Authentication
+        schema:
+          type: string
+        description: Authentication token
+    post:
+      summary: Validate an entity
+      tags:
+        - entities
+      requestBody:
+        content:
+          application/json:
+            schema:
+              type: object
+              description: Entity JSON
+      responses:
+        '200':
+          description: Validation result
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/validation'
+        '400':
+          $ref: '#/components/responses/InvalidInput'
+        '401':
+          $ref: '#/components/responses/AuthenticationRequired'
+        '403':
+          $ref: '#/components/responses/NotAllowed'
 components:
   schemas:
     health:
@@ -215,6 +245,20 @@ components:
         uptime:
           type: string
           example: ISO8601 conforming timespan
+    validation:
+      type: object
+      properties:
+        valid:
+          type: boolean
+        findings:
+          type: array
+          items:
+            type: object
+            properties:
+              field:
+                type: string
+              message:
+                type: string
   responses:
     AuthenticationRequired:
       description: Authentication is required (401)

From 6a53c6e6703ee25be5e3692849d05ec602c5341f Mon Sep 17 00:00:00 2001
From: Stefan Haun <tux@netz39.de>
Date: Tue, 22 Dec 2020 14:37:45 +0100
Subject: [PATCH 02/35] Add endpoints to upload/download application/sepa for
 members

---
 OAS3.yml | 79 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 78 insertions(+), 1 deletion(-)

diff --git a/OAS3.yml b/OAS3.yml
index 16c65a6..501f221 100644
--- a/OAS3.yml
+++ b/OAS3.yml
@@ -200,6 +200,84 @@ paths:
         '404':
           $ref: '#/components/responses/NotFound'
 
+  /document/{id}/{type}:
+    parameters:
+      - in: path
+        name: id
+        required: true
+        schema:
+          type: string
+        description: Entity ID
+      - in: path
+        name: type
+        required: true
+        schema:
+          type: string
+          enum: [application, sepa]
+        description: Type of document to upload
+      - in: header
+        name: Authentication
+        schema:
+          type: string
+        description: Authentication token
+    post:
+      summary: Upload a PDF document for a member
+      description: Note that the entry must be updated with the URI obtained from this call
+      tags:
+        - document
+      requestBody:
+        description: The document
+        content:
+          'application/pdf':
+            schema:
+              type: string
+              format: binary
+      responses:
+        '201':
+          description: File has been stored ("created") locally, returns the URI for downloading the file
+          content:
+            text/plain:
+              schema:
+                type: string
+                format: uri
+        '303':
+          description: The file is already in storage, returns the URI for downloading the file
+          content:
+            text/plain:
+              schema:
+                type: string
+                format: uri
+        '401':
+          $ref: '#/components/responses/AuthenticationRequired'
+        '403':
+          $ref: '#/components/responses/NotAllowed'
+        '405':
+          $ref: '#/components/responses/InvalidInput'
+        '500':
+          $ref: '#/components/responses/InternalError'
+    get:
+      summary: Get a PDF document for a member
+      tags:
+        - document
+      responses:
+        '200':
+          description: Returns PDF data
+          content:
+            'application/pdf':
+              schema:
+                type: string
+                format: binary
+        '404':
+          $ref: '#/components/responses/NotFound'
+        '401':
+          $ref: '#/components/responses/AuthenticationRequired'
+        '403':
+          $ref: '#/components/responses/NotAllowed'
+        '405':
+          $ref: '#/components/responses/InvalidInput'
+        '500':
+          $ref: '#/components/responses/InternalError'
+
 components:
   schemas:
     health:
@@ -236,4 +314,3 @@ components:
           schema:
             type: string
             example: error message
-

From 186e5ac2abafe3c2e603294627d3a98a4205a624 Mon Sep 17 00:00:00 2001
From: Stefan Haun <tux@netz39.de>
Date: Thu, 11 Feb 2021 20:58:17 +0100
Subject: [PATCH 03/35] Add GitPython requirement

---
 requirements.txt | 1 +
 1 file changed, 1 insertion(+)

diff --git a/requirements.txt b/requirements.txt
index 757931f..b803341 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,3 +1,4 @@
 tornado==6.0.4
 isodate==0.6.0
 pytest==5.4.1
+GitPython==3.1.12
\ No newline at end of file

From 234421fb6467055c9fce9c2a0806aae1bec8144b Mon Sep 17 00:00:00 2001
From: Stefan Haun <tux@netz39.de>
Date: Thu, 11 Feb 2021 20:58:42 +0100
Subject: [PATCH 04/35] Add the GitManager with configuration and repository
 setup

---
 gitmgr.py | 150 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 150 insertions(+)
 create mode 100644 gitmgr.py

diff --git a/gitmgr.py b/gitmgr.py
new file mode 100644
index 0000000..eef02d7
--- /dev/null
+++ b/gitmgr.py
@@ -0,0 +1,150 @@
+import git
+import os
+import shutil
+import tempfile
+
+from util import load_env
+
+
+class GitManagerConfiguration:
+    @staticmethod
+    def from_environment():
+        origin = load_env("GIT_ORIGIN", None)
+        wc_path = load_env("GIT_WC_PATH", None)
+        git_pw = load_env("GIT_PASSWORD", None)
+
+        return GitManagerConfiguration(origin=origin,
+                                       git_pw=git_pw,
+                                       wc_path=wc_path)
+
+    def __init__(self, origin, git_pw=None, wc_path=None):
+        if not origin:
+            raise ValueError("Git origin cannot be empty!")
+
+        self._origin = origin
+        self._git_pw = git_pw
+        self._wc_path = wc_path
+
+    @property
+    def origin(self):
+        return self._origin
+
+    @property
+    def git_pw(self):
+        return self._git_pw
+
+    @property
+    def wc_path(self):
+        return self._wc_path
+
+
+class GitManager:
+    def __init__(self, configuration):
+        if configuration is None:
+            raise ValueError("GitManager must be initialized with a configuration!")
+
+        self._configuration = configuration
+        self._wc = None
+
+    @property
+    def configuration(self):
+        return self._configuration
+
+    def _setup_wc(self):
+        if self._wc is not None:
+            return
+
+        _wc = self.configuration.wc_path
+
+        if _wc is None:
+            _wc = tempfile.mkdtemp(prefix='entities_git_')
+
+        if not os.path.isdir(_wc):
+            raise ValueError("Configured directory for the working copy does not exist!")
+
+        self._wc = _wc
+
+    def _teardown_wc(self):
+        if self._wc is None:
+            return
+
+        if self.configuration.wc_path is not None:
+            print("NOTE: Not tearing down externally configured working copy.")
+            return
+
+        shutil.rmtree(self._wc)
+
+        self._wc = None
+
+    def _assert_wc(self):
+        """Assert working copy matches origin and is a valid repository.
+
+           A failed assertion will throw exceptions and lead to service abort,
+           as this error is not recoverable.
+
+           Returns False if the WC path is an empty directory"""
+
+        # Check if WC is empty
+        if not os.listdir(self._wc):
+            return False
+
+        # Create a repository object
+        # This fails if there is no valid repository
+        repo = git.Repo(self._wc)
+
+        # Assert that this is not a bare repo
+        if repo.bare:
+            raise ValueError("WC path points to a bare git repository!")
+
+        origin = repo.remote('origin')
+        if self.configuration.origin not in origin.urls:
+            raise ValueError("Origin URL does not match!")
+
+        # We're good here.
+        return True
+
+    def _askpass_script(self):
+        # Passwords are impossible to store in scripts, as they may contain any character ...
+        # We convert the password into a list of integers and create a little script
+        # that reconstructs the password and writes it to the console.
+        # Python will be installed anyways.
+
+        pw_chars = [ord(c) for c in self.configuration.git_pw]
+
+        script = "#!/usr/bin/env python3\n"
+        script += "l = %s\n" % str(list(pw_chars))
+        script += "p = [chr(c) for c in l]\n"
+        script += f"print(\"\".join(p))\n"
+        return script
+
+    def _init_repo(self):
+        # Assert working copy is valid,
+        # return false if cloning is necessary
+        if not self._assert_wc():
+            print("Cloning new git working copy ...")
+
+            # Create a temporary script file for GIT_ASKPASS
+            with tempfile.NamedTemporaryFile(mode='w+t') as askpass:
+                askpass.write(self._askpass_script())
+                askpass.file.close()
+                os.chmod(path=askpass.name, mode=0o700)
+                self.repo = git.Repo.clone_from(url=self.configuration.origin,
+                                                to_path=self._wc,
+                                                env={'GIT_ASKPASS': askpass.name})
+        else:
+            print("Reusing existing git working copy ...")
+            self.repo = git.Repo(self._wc)
+
+    def setup(self):
+        self._setup_wc()
+        self._init_repo()
+
+    def teardown(self):
+        self._teardown_wc()
+
+    def printout(self):
+        print("Git Manager:")
+        print(f"\tGit origin is %s" % self.configuration.origin)
+        print(f"\tUsing working copy path %s" % self._wc)
+        if not self._wc == self.configuration.wc_path:
+            print("\tUsing a temporary working copy.")

From f9a7d7fe2184618c9bda1e1054506f043aabe5fc Mon Sep 17 00:00:00 2001
From: Stefan Haun <tux@netz39.de>
Date: Thu, 11 Feb 2021 20:59:18 +0100
Subject: [PATCH 05/35] Setup the Git Manager in the main application

---
 app.py | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/app.py b/app.py
index 7bff5b8..0347d04 100644
--- a/app.py
+++ b/app.py
@@ -12,6 +12,7 @@ import json
 
 import util
 from auth import AuthProvider
+from gitmgr import GitManagerConfiguration, GitManager
 
 
 startup_timestamp = datetime.now()
@@ -83,10 +84,16 @@ def main():
     # Setup
     auth_provider = AuthProvider.from_environment()
 
+    gitcfg = GitManagerConfiguration.from_environment()
+    gitmgr = GitManager(configuration=gitcfg)
+    gitmgr.setup()
+    gitmgr.printout()
+
     util.run_tornado_server(make_app(auth_provider),
                             server_port=port)
 
     # Teardown
+    gitmgr.teardown()
 
     print("Server stopped")
 

From 78671e9ad9fd4fb60d23f851fa5aa5ec8bda3f62 Mon Sep 17 00:00:00 2001
From: Stefan Haun <tux@netz39.de>
Date: Thu, 11 Feb 2021 21:06:59 +0100
Subject: [PATCH 06/35] Add Git configuration variables to README

---
 README.md | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/README.md b/README.md
index a6d6afb..2baa98e 100644
--- a/README.md
+++ b/README.md
@@ -9,4 +9,7 @@ Query and manipulate the Netz39 entities database.
 The service is configured via the following environment variables:
 * `PORT`: Service port. defaults to 8080
 * `AUTH`: Authentication tokens, defaults to None. Example Configuration : `AUTH={"token_1": "user_1", "token_2": "user_2"}`
-
+* `GIT_ORIGIN`: URL for the origin Git repository, including the user name
+* `GIT_PASSWORD`: The git password for the user encoded in the origin URL
+* `GIT_PULL_INTV`: Time interval between automated pull operations (default: 30s)
+* `GIT_WC_PATH`: Set a path for the working copy. Will create a temporary checkout if not provided.

From 50ebba0df62343ac2d9ced589a7e82fdfe6e9333 Mon Sep 17 00:00:00 2001
From: Stefan Haun <tux@netz39.de>
Date: Fri, 12 Feb 2021 16:33:11 +0100
Subject: [PATCH 07/35] Add git pull with cooldown interval

---
 gitmgr.py | 42 ++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 40 insertions(+), 2 deletions(-)

diff --git a/gitmgr.py b/gitmgr.py
index eef02d7..43506e2 100644
--- a/gitmgr.py
+++ b/gitmgr.py
@@ -2,6 +2,7 @@ import git
 import os
 import shutil
 import tempfile
+import time
 
 from util import load_env
 
@@ -12,18 +13,21 @@ class GitManagerConfiguration:
         origin = load_env("GIT_ORIGIN", None)
         wc_path = load_env("GIT_WC_PATH", None)
         git_pw = load_env("GIT_PASSWORD", None)
+        pull_intv = load_env("GIT_PULL_INTV", None)
 
         return GitManagerConfiguration(origin=origin,
                                        git_pw=git_pw,
-                                       wc_path=wc_path)
+                                       wc_path=wc_path,
+                                       pull_intv=pull_intv)
 
-    def __init__(self, origin, git_pw=None, wc_path=None):
+    def __init__(self, origin, git_pw=None, wc_path=None, pull_intv=None):
         if not origin:
             raise ValueError("Git origin cannot be empty!")
 
         self._origin = origin
         self._git_pw = git_pw
         self._wc_path = wc_path
+        self._pull_intv = 30 if pull_intv is None else int(pull_intv)
 
     @property
     def origin(self):
@@ -37,6 +41,10 @@ class GitManagerConfiguration:
     def wc_path(self):
         return self._wc_path
 
+    @property
+    def pull_intv(self):
+        return self._pull_intv
+
 
 class GitManager:
     def __init__(self, configuration):
@@ -45,6 +53,7 @@ class GitManager:
 
         self._configuration = configuration
         self._wc = None
+        self._last_pull = 0
 
     @property
     def configuration(self):
@@ -138,6 +147,7 @@ class GitManager:
     def setup(self):
         self._setup_wc()
         self._init_repo()
+        self.pull(force=True)
 
     def teardown(self):
         self._teardown_wc()
@@ -148,3 +158,31 @@ class GitManager:
         print(f"\tUsing working copy path %s" % self._wc)
         if not self._wc == self.configuration.wc_path:
             print("\tUsing a temporary working copy.")
+
+    @property
+    def head_sha(self):
+        return None if self.repo is None else self.repo.head.object.hexsha
+
+    def pull(self, force=False):
+        """Pull from origin.
+
+        Arguments:
+            `force` -- Do a pull even though the pull interval has not elapsed
+
+        Returns: True if pull was executed
+        """
+
+        if not force and (time.time() - self._last_pull < self.configuration.pull_intv):
+            return False
+
+        self._last_pull = time.time()
+
+        old_head = self.head_sha
+
+        # get the origin
+        # (We verified during initialization that this origin exists.)
+        origin = self.repo.remote('origin')
+
+        origin.pull(rebase=True)
+
+        return self.head_sha != old_head

From b161d0d7e98c4cac0d5f3ac03dd3628f1915546b Mon Sep 17 00:00:00 2001
From: Stefan Haun <tux@netz39.de>
Date: Fri, 19 Feb 2021 16:56:52 +0100
Subject: [PATCH 08/35] Show git head in health infos

---
 app.py | 16 ++++++++++++----
 1 file changed, 12 insertions(+), 4 deletions(-)

diff --git a/app.py b/app.py
index 0347d04..cd31e35 100644
--- a/app.py
+++ b/app.py
@@ -20,8 +20,9 @@ startup_timestamp = datetime.now()
 
 class HealthHandler(tornado.web.RequestHandler, metaclass=ABCMeta):
     # noinspection PyAttributeOutsideInit
-    def initialize(self):
+    def initialize(self, sources=None):
         self.git_version = self._load_git_version()
+        self.sources = sources
 
     @staticmethod
     def _load_git_version():
@@ -53,6 +54,12 @@ class HealthHandler(tornado.web.RequestHandler, metaclass=ABCMeta):
         health['timestamp'] = isodate.datetime_isoformat(datetime.now())
         health['uptime'] = isodate.duration_isoformat(datetime.now() - startup_timestamp)
 
+        if self.sources:
+            for s in self.sources:
+                h = s()
+                if h is not None:
+                    health = {**health, **h}
+
         self.set_header("Content-Type", "application/json")
         self.write(json.dumps(health, indent=4))
         self.set_status(200)
@@ -70,10 +77,11 @@ class Oas3Handler(tornado.web.RequestHandler, metaclass=ABCMeta):
         self.finish()
 
 
-def make_app(_auth_provider=None):
+def make_app(_auth_provider=None, gitmgr=None):
     version_path = r"/v[0-9]"
     return tornado.web.Application([
-        (version_path + r"/health", HealthHandler),
+        (version_path + r"/health", HealthHandler,
+         {"sources": [lambda: {"git-head":  gitmgr.head_sha}] if gitmgr else None}),
         (version_path + r"/oas3", Oas3Handler),
     ])
 
@@ -89,7 +97,7 @@ def main():
     gitmgr.setup()
     gitmgr.printout()
 
-    util.run_tornado_server(make_app(auth_provider),
+    util.run_tornado_server(make_app(auth_provider, gitmgr),
                             server_port=port)
 
     # Teardown

From f4aa0b403d7c0632de2ef2a1d7a0b0dbdff5412c Mon Sep 17 00:00:00 2001
From: Renovate Bot <accounts+renovatebot@netz39.de>
Date: Sat, 29 Jul 2023 19:36:03 +0000
Subject: [PATCH 09/35] Add renovate.json

---
 renovate.json | 3 +++
 1 file changed, 3 insertions(+)
 create mode 100644 renovate.json

diff --git a/renovate.json b/renovate.json
new file mode 100644
index 0000000..7190a60
--- /dev/null
+++ b/renovate.json
@@ -0,0 +1,3 @@
+{
+  "$schema": "https://docs.renovatebot.com/renovate-schema.json"
+}

From 8c3d8ae96e7976c6c53a9a50cfbbf21c9f39e958 Mon Sep 17 00:00:00 2001
From: Renovate Bot <accounts+renovatebot@netz39.de>
Date: Thu, 7 Sep 2023 19:18:29 +0000
Subject: [PATCH 10/35] Update dependency pytest to v7

---
 requirements.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/requirements.txt b/requirements.txt
index b803341..cf389bc 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,4 +1,4 @@
 tornado==6.0.4
 isodate==0.6.0
-pytest==5.4.1
+pytest==7.4.2
 GitPython==3.1.12
\ No newline at end of file

From 53595bebda84b5d0ad3053dd1e18fe1b78779aad Mon Sep 17 00:00:00 2001
From: Renovate Bot <accounts+renovatebot@netz39.de>
Date: Fri, 1 Sep 2023 20:17:28 +0000
Subject: [PATCH 11/35] Update python Docker tag to v3.11

---
 Dockerfile | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Dockerfile b/Dockerfile
index 488d25c..41407f0 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -8,7 +8,7 @@ COPY . /git/
 RUN find . -type d -name .git -exec git describe --always --dirty > /git-version.txt \;
 
 
-FROM python:3.8
+FROM python:3.11
 
 EXPOSE 8080
 

From d1845d7bbaa19cd72ff95e83f1711cf44ccc018e Mon Sep 17 00:00:00 2001
From: Renovate Bot <accounts+renovatebot@netz39.de>
Date: Fri, 1 Sep 2023 19:17:29 +0000
Subject: [PATCH 12/35] Update dependency tornado to v6.3.3

---
 requirements.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/requirements.txt b/requirements.txt
index cf389bc..c3a2079 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,4 +1,4 @@
-tornado==6.0.4
+tornado==6.3.3
 isodate==0.6.0
 pytest==7.4.2
 GitPython==3.1.12
\ No newline at end of file

From deaeca5f0b045ef0210f02aa972be9cfd2299cb5 Mon Sep 17 00:00:00 2001
From: Renovate Bot <accounts+renovatebot@netz39.de>
Date: Tue, 24 Oct 2023 20:17:22 +0000
Subject: [PATCH 13/35] Update dependency pytest to v7.4.3

---
 requirements.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/requirements.txt b/requirements.txt
index c3a2079..d8380c1 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,4 +1,4 @@
 tornado==6.3.3
 isodate==0.6.0
-pytest==7.4.2
+pytest==7.4.3
 GitPython==3.1.12
\ No newline at end of file

From 1995b738686d3590597c389dae82d565d0e58e7a Mon Sep 17 00:00:00 2001
From: Renovate Bot <accounts+renovatebot@netz39.de>
Date: Tue, 3 Oct 2023 03:17:22 +0000
Subject: [PATCH 14/35] Update python Docker tag to v3.12

---
 Dockerfile | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Dockerfile b/Dockerfile
index 41407f0..45eb9ba 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -8,7 +8,7 @@ COPY . /git/
 RUN find . -type d -name .git -exec git describe --always --dirty > /git-version.txt \;
 
 
-FROM python:3.11
+FROM python:3.12
 
 EXPOSE 8080
 

From 90395cd7a3e05b23b0a3d56c462f7f6a4163e4bd Mon Sep 17 00:00:00 2001
From: Renovate Bot <accounts+renovatebot@netz39.de>
Date: Sun, 5 Nov 2023 16:17:19 +0000
Subject: [PATCH 15/35] Update dependency GitPython to v3.1.40

---
 requirements.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/requirements.txt b/requirements.txt
index d8380c1..3a34529 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,4 +1,4 @@
 tornado==6.3.3
 isodate==0.6.0
 pytest==7.4.3
-GitPython==3.1.12
\ No newline at end of file
+GitPython==3.1.40
\ No newline at end of file

From d3badfe3d4a2b371c606c107b2d75c73ddf57094 Mon Sep 17 00:00:00 2001
From: Renovate Bot <accounts+renovatebot@netz39.de>
Date: Sun, 5 Nov 2023 16:17:24 +0000
Subject: [PATCH 16/35] Update dependency isodate to v0.6.1

---
 requirements.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/requirements.txt b/requirements.txt
index 3a34529..87c39f9 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,4 +1,4 @@
 tornado==6.3.3
-isodate==0.6.0
+isodate==0.6.1
 pytest==7.4.3
 GitPython==3.1.40
\ No newline at end of file

From 33544d3e90eae64f73d3d884ce155a08580515b3 Mon Sep 17 00:00:00 2001
From: Renovate Bot <accounts+renovatebot@netz39.de>
Date: Fri, 26 Jan 2024 19:22:20 +0000
Subject: [PATCH 17/35] Update dependency pytest to v7.4.4

---
 requirements.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/requirements.txt b/requirements.txt
index 87c39f9..a169d64 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,4 +1,4 @@
 tornado==6.3.3
 isodate==0.6.1
-pytest==7.4.3
+pytest==7.4.4
 GitPython==3.1.40
\ No newline at end of file

From cdef92e8fa56aa97c1fee291f539339e7dc7b5ff Mon Sep 17 00:00:00 2001
From: Renovate Bot <accounts+renovatebot@netz39.de>
Date: Wed, 24 Jan 2024 19:22:31 +0000
Subject: [PATCH 18/35] Update dependency tornado to v6.4

---
 requirements.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/requirements.txt b/requirements.txt
index a169d64..ca6a49d 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,4 +1,4 @@
-tornado==6.3.3
+tornado==6.4
 isodate==0.6.1
 pytest==7.4.4
 GitPython==3.1.40
\ No newline at end of file

From e8886b53ad33b43ea811ab35ed99c13adaa2f9af Mon Sep 17 00:00:00 2001
From: Renovate Bot <accounts+renovatebot@netz39.de>
Date: Fri, 23 Feb 2024 20:41:04 +0000
Subject: [PATCH 19/35] Update dependency pytest to v8

---
 requirements.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/requirements.txt b/requirements.txt
index ca6a49d..a403f9a 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,4 +1,4 @@
 tornado==6.4
 isodate==0.6.1
-pytest==7.4.4
+pytest==8.0.1
 GitPython==3.1.40
\ No newline at end of file

From e8a6de078a0f4fb6bf8d27eeb4bcd50c37efc2ff Mon Sep 17 00:00:00 2001
From: Renovate Bot <accounts+renovatebot@netz39.de>
Date: Sat, 9 Mar 2024 12:17:22 +0000
Subject: [PATCH 20/35] Update dependency pytest to v8.1.1

---
 requirements.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/requirements.txt b/requirements.txt
index a403f9a..71835aa 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,4 +1,4 @@
 tornado==6.4
 isodate==0.6.1
-pytest==8.0.1
+pytest==8.1.1
 GitPython==3.1.40
\ No newline at end of file

From 24c6a4d7647d1e0a84a482eb8d208d3792a4c76c Mon Sep 17 00:00:00 2001
From: Renovate Bot <accounts+renovatebot@netz39.de>
Date: Sun, 17 Mar 2024 20:17:19 +0000
Subject: [PATCH 21/35] Update dependency GitPython to v3.1.42

---
 requirements.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/requirements.txt b/requirements.txt
index 71835aa..1b65af0 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,4 +1,4 @@
 tornado==6.4
 isodate==0.6.1
 pytest==8.1.1
-GitPython==3.1.40
\ No newline at end of file
+GitPython==3.1.42
\ No newline at end of file

From a4b7025c078bad346f6dd5a5e820348caf90836d Mon Sep 17 00:00:00 2001
From: Renovate Bot <accounts+renovatebot@netz39.de>
Date: Thu, 6 Jun 2024 19:17:23 +0000
Subject: [PATCH 22/35] Update dependency tornado to v6.4.1

---
 requirements.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/requirements.txt b/requirements.txt
index 1b65af0..1d68897 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,4 +1,4 @@
-tornado==6.4
+tornado==6.4.1
 isodate==0.6.1
 pytest==8.1.1
 GitPython==3.1.42
\ No newline at end of file

From c582e6f47ab23a9afbaf6663542df3e4ce59bb07 Mon Sep 17 00:00:00 2001
From: Renovate Bot <accounts+renovatebot@netz39.de>
Date: Tue, 4 Jun 2024 14:17:18 +0000
Subject: [PATCH 23/35] Update dependency pytest to v8.2.2

---
 requirements.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/requirements.txt b/requirements.txt
index 1d68897..b28d797 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,4 +1,4 @@
 tornado==6.4.1
 isodate==0.6.1
-pytest==8.1.1
+pytest==8.2.2
 GitPython==3.1.42
\ No newline at end of file

From 7c9ea635ce457b6d421d650064bc8b398af54071 Mon Sep 17 00:00:00 2001
From: Renovate Bot <accounts+renovatebot@netz39.de>
Date: Thu, 6 Jun 2024 21:18:59 +0000
Subject: [PATCH 24/35] Update dependency GitPython to v3.1.43

---
 requirements.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/requirements.txt b/requirements.txt
index b28d797..5954c84 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,4 +1,4 @@
 tornado==6.4.1
 isodate==0.6.1
 pytest==8.2.2
-GitPython==3.1.42
\ No newline at end of file
+GitPython==3.1.43
\ No newline at end of file

From e903272dd8561b76e7cead5f2274d1b28d7e190b Mon Sep 17 00:00:00 2001
From: Renovate Bot <accounts+renovatebot@netz39.de>
Date: Thu, 25 Jul 2024 11:17:18 +0000
Subject: [PATCH 25/35] Update dependency pytest to v8.3.2

---
 requirements.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/requirements.txt b/requirements.txt
index 5954c84..c973f24 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,4 +1,4 @@
 tornado==6.4.1
 isodate==0.6.1
-pytest==8.2.2
+pytest==8.3.2
 GitPython==3.1.43
\ No newline at end of file

From 3904819032631489f9987fff4f3bcfb70c08ac28 Mon Sep 17 00:00:00 2001
From: Renovate Bot <accounts+renovatebot@netz39.de>
Date: Tue, 10 Sep 2024 11:17:41 +0000
Subject: [PATCH 26/35] Update dependency pytest to v8.3.3

---
 requirements.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/requirements.txt b/requirements.txt
index c973f24..bb3c017 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,4 +1,4 @@
 tornado==6.4.1
 isodate==0.6.1
-pytest==8.3.2
+pytest==8.3.3
 GitPython==3.1.43
\ No newline at end of file

From 2863795580d640dbd177c12f66f26e701f10b8c6 Mon Sep 17 00:00:00 2001
From: Renovate Bot <accounts+renovatebot@netz39.de>
Date: Tue, 8 Oct 2024 23:17:22 +0000
Subject: [PATCH 27/35] Update dependency isodate to v0.7.2

---
 requirements.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/requirements.txt b/requirements.txt
index bb3c017..96238c0 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,4 +1,4 @@
 tornado==6.4.1
-isodate==0.6.1
+isodate==0.7.2
 pytest==8.3.3
 GitPython==3.1.43
\ No newline at end of file

From e36cff1a3d09dd7a1fa3c3aff6cdb5f3156909db Mon Sep 17 00:00:00 2001
From: Renovate Bot <accounts+renovatebot@netz39.de>
Date: Tue, 8 Oct 2024 00:17:22 +0000
Subject: [PATCH 28/35] Update python Docker tag to v3.13

---
 Dockerfile | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Dockerfile b/Dockerfile
index 45eb9ba..ff34993 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -8,7 +8,7 @@ COPY . /git/
 RUN find . -type d -name .git -exec git describe --always --dirty > /git-version.txt \;
 
 
-FROM python:3.12
+FROM python:3.13
 
 EXPOSE 8080
 

From cde0feab049ce3b6532bce49ce79dd8447334715 Mon Sep 17 00:00:00 2001
From: Renovate Bot <accounts+renovatebot@netz39.de>
Date: Sun, 1 Dec 2024 13:17:21 +0000
Subject: [PATCH 29/35] Update dependency pytest to v8.3.4

---
 requirements.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/requirements.txt b/requirements.txt
index 96238c0..19eb7da 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,4 +1,4 @@
 tornado==6.4.1
 isodate==0.7.2
-pytest==8.3.3
+pytest==8.3.4
 GitPython==3.1.43
\ No newline at end of file

From e297b0026b10df3f5ef789f67fa64d909018d8cc Mon Sep 17 00:00:00 2001
From: Renovate Bot <accounts+renovatebot@netz39.de>
Date: Fri, 22 Nov 2024 03:17:22 +0000
Subject: [PATCH 30/35] Update dependency tornado to v6.4.2

---
 requirements.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/requirements.txt b/requirements.txt
index 19eb7da..a60ecfc 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,4 +1,4 @@
-tornado==6.4.1
+tornado==6.4.2
 isodate==0.7.2
 pytest==8.3.4
 GitPython==3.1.43
\ No newline at end of file

From d08cc901c3c72178257ee6819e06865ba997bac0 Mon Sep 17 00:00:00 2001
From: Renovate Bot <accounts+renovatebot@netz39.de>
Date: Thu, 2 Jan 2025 08:17:23 +0000
Subject: [PATCH 31/35] Update dependency GitPython to v3.1.44

---
 requirements.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/requirements.txt b/requirements.txt
index a60ecfc..34363f0 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,4 +1,4 @@
 tornado==6.4.2
 isodate==0.7.2
 pytest==8.3.4
-GitPython==3.1.43
\ No newline at end of file
+GitPython==3.1.44
\ No newline at end of file

From 4e6f99e09749300d849fe2500f5814567fcf6d13 Mon Sep 17 00:00:00 2001
From: Renovate Bot <accounts+renovatebot@netz39.de>
Date: Sun, 2 Mar 2025 13:17:22 +0000
Subject: [PATCH 32/35] Update dependency pytest to v8.3.5

---
 requirements.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/requirements.txt b/requirements.txt
index 34363f0..a5bf826 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,4 +1,4 @@
 tornado==6.4.2
 isodate==0.7.2
-pytest==8.3.4
+pytest==8.3.5
 GitPython==3.1.44
\ No newline at end of file

From 05221bb3d1442f976f0b60f21d24006fb7fead33 Mon Sep 17 00:00:00 2001
From: Renovate Bot <accounts+renovatebot@netz39.de>
Date: Thu, 15 May 2025 21:17:22 +0000
Subject: [PATCH 33/35] Update dependency tornado to v6.5

---
 requirements.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/requirements.txt b/requirements.txt
index a5bf826..059171a 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,4 +1,4 @@
-tornado==6.4.2
+tornado==6.5
 isodate==0.7.2
 pytest==8.3.5
 GitPython==3.1.44
\ No newline at end of file

From d0c708bc4e606673e5a2a116ab767235e4892066 Mon Sep 17 00:00:00 2001
From: Renovate Bot <accounts+renovatebot@netz39.de>
Date: Thu, 22 May 2025 18:17:22 +0000
Subject: [PATCH 34/35] Update dependency tornado to v6.5.1

---
 requirements.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/requirements.txt b/requirements.txt
index 059171a..fde0605 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,4 +1,4 @@
-tornado==6.5
+tornado==6.5.1
 isodate==0.7.2
 pytest==8.3.5
 GitPython==3.1.44
\ No newline at end of file

From 4c69c27deca9226065a9a122bc114104f3ca989c Mon Sep 17 00:00:00 2001
From: Renovate Bot <accounts+renovatebot@netz39.de>
Date: Mon, 2 Jun 2025 18:17:22 +0000
Subject: [PATCH 35/35] Update dependency pytest to v8.4.0

---
 requirements.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/requirements.txt b/requirements.txt
index fde0605..2d455a1 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,4 +1,4 @@
 tornado==6.5.1
 isodate==0.7.2
-pytest==8.3.5
+pytest==8.4.0
 GitPython==3.1.44
\ No newline at end of file