From cc804b38a19d02a34fc49d168f67949e10ad8e47 Mon Sep 17 00:00:00 2001 From: Vadim Troshchinskiy Date: Mon, 7 Jul 2025 12:57:52 +0200 Subject: [PATCH] refs #2428 Remove obsolete api directory --- api/README-en.md | 61 ---- api/README.md | 69 ---- api/gitapi.py | 633 --------------------------------- api/opengnsys_git_installer.py | 1 - api/requirements-3.13.txt | 34 -- api/requirements.txt | 27 -- 6 files changed, 825 deletions(-) delete mode 100644 api/README-en.md delete mode 100644 api/README.md delete mode 100755 api/gitapi.py delete mode 120000 api/opengnsys_git_installer.py delete mode 100644 api/requirements-3.13.txt delete mode 100644 api/requirements.txt diff --git a/api/README-en.md b/api/README-en.md deleted file mode 100644 index 4ac7779..0000000 --- a/api/README-en.md +++ /dev/null @@ -1,61 +0,0 @@ -# GitLib - -The `gitapi.py` is an API for OgGit, written in Python/Flask. - -It is an HTTP server that receives commands and executes maintenance actions including the creation and deletion of repositories. - - -# Installing Python dependencies - -The conversion of the code to Python 3 currently requires the packages specified in `requirements.txt`. - -To install Python dependencies, the `venv` module is used (https://docs.python.org/3/library/venv.html), which installs all dependencies in an environment independent of the system. - - -# Usage - - -# Ubuntu 24.04 - - sudo apt install -y python3-flask python3-paramiko opengnsys-flask-executor opengnsys-flask-restx - -The `opengnsys-flask-executor` and `opengnsys-flask-restx` packages are available on the OpenGnsys package server. - -Run with: - - ./gitapi.py - -**Note:** Run as `opengnsys`, as it manages the images located in `/opt/opengnsys/images`. - - -# Documentation - -Python documentation can be generated using a utility like pdoc3 (there are multiple possible alternatives): - - # Install pdoc3 - pip install --user pdoc3 - - # Generate documentation - pdoc3 --force --html opengnsys_git_installer.py - -# Operation - -## Requirements - -The gitapi is designed to run within an existing opengnsys environment. It should be installed in an ogrepository. - - -## API Examples - -### Get list of branches - - $ curl -L http://localhost:5000/repositories/linux/branches - { - "branches": [ - "master" - ] - } - -### Synchronize with remote repository - - curl --header "Content-Type: application/json" --data '{"remote_repository":"foobar"}' -X POST -L http://localhost:5000/repositories/linux/sync \ No newline at end of file diff --git a/api/README.md b/api/README.md deleted file mode 100644 index 4462719..0000000 --- a/api/README.md +++ /dev/null @@ -1,69 +0,0 @@ -# Git API - -La `gitapi.py` es una API para OgGit, escrita en Python/Flask. - -Es un servidor HTTP que recibe comandos y ejecuta acciones de mantenimiento incluyendo la creación y eliminación de repositorios. - - -# Instalación de dependencias para python - -La conversion del código a Python 3 requiere actualmente los paquetes especificados en `requirements.txt` - -Para instalar dependencias de python se usa el modulo venv (https://docs.python.org/3/library/venv.html) que instala todas las dependencias en un entorno independiente del sistema. - - -# Uso - - -## Distribuciones antiguas (18.04) - - sudo apt install -y python3.8 python3.8-venv python3-venv libarchive-dev - python3.8 -m venv venvog - . venvog/bin/activate - python3.8 -m pip install --upgrade pip - pip3 install -r requirements.txt - -Ejecutar con: - - ./gitapi.py - - -## Uso - -**Nota:** Ejecutar como `opengnsys`, ya que gestiona las imágenes que se encuentran en `/opt/opengnsys/images`. - - $ . venvog/bin/activate - $ ./gitapi.py - - -# Documentación - -Se puede generar documentación de Python con una utilidad como pdoc3 (hay multiples alternativas posibles): - - # Instalar pdoc3 - pip install --user pdoc3 - - # Generar documentación - pdoc3 --force --html opengnsys_git_installer.py - -# Funcionamiento - -## Requisitos - -La gitapi esta diseñada para funcionar dentro de un entorno opengnsys existente. Se debe instalar en un ogrepository. - -## Ejemplo de API - -### Obtener lista de ramas - - $ curl -L http://localhost:5000/repositories/linux/branches - { - "branches": [ - "master" - ] - } - -### Sincronizar con repositorio remoto - - curl --header "Content-Type: application/json" --data '{"remote_repository":"foobar"}' -X POST -L http://localhost:5000/repositories/linux/sync - diff --git a/api/gitapi.py b/api/gitapi.py deleted file mode 100755 index 02e3747..0000000 --- a/api/gitapi.py +++ /dev/null @@ -1,633 +0,0 @@ -#!/usr/bin/env python3 -""" -This module provides a Flask-based API for managing Git repositories in the OpenGnsys system. -It includes endpoints for creating, deleting, synchronizing, backing up, and performing garbage -collection on Git repositories. The API also provides endpoints for retrieving repository -information such as the list of repositories and branches, as well as checking the status of -asynchronous tasks. - -Classes: - None - -Functions: - do_repo_backup(repo, params) - - do_repo_sync(repo, params) - - do_repo_gc(repo) - - home() - - get_repositories() - - create_repo(repo) - - sync_repo(repo) - - backup_repository(repo) - - gc_repo(repo) - - tasks_status(task_id) - - delete_repo(repo) - - get_repository_branches(repo) - - health_check() - -Constants: - REPOSITORIES_BASE_PATH (str): The base path where Git repositories are stored. - -Global Variables: - app (Flask): The Flask application instance. - executor (Executor): The Flask-Executor instance for managing asynchronous tasks. - tasks (dict): A dictionary to store the status of asynchronous tasks. -""" - -# pylint: disable=locally-disabled, line-too-long - -import os.path -import os -import shutil -import uuid -import git -import time -from opengnsys_git_installer import OpengnsysGitInstaller -from flask import Flask, request, jsonify # stream_with_context, Response, -from flask_executor import Executor -from flask_restx import Api, Resource, fields -#from flasgger import Swagger -import paramiko -import logging -import traceback -from werkzeug.exceptions import HTTPException - -from systemd.journal import JournalHandler - -debug_enabled = False - -log = logging.getLogger('gitapi') -log.addHandler(JournalHandler()) -log.setLevel(logging.INFO) -log.info("Started") - - -REPOSITORIES_BASE_PATH = "/opt/opengnsys/ogrepository/oggit/git/oggit/" - -start_time = time.time() -tasks = {} -tasks_max = 1024 - -# Create an instance of the Flask class -app = Flask(__name__) -api = Api(app, - version='0.50', - title = "OpenGnsys Git API", - description = "API for managing disk images stored in Git", - doc = "/swagger/") - -git_ns = api.namespace(name = "oggit", description = "Git operations", path = "/oggit/v1") - -executor = Executor(app) - - - -def add_task(future): - task_id = uuid.uuid4().hex - task_data = { - "future" : future, - "start_time" : time.time() - } - - while len(tasks) >= tasks_max: - oldest_task_id = min(tasks, key=lambda k: tasks[k]['start_time']) - task = tasks[task_id]["future"] - if task.running(): - log.error("Cancelling still running task %s, maximum task limit of %i reached", task_id, tasks_max) - task.cancel() - - del tasks[oldest_task_id] - - tasks[task_id] = task_data - return task_id - -def do_repo_backup(repo, params): - """ - Creates a backup of the specified Git repository and uploads it to a remote server via SFTP. - - Args: - repo (str): The name of the repository to back up. - params (dict): A dictionary containing the following keys: - - ssh_server (str): The SSH server address. - - ssh_port (int): The SSH server port. - - ssh_user (str): The SSH username. - - filename (str): The remote filename where the backup will be stored. - - Returns: - bool: True if the backup was successful. - """ - - git_repo_path = f"{REPOSITORIES_BASE_PATH}/{repo}.git" - git_repo = git.Repo(git_repo_path) - git_repo.git.config('--global', '--add', 'safe.directory', git_repo_path) - - - ssh = paramiko.SSHClient() - ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - - ssh.connect(params["ssh_server"], params["ssh_port"], params["ssh_user"]) - sftp = ssh.open_sftp() - - - with sftp.file(params["filename"], mode='wb+') as remote_file: - git_repo.archive(remote_file, format="tar.gz") - - - return True - -def do_repo_sync(repo, params): - """ - Synchronizes a local Git repository with a remote repository. - - Args: - repo (str): The name of the local repository to synchronize. - params (dict): A dictionary containing the remote repository URL with the key "remote_repository". - - Returns: - list: A list of dictionaries, each containing: - - "local_ref" (str): The name of the local reference. - - "remote_ref" (str): The name of the remote reference. - - "summary" (str): A summary of the push operation for the reference. - """ - git_repo_path = f"{REPOSITORIES_BASE_PATH}/{repo}.git" - git_repo = git.Repo(git_repo_path) - git_repo.git.config('--global', '--add', 'safe.directory', git_repo_path) - - - # Recreate the remote every time, it might change - if "backup" in git_repo.remotes: - git_repo.delete_remote("backup") - - backup_repo = git_repo.create_remote("backup", params["remote_repository"]) - pushed_references = backup_repo.push("*:*") - results = [] - - # This gets returned to the API - for ref in pushed_references: - results = results + [ {"local_ref" : ref.local_ref.name, "remote_ref" : ref.remote_ref.name, "summary" : ref.summary }] - - return results - -def do_repo_gc(repo): - """ - Perform garbage collection on the specified Git repository. - - Args: - repo (str): The name of the repository to perform garbage collection on. - - Returns: - bool: True if the garbage collection command was executed successfully. - """ - git_repo_path = os.path.join(REPOSITORIES_BASE_PATH, repo + ".git") - git_repo = git.Repo(git_repo_path) - git_repo.git.config('--global', '--add', 'safe.directory', git_repo_path) - - git_repo.git.gc() - -@app.errorhandler(HTTPException) -def handle_exception(e): - """Return JSON for HTTP errors. - - We create and log an error UUID for each error, and use journald's additional fields for easier searching. - """ - # start with the correct headers and status code from the error - response = e.get_response() - - errid = uuid.uuid4().hex - - - if debug_enabled: - response = { - "errcode": e.code, - "errname": e.name, - "description": e.description, - } - else: - response = { - "errcode" : 500, - "errname" : "Internal error", - "description": f"Please see the log for error {errid}", - "error_id" : errid - } - - log.error("Error ID %s: code %i, name %s, description %s", errid, e.code, e.name, e.description, extra = { "error_id" : errid, "errcode" : e.code, "errname" : e.name, "description" : e.description }) - - # response.content_type = "application/json" - return response - - -# Define a route for the root URL -@api.route('/') -class GitLib(Resource): - - @api.doc('home') - def get(self): - """ - Home route that returns a JSON response with a welcome message for the OpenGnsys Git API. - - Returns: - Response: A Flask JSON response containing a welcome message. - """ - log.info("Root URL accessed") - - return { - "message": "OpenGnsys Git API" - } - -@git_ns.route('/repositories') -class GitRepositories(Resource): - def get(self): - """ - Retrieve a list of Git repositories. - - This endpoint scans the OpenGnsys image path for directories that - appear to be Git repositories (i.e., they contain a "HEAD" file). - It returns a JSON response containing the names of these repositories. - - Returns: - Response: A JSON response with a list of repository names or an - error message if the repository storage is not found. - - 200 OK: When the repositories are successfully retrieved. - - 500 Internal Server Error: When the repository storage is not found. - - Example JSON response: - { - "repositories": ["repo1", "repo2"] - } - """ - - if not os.path.isdir(REPOSITORIES_BASE_PATH): - log.error("Can't list repositories. Repository storage at %s not found", REPOSITORIES_BASE_PATH, extra = {"path" : REPOSITORIES_BASE_PATH}) - return {"error": "Repository storage not found, git functionality may not be installed."}, 500 - - repos = [] - for entry in os.scandir(REPOSITORIES_BASE_PATH): - if entry.is_dir(follow_symlinks=False) and os.path.isfile(os.path.join(entry.path, "HEAD")): - name = entry.name - if name.endswith(".git"): - name = name[:-4] - - repos = repos + [name] - - log.info("Returning %i repositories", len(repos)) - return { - "repositories": repos - } - - def post(self): - """ - Create a new Git repository. - - This endpoint creates a new Git repository with the specified name. - If the repository already exists, it returns a status message indicating so. - - Args: - repo (str): The name of the repository to be created. - - Returns: - Response: A JSON response with a status message and HTTP status code. - - 200: If the repository already exists. - - 201: If the repository is successfully created. - """ - data = request.json - - if data is None: - log.error("Can't create repository, JSON post data missing") - return {"error" : "Parameters missing"}, 400 - - repo = data["name"] - - repo_path = os.path.join(REPOSITORIES_BASE_PATH, repo + ".git") - if os.path.isdir(repo_path): - log.error("Can't create repository %s, already exists at %s", repo, repo_path, extra = {"repository" : repo, "path" : repo_path}) - return {"status": "Repository already exists"}, 200 - - - installer = OpengnsysGitInstaller() - installer.add_forgejo_repo(repo) - - #installer.init_git_repo(repo + ".git") - - log.info("Repository %s created", repo, extra = {"repository" : repo}) - return {"status": "Repository created"}, 201 - - -@git_ns.route('/repositories//sync') -class GitRepoSync(Resource): - def post(self, repo): - """ - Synchronize a repository with a remote repository. - - This endpoint triggers the synchronization process for a specified repository. - It expects a JSON payload with the remote repository details. - - Args: - repo (str): The name of the repository to be synchronized. - - Returns: - Response: A JSON response indicating the status of the synchronization process. - - 200: If the synchronization process has started successfully. - - 400: If the request payload is missing or invalid. - - 404: If the specified repository is not found. - """ - repo_path = os.path.join(REPOSITORIES_BASE_PATH, repo + ".git") - if not os.path.isdir(repo_path): - log.error("Can't sync repository %s, not found. Looked in %s", repo, repo_path, extra = {"repository" : repo, "path" : repo_path }) - return {"error": "Repository not found"}, 404 - - - data = request.json - - if data is None: - log.error("Can't create repository, JSON post data missing") - return {"error" : "Parameters missing"}, 400 - - if not "remote_repository" in data: - log.error("Can't create repository, parameter 'remote_repository' missing") - return {"error" : "Parameter 'remote_repository' missing"}, 400 - - - future = executor.submit(do_repo_sync, repo, data) - task_id = add_task(future) - - log.info("Starting synchronization of repository %s, task %s", repo, task_id, extra = {"repository" : repo, "task_id" : task_id}) - return {"status": "started", "task_id" : task_id}, 200 - - - -@git_ns.route('/repositories//backup') -class GitRepoBackup(Resource): - def backup_repository(self, repo): - """ - Backup a specified repository. - - Endpoint: POST /repositories//backup - - Args: - repo (str): The name of the repository to back up. - - Request Body (JSON): - ssh_port (int, optional): The SSH port to use for the backup. Defaults to 22. - - Returns: - Response: A JSON response indicating the status of the backup operation. - - If the repository is not found, returns a 404 error with a message. - - If the request body is missing, returns a 400 error with a message. - - If the backup process starts successfully, returns a 200 status with the task ID. - - Notes: - - The repository path is constructed by appending ".git" to the repository name. - - The backup operation is performed asynchronously using a thread pool executor. - - The task ID of the backup operation is generated using UUID and stored in a global tasks dictionary. - """ - repo_path = os.path.join(REPOSITORIES_BASE_PATH, repo + ".git") - if not os.path.isdir(repo_path): - log.error("Can't backup repository %s, not found. Looked in %s", repo, repo_path, extra = {"repository" : repo, "path" : repo_path }) - return {"error": "Repository not found"}, 404 - - - data = request.json - if data is None: - log.error("Can't create repository, JSON post data missing") - return {"error" : "Parameters missing"}, 400 - - - if not "ssh_port" in data: - data["ssh_port"] = 22 - - - future = executor.submit(do_repo_backup, repo, data) - task_id = add_task(future) - - log.info("Starting backup of repository %s, task %s", repo, task_id, extra = {"repository" : repo, "task_id" : task_id}) - return {"status": "started", "task_id" : task_id}, 200 - -@git_ns.route('/repositories//compact', methods=['POST']) -class GitRepoCompact(Resource): - def post(self, repo): - """ - Initiates a garbage collection (GC) process for a specified Git repository. - - This endpoint triggers an asynchronous GC task for the given repository. - The task is submitted to an executor, and a unique task ID is generated - and returned to the client. - - Args: - repo (str): The name of the repository to perform GC on. - - Returns: - Response: A JSON response containing the status of the request and - a unique task ID if the repository is found, or an error - message if the repository is not found. - """ - repo_path = os.path.join(REPOSITORIES_BASE_PATH, repo + ".git") - if not os.path.isdir(repo_path): - log.error("Can't compact repository %s, not found. Looked in %s", repo, repo_path, extra = {"repository" : repo, "path" : repo_path }) - return {"error": "Repository not found"}, 404 - - future = executor.submit(do_repo_gc, repo) - task_id = add_task(future) - - log.info("Starting compaction of repository %s, task %s", repo, task_id, extra = {"repository" : repo, "task_id" : task_id}) - return {"status": "started", "task_id" : task_id}, 200 - - -@git_ns.route('/tasks//status') -class GitTaskStatus(Resource): - def get(self, task_id): - """ - Endpoint to check the status of a specific task. - - Args: - task_id (str): The unique identifier of the task. - - Returns: - Response: A JSON response containing the status of the task. - - If the task is not found, returns a 404 error with an error message. - - If the task is completed, returns a 200 status with the result. - - If the task is still in progress, returns a 202 status indicating the task is in progress. - """ - if not task_id in tasks: - log.error("Task %s was not found", task_id, extra = {"task_id" : task_id}) - return {"error": "Task not found"}, 404 - - future = tasks[task_id]["future"] - - try: - if future.done(): - result = future.result() - log.info("Returning completion of task %s", task_id, extra = {"task_id" : task_id}) - return {"status" : "completed", "result" : result}, 200 - else: - log.info("Task %s is still in progress", task_id, extra = {"task_id" : task_id}) - return {"status" : "in progress"}, 202 - except Exception as e: - errid = uuid.uuid4().hex - - - log.error("Task %s failed with exception %s, UUID %s", task_id, traceback.format_exception(e), errid, extra = {"task_id" : task_id, "exception" : traceback.format_exception(e), "error_id" : errid}) - return {"status" : "internal error", "error_id" : errid }, 500 - - -@git_ns.route('/repositories/', methods=['DELETE']) -class GitRepo(Resource): - def delete(self, repo): - """ - Deletes a Git repository. - - This endpoint deletes a Git repository specified by the `repo` parameter. - If the repository does not exist, it returns a 404 error with a message - indicating that the repository was not found. If the repository is successfully - deleted, it returns a 200 status with a message indicating that the repository - was deleted. - - Args: - repo (str): The name of the repository to delete. - - Returns: - Response: A JSON response with a status message and the appropriate HTTP status code. - """ - repo_path = os.path.join(REPOSITORIES_BASE_PATH, repo + ".git") - if not os.path.isdir(repo_path): - log.error("Can't delete repository %s, not found. Looked in %s", repo, repo_path, extra = {"repository" : repo, "path" : repo_path }) - return {"error": "Repository not found"}, 404 - - - shutil.rmtree(repo_path) - log.info("Deleted repository %s", repo, extra = {"repository" : repo}) - return {"status": "Repository deleted"}, 200 - - - - -@git_ns.route('/repositories//branches') -class GitRepoBranches(Resource): - def get(self, repo): - """ - Retrieve the list of branches for a given repository. - - Args: - repo (str): The name of the repository. - - Returns: - Response: A JSON response containing a list of branch names or an error message if the repository is not found. - - 200: A JSON object with a "branches" key containing a list of branch names. - - 404: A JSON object with an "error" key containing the message "Repository not found" if the repository does not exist. - """ - repo_path = os.path.join(REPOSITORIES_BASE_PATH, repo + ".git") - if not os.path.isdir(repo_path): - log.error("Can't get branches of repository repository %s, not found. Looked in %s", repo, repo_path, extra = {"repository" : repo, "path" : repo_path }) - return {"error": "Repository not found"}, 404 - - git_repo = git.Repo(repo_path) - git_repo.git.config('--global', '--add', 'safe.directory', repo_path) - - - branches = [] - for branch in git_repo.branches: - branches = branches + [branch.name] - - log.info("Returning %i branches", len(branches)) - return { - "branches": branches - } - -@git_ns.route('/repositories//branches/') -class GitRepoBranchesDeleter(Resource): - def delete(self, repo, branch): - """Delete a given branch in a given repository - - Args: - repo (str): The name of the repository. - - Returns: - Response: A JSON response containing a list of branch names or an error message if the repository is not found. - - 200: A JSON object with a "status" key containing "deleted" - - 404: A JSON object with an "error" key containing the message "Repository not found" or "Branch not found" - """ - - repo_path = os.path.join(REPOSITORIES_BASE_PATH, repo + ".git") - if not os.path.isdir(repo_path): - log.error("Can't get branches of repository repository %s, not found. Looked in %s", repo, repo_path, extra = {"repository" : repo, "path" : repo_path }) - return {"error": "Repository not found"}, 404 - - git_repo = git.Repo(repo_path) - git_repo.git.config('--global', '--add', 'safe.directory', repo_path) - - - if not branch in git_repo.branches: - log.error("Can't delete branch %s, not found in repository %s", branch, repo, extra = {"repository" : repo, "branch" : branch}) - return {"error": "Branch not found"}, 404 - - git_repo.delete_head(branch) - log.info("Branch %s of repository %s deleted", branch, repo, extra = {"repository" : repo, "branch" : branch}) - return {"status": "deleted"}, 200 - - -@git_ns.route('/health') -class GitHealth(Resource): - def get(self): - """ - Health check endpoint. - - This endpoint returns a JSON response indicating the health status of the application. - - Returns: - Response: A JSON response with a status key set to "OK". Currently it always returns - a successful value, but this endpoint can still be used to check that the API is - active and functional. - - """ - log.info("Health check endpoint called") - return { - "status": "OK" - } - -@git_ns.route('/status') -class GitStatus(Resource): - def get(self): - """ - Status check endpoint. - - This endpoint returns a JSON response indicating the status of the application. - - Returns: - Response: A JSON response with status information - - """ - log.info("Status endpoint called") - - return { - "uptime" : time.time() - start_time, - "active_tasks" : len(tasks) - } - -@app.after_request -def after_request(response): - log.info("Request from %s: %s %s %s %s", request.remote_addr, request.method, request.scheme, request.full_path, response.status, - extra = {"remote_addr" : request.remote_addr, "method" : request.method, "scheme" : request.scheme, "full_path" : request.full_path, "status" : response.status}) - - if debug_enabled: - log.debug("Response: %s", response.data, extra = {"response" : response.data}) - - return response - -api.add_namespace(git_ns) - - - -# Run the Flask app -if __name__ == '__main__': - print(f"Map: {app.url_map}") - app.run(debug=True, host='0.0.0.0') diff --git a/api/opengnsys_git_installer.py b/api/opengnsys_git_installer.py deleted file mode 120000 index a550869..0000000 --- a/api/opengnsys_git_installer.py +++ /dev/null @@ -1 +0,0 @@ -../installer/opengnsys_git_installer.py \ No newline at end of file diff --git a/api/requirements-3.13.txt b/api/requirements-3.13.txt deleted file mode 100644 index eb57d6f..0000000 --- a/api/requirements-3.13.txt +++ /dev/null @@ -1,34 +0,0 @@ -aniso8601==9.0.1 -attrs==24.2.0 -bcrypt==4.2.0 -blinker==1.8.2 -cffi==1.17.1 -click==8.1.7 -cryptography==43.0.1 -dataclasses==0.6 -flasgger==0.9.7.1 -Flask==3.0.3 -Flask-Executor==1.0.0 -flask-restx==1.3.0 -gitdb==4.0.11 -GitPython==3.1.43 -importlib_resources==6.4.5 -itsdangerous==2.2.0 -Jinja2==3.1.4 -jsonschema==4.23.0 -jsonschema-specifications==2024.10.1 -libarchive-c==5.1 -MarkupSafe==3.0.1 -mistune==3.0.2 -packaging==24.1 -paramiko==3.5.0 -pycparser==2.22 -PyNaCl==1.5.0 -pytz==2024.2 -PyYAML==6.0.2 -referencing==0.35.1 -rpds-py==0.20.0 -six==1.16.0 -smmap==5.0.1 -termcolor==2.5.0 -Werkzeug==3.0.4 diff --git a/api/requirements.txt b/api/requirements.txt deleted file mode 100644 index 5c63be4..0000000 --- a/api/requirements.txt +++ /dev/null @@ -1,27 +0,0 @@ -bcrypt==4.0.1 -cffi==1.15.1 -click==8.0.4 -colorterm==0.3 -contextvars==2.4 -cryptography==40.0.2 -dataclasses==0.8 -Flask==2.0.3 -Flask-Executor==1.0.0 -gitdb==4.0.9 -GitPython==3.1.20 -immutables==0.19 -importlib-metadata==4.8.3 -itsdangerous==2.0.1 -Jinja2==3.0.3 -libarchive==0.4.7 -MarkupSafe==2.0.1 -nose==1.3.7 -paramiko==3.5.0 -pkg_resources==0.0.0 -pycparser==2.21 -PyNaCl==1.5.0 -smmap==5.0.0 -termcolor==1.1.0 -typing_extensions==4.1.1 -Werkzeug==2.0.3 -zipp==3.6.0