Compare commits
No commits in common. "ogrepository-fixes" and "main" have entirely different histories.
ogreposito
...
main
|
@ -1,8 +0,0 @@
|
|||
__pycache__
|
||||
.venv
|
||||
venvog
|
||||
*.deb
|
||||
*.build
|
||||
*.dsc
|
||||
*.changes
|
||||
*.buildinfo
|
|
@ -44,7 +44,6 @@ Python documentation can be generated using a utility like pdoc3 (there are mult
|
|||
|
||||
The gitapi is designed to run within an existing opengnsys environment. It should be installed in an ogrepository.
|
||||
|
||||
|
||||
## API Examples
|
||||
|
||||
### Get list of branches
|
||||
|
|
245
api/gitapi.py
245
api/gitapi.py
|
@ -59,25 +59,12 @@ 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/"
|
||||
REPOSITORIES_BASE_PATH = "/opt/opengnsys/images"
|
||||
|
||||
start_time = time.time()
|
||||
tasks = {}
|
||||
tasks_max = 1024
|
||||
|
||||
|
||||
# Create an instance of the Flask class
|
||||
app = Flask(__name__)
|
||||
|
@ -93,24 +80,6 @@ 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):
|
||||
"""
|
||||
|
@ -128,10 +97,7 @@ def do_repo_backup(repo, params):
|
|||
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)
|
||||
|
||||
gitrepo = git.Repo(f"{REPOSITORIES_BASE_PATH}/{repo}.git")
|
||||
|
||||
ssh = paramiko.SSHClient()
|
||||
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
|
@ -141,7 +107,7 @@ def do_repo_backup(repo, params):
|
|||
|
||||
|
||||
with sftp.file(params["filename"], mode='wb+') as remote_file:
|
||||
git_repo.archive(remote_file, format="tar.gz")
|
||||
gitrepo.archive(remote_file, format="tar.gz")
|
||||
|
||||
|
||||
return True
|
||||
|
@ -160,16 +126,13 @@ def do_repo_sync(repo, params):
|
|||
- "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)
|
||||
|
||||
gitrepo = git.Repo(f"{REPOSITORIES_BASE_PATH}/{repo}.git")
|
||||
|
||||
# Recreate the remote every time, it might change
|
||||
if "backup" in git_repo.remotes:
|
||||
git_repo.delete_remote("backup")
|
||||
if "backup" in gitrepo.remotes:
|
||||
gitrepo.delete_remote("backup")
|
||||
|
||||
backup_repo = git_repo.create_remote("backup", params["remote_repository"])
|
||||
backup_repo = gitrepo.create_remote("backup", params["remote_repository"])
|
||||
pushed_references = backup_repo.push("*:*")
|
||||
results = []
|
||||
|
||||
|
@ -189,42 +152,10 @@ def do_repo_gc(repo):
|
|||
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)
|
||||
gitrepo = git.Repo(f"{REPOSITORIES_BASE_PATH}/{repo}.git")
|
||||
|
||||
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
|
||||
gitrepo.git.gc()
|
||||
return True
|
||||
|
||||
|
||||
# Define a route for the root URL
|
||||
|
@ -239,13 +170,11 @@ class GitLib(Resource):
|
|||
Returns:
|
||||
Response: A Flask JSON response containing a welcome message.
|
||||
"""
|
||||
log.info("Root URL accessed")
|
||||
|
||||
return {
|
||||
"message": "OpenGnsys Git API"
|
||||
}
|
||||
|
||||
@git_ns.route('/repositories')
|
||||
@git_ns.route('/oggit/v1/repositories')
|
||||
class GitRepositories(Resource):
|
||||
def get(self):
|
||||
"""
|
||||
|
@ -268,8 +197,7 @@ class GitRepositories(Resource):
|
|||
"""
|
||||
|
||||
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
|
||||
return jsonify({"error": "Repository storage not found, git functionality may not be installed."}), 500
|
||||
|
||||
repos = []
|
||||
for entry in os.scandir(REPOSITORIES_BASE_PATH):
|
||||
|
@ -280,10 +208,9 @@ class GitRepositories(Resource):
|
|||
|
||||
repos = repos + [name]
|
||||
|
||||
log.info("Returning %i repositories", len(repos))
|
||||
return {
|
||||
return jsonify({
|
||||
"repositories": repos
|
||||
}
|
||||
})
|
||||
|
||||
def post(self):
|
||||
"""
|
||||
|
@ -303,15 +230,13 @@ class GitRepositories(Resource):
|
|||
data = request.json
|
||||
|
||||
if data is None:
|
||||
log.error("Can't create repository, JSON post data missing")
|
||||
return {"error" : "Parameters missing"}, 400
|
||||
return jsonify({"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
|
||||
return jsonify({"status": "Repository already exists"}), 200
|
||||
|
||||
|
||||
installer = OpengnsysGitInstaller()
|
||||
|
@ -319,11 +244,11 @@ class GitRepositories(Resource):
|
|||
|
||||
#installer.init_git_repo(repo + ".git")
|
||||
|
||||
log.info("Repository %s created", repo, extra = {"repository" : repo})
|
||||
return {"status": "Repository created"}, 201
|
||||
|
||||
return jsonify({"status": "Repository created"}), 201
|
||||
|
||||
|
||||
@git_ns.route('/repositories/<repo>/sync')
|
||||
@git_ns.route('/oggit/v1/repositories/<repo>/sync')
|
||||
class GitRepoSync(Resource):
|
||||
def post(self, repo):
|
||||
"""
|
||||
|
@ -343,30 +268,22 @@ class GitRepoSync(Resource):
|
|||
"""
|
||||
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
|
||||
return jsonify({"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
|
||||
|
||||
return jsonify({"error" : "Parameters 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
|
||||
task_id = str(uuid.uuid4())
|
||||
tasks[task_id] = future
|
||||
return jsonify({"status": "started", "task_id" : task_id}), 200
|
||||
|
||||
|
||||
|
||||
@git_ns.route('/repositories/<repo>/backup')
|
||||
@git_ns.route('/oggit/v1/repositories/<repo>/backup')
|
||||
class GitRepoBackup(Resource):
|
||||
def backup_repository(self, repo):
|
||||
"""
|
||||
|
@ -393,14 +310,12 @@ class GitRepoBackup(Resource):
|
|||
"""
|
||||
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
|
||||
return jsonify({"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
|
||||
return jsonify({"error" : "Parameters missing"}), 400
|
||||
|
||||
|
||||
if not "ssh_port" in data:
|
||||
|
@ -408,12 +323,12 @@ class GitRepoBackup(Resource):
|
|||
|
||||
|
||||
future = executor.submit(do_repo_backup, repo, data)
|
||||
task_id = add_task(future)
|
||||
task_id = str(uuid.uuid4())
|
||||
tasks[task_id] = 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
|
||||
return jsonify({"status": "started", "task_id" : task_id}), 200
|
||||
|
||||
@git_ns.route('/repositories/<repo>/compact', methods=['POST'])
|
||||
@git_ns.route('/oggit/v1/repositories/<repo>/compact', methods=['POST'])
|
||||
class GitRepoCompact(Resource):
|
||||
def post(self, repo):
|
||||
"""
|
||||
|
@ -433,17 +348,16 @@ class GitRepoCompact(Resource):
|
|||
"""
|
||||
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
|
||||
return jsonify({"error": "Repository not found"}), 404
|
||||
|
||||
future = executor.submit(do_repo_gc, repo)
|
||||
task_id = add_task(future)
|
||||
task_id = str(uuid.uuid4())
|
||||
tasks[task_id] = 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
|
||||
return jsonify({"status": "started", "task_id" : task_id}), 200
|
||||
|
||||
|
||||
@git_ns.route('/tasks/<task_id>/status')
|
||||
@git_ns.route('/oggit/v1/tasks/<task_id>/status')
|
||||
class GitTaskStatus(Resource):
|
||||
def get(self, task_id):
|
||||
"""
|
||||
|
@ -459,28 +373,19 @@ class GitTaskStatus(Resource):
|
|||
- 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
|
||||
return jsonify({"error": "Task not found"}), 404
|
||||
|
||||
future = tasks[task_id]["future"]
|
||||
future = tasks[task_id]
|
||||
|
||||
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
|
||||
if future.done():
|
||||
result = future.result()
|
||||
return jsonify({"status" : "completed", "result" : result}), 200
|
||||
else:
|
||||
return jsonify({"status" : "in progress"}), 202
|
||||
|
||||
|
||||
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/<repo>', methods=['DELETE'])
|
||||
@git_ns.route('/oggit/v1/repositories/<repo>', methods=['DELETE'])
|
||||
class GitRepo(Resource):
|
||||
def delete(self, repo):
|
||||
"""
|
||||
|
@ -500,18 +405,16 @@ class GitRepo(Resource):
|
|||
"""
|
||||
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
|
||||
return jsonify({"error": "Repository not found"}), 404
|
||||
|
||||
|
||||
shutil.rmtree(repo_path)
|
||||
log.info("Deleted repository %s", repo, extra = {"repository" : repo})
|
||||
return {"status": "Repository deleted"}, 200
|
||||
return jsonify({"status": "Repository deleted"}), 200
|
||||
|
||||
|
||||
|
||||
|
||||
@git_ns.route('/repositories/<repo>/branches')
|
||||
@git_ns.route('/oggit/v1/repositories/<repo>/branches')
|
||||
class GitRepoBranches(Resource):
|
||||
def get(self, repo):
|
||||
"""
|
||||
|
@ -527,52 +430,20 @@ class GitRepoBranches(Resource):
|
|||
"""
|
||||
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
|
||||
return jsonify({"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 {
|
||||
|
||||
return jsonify({
|
||||
"branches": branches
|
||||
}
|
||||
|
||||
@git_ns.route('/repositories/<repo>/branches/<branch>')
|
||||
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')
|
||||
|
@ -589,7 +460,6 @@ class GitHealth(Resource):
|
|||
active and functional.
|
||||
|
||||
"""
|
||||
log.info("Health check endpoint called")
|
||||
return {
|
||||
"status": "OK"
|
||||
}
|
||||
|
@ -606,22 +476,11 @@ class GitStatus(Resource):
|
|||
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)
|
||||
|
||||
|
|
|
@ -1,42 +0,0 @@
|
|||
# API Server
|
||||
|
||||
`api_server.py` is a Flask script that loads and executes Flask blueprints from the `blueprints` directory.
|
||||
|
||||
|
||||
Currently it's intended to combine oggit and ogrepository.
|
||||
|
||||
|
||||
# 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:
|
||||
|
||||
./api_server.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
|
|
@ -1,170 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
import os
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, "/usr/share/opengnsys-modules/python3/dist-packages")
|
||||
sys.path.insert(0, "/opt/opengnsys/oggit/bin/")
|
||||
|
||||
|
||||
import importlib
|
||||
import logging
|
||||
import uuid
|
||||
import argparse
|
||||
import yaml
|
||||
from flask import Flask, request
|
||||
from flask_executor import Executor
|
||||
from flask_restx import Api
|
||||
from flasgger import Swagger
|
||||
from werkzeug.exceptions import HTTPException
|
||||
from systemd.journal import JournalHandler
|
||||
|
||||
class FakeArgs:
|
||||
def __init__(self):
|
||||
self.verbose = False
|
||||
self.listen = None
|
||||
self.port = None
|
||||
self.debug = None
|
||||
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
prog="api_server.py",
|
||||
description="OpenGnsys Repository API Server",
|
||||
)
|
||||
|
||||
debug_enabled = False
|
||||
listen_host = '0.0.0.0'
|
||||
listen_port = 8006
|
||||
|
||||
is_gunicorn = "gunicorn" in os.environ.get("SERVER_SOFTWARE", "")
|
||||
|
||||
if not is_gunicorn:
|
||||
# Gunicorn passes us all the arguments passed to gunicorn itself, which of course crashes here since we don't recognize them.
|
||||
# Deal with this by not doing argument handling when running under gunicorn
|
||||
parser.add_argument('--debug', action='store_true', help="Enable debug output")
|
||||
parser.add_argument('--listen', metavar="HOST", help="Listen address")
|
||||
parser.add_argument('--port', metavar="PORT", help="Listen port")
|
||||
parser.add_argument("-v", "--verbose", action="store_true", help = "Verbose console output")
|
||||
|
||||
args = parser.parse_args()
|
||||
else:
|
||||
args = FakeArgs()
|
||||
|
||||
|
||||
log = logging.getLogger('api_server')
|
||||
log.addHandler(JournalHandler())
|
||||
|
||||
if args.verbose:
|
||||
log.addHandler(logging.StreamHandler(stream=sys.stderr))
|
||||
log.setLevel(logging.DEBUG)
|
||||
else:
|
||||
log.setLevel(logging.INFO)
|
||||
|
||||
|
||||
if is_gunicorn:
|
||||
log.info("Running under gunicorn, argument handling disabled.")
|
||||
|
||||
if args.listen:
|
||||
listen_host = args.listen
|
||||
|
||||
if args.port:
|
||||
listen_port = args.port
|
||||
|
||||
|
||||
if args.debug:
|
||||
debug_enabled = True
|
||||
|
||||
|
||||
api_base_dir = os.path.dirname(os.path.realpath(__file__))
|
||||
blueprints_dir = os.path.join(api_base_dir, 'blueprints')
|
||||
installer_dir = os.path.join(api_base_dir, '../installer')
|
||||
|
||||
|
||||
|
||||
|
||||
sys.path.insert(0, installer_dir)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# 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 = "/apidocs/")
|
||||
|
||||
|
||||
executor = Executor(app)
|
||||
|
||||
log.info("Loading blueprints from %s", blueprints_dir)
|
||||
sys.path.insert(0, blueprints_dir)
|
||||
|
||||
for filename in os.listdir(blueprints_dir):
|
||||
if filename.endswith('.py'):
|
||||
|
||||
log.info("Loading %s/%s", blueprints_dir, filename)
|
||||
|
||||
module_name = filename.replace(".py", "")
|
||||
swagger_file = os.path.join(blueprints_dir, filename.replace(".py", ".yaml"))
|
||||
|
||||
log.info("Importing %s", module_name)
|
||||
importlib.invalidate_caches()
|
||||
module = importlib.import_module(module_name)
|
||||
log.debug("Returned: %s", module)
|
||||
|
||||
app.register_blueprint(module.blueprint)
|
||||
|
||||
if os.path.exists(swagger_file):
|
||||
log.info("Loading Swagger documentation from %s...", swagger_file)
|
||||
|
||||
with open(swagger_file, "r", encoding='utf-8') as file:
|
||||
swagger_template = yaml.safe_load(file)
|
||||
|
||||
#print(f"Template: {swagger_template}")
|
||||
#swagger = Swagger(app, template=swagger_template)
|
||||
else:
|
||||
log.warning("Swagger not found for this module, looked in %s", swagger_file)
|
||||
|
||||
|
||||
|
||||
@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
|
||||
|
||||
|
||||
response = {
|
||||
"errcode": e.code,
|
||||
"errname": e.name,
|
||||
"description": e.description,
|
||||
}
|
||||
|
||||
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 })
|
||||
|
||||
return response, 500
|
||||
|
||||
@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
|
||||
|
||||
|
||||
|
||||
|
||||
# Run the Flask app
|
||||
if __name__ == '__main__':
|
||||
print(f"Map: {app.url_map}")
|
||||
app.run(debug=debug_enabled, host=listen_host, port=listen_port)
|
|
@ -1,713 +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 time
|
||||
import logging
|
||||
import traceback
|
||||
|
||||
import git
|
||||
from opengnsys_git_installer import OpengnsysGitInstaller
|
||||
from flask import Blueprint, request
|
||||
from flask_restx import Resource, Api
|
||||
import paramiko
|
||||
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
|
||||
|
||||
blueprint = Blueprint('git_api', __name__, template_folder='templates', url_prefix = '/oggit/v1')
|
||||
api = Api(blueprint)
|
||||
git_ns = api
|
||||
|
||||
|
||||
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()
|
||||
|
||||
|
||||
|
||||
# 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/<repo>/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/<repo>/backup')
|
||||
class GitRepoBackup(Resource):
|
||||
def backup_repository(self, repo):
|
||||
"""
|
||||
Backup a specified repository.
|
||||
|
||||
Endpoint: POST /repositories/<repo>/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/<repo>/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/<task_id>/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/<repo>', 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/<repo>/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/<repo>/branches/<branch>')
|
||||
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
|
||||
|
||||
def post(self, repo, branch):
|
||||
"""Create 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"
|
||||
- 409: A JSON object with an "error" key containing the message "Branch already exists"
|
||||
"""
|
||||
|
||||
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)
|
||||
|
||||
data = request.json
|
||||
if data is None:
|
||||
log.error("Can't create branch, JSON post data missing")
|
||||
return {"error" : "Parameters missing"}, 400
|
||||
|
||||
if not "commit" in data:
|
||||
log.error("Can't create branch, commit parameter missing")
|
||||
return {"error" : "commit parameter missing"}, 400
|
||||
|
||||
|
||||
if branch in git_repo.branches:
|
||||
log.error("Can't create branch %s, already found in repository %s", branch, repo, extra = {"repository" : repo, "branch" : branch})
|
||||
return {"error": "Branch already exists"}, 409
|
||||
|
||||
git_repo.create_head(branch, commit = data["commit"] )
|
||||
log.info("Branch %s of repository %s created", branch, repo, extra = {"repository" : repo, "branch" : branch})
|
||||
return {"status": "created"}, 200
|
||||
|
||||
@git_ns.route('/repositories/<repo>/tags')
|
||||
class GitRepoTags(Resource):
|
||||
def get(self, repo):
|
||||
"""
|
||||
Retrieve the list of tags for a given repository.
|
||||
|
||||
Args:
|
||||
repo (str): The name of the repository.
|
||||
|
||||
Returns:
|
||||
Response: A JSON response containing a list of tags names or an error message if the repository is not found.
|
||||
- 200: A JSON object with a "tags" key containing a list of tags 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 tags 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)
|
||||
|
||||
|
||||
tags = []
|
||||
for tag in git_repo.tags:
|
||||
tags = tags + [tag.name]
|
||||
|
||||
log.info("Returning %i tags", len(tags))
|
||||
return {
|
||||
"tags": tags
|
||||
}
|
||||
|
||||
@git_ns.route('/repositories/<repo>/tags/<tag>')
|
||||
class GitRepoTagsDeleter(Resource):
|
||||
def delete(self, repo, tag):
|
||||
"""Delete a given tag in a given repository
|
||||
|
||||
Args:
|
||||
repo (str): The name of the repository.
|
||||
|
||||
Returns:
|
||||
Response: A JSON response containing a list of tag 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 "Tag not found"
|
||||
"""
|
||||
|
||||
repo_path = os.path.join(REPOSITORIES_BASE_PATH, repo + ".git")
|
||||
if not os.path.isdir(repo_path):
|
||||
log.error("Can't get tags 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 tag in git_repo.tags:
|
||||
log.error("Can't delete tag %s, not found in repository %s", tag, repo, extra = {"repository" : repo, "tag" : tag})
|
||||
return {"error": "Tag not found"}, 404
|
||||
|
||||
git_repo.delete_head(tag)
|
||||
log.info("Tag %s of repository %s deleted", tag, repo, extra = {"repository" : repo, "tag" : tag})
|
||||
return {"status": "deleted"}, 200
|
||||
|
||||
def post(self, repo, tag):
|
||||
"""Create a given tag in a given repository
|
||||
|
||||
Args:
|
||||
repo (str): The name of the repository.
|
||||
|
||||
Returns:
|
||||
Response: A JSON response containing a creation status
|
||||
- 200: A JSON object with a "status" key containing "created"
|
||||
- 404: A JSON object with an "error" key containing the message "Repository not found"
|
||||
- 409: A JSON object with an "error" key containing the message "Tag already exists"
|
||||
"""
|
||||
|
||||
repo_path = os.path.join(REPOSITORIES_BASE_PATH, repo + ".git")
|
||||
if not os.path.isdir(repo_path):
|
||||
log.error("Can't get tags 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)
|
||||
|
||||
data = request.json
|
||||
if data is None:
|
||||
log.error("Can't create tag, JSON post data missing")
|
||||
return {"error" : "Parameters missing"}, 400
|
||||
|
||||
if not "commit" in data:
|
||||
log.error("Can't create tag, commit parameter missing")
|
||||
return {"error" : "commit parameter missing"}, 400
|
||||
|
||||
|
||||
if tag in git_repo.tags:
|
||||
log.error("Can't create tag %s, already found in repository %s", tag, repo, extra = {"repository" : repo, "tag" : tag})
|
||||
return {"error": "Tag already exists"}, 409
|
||||
git_repo.create_tag(tag, ref = data["commit"])
|
||||
|
||||
log.info("Tag %s of repository %s created", tag, repo, extra = {"repository" : repo, "tag" : tag})
|
||||
return {"status": "created"}, 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)
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,5 +0,0 @@
|
|||
oggit (0.5) UNRELEASED; urgency=medium
|
||||
|
||||
* Initial release.
|
||||
|
||||
-- OpenGnsys <opengnsys@opengnsys.es> Fri, 14 Mar 2025 08:40:35 +0100
|
|
@ -1,38 +0,0 @@
|
|||
Source: oggit
|
||||
Section: unknown
|
||||
Priority: optional
|
||||
Maintainer: OpenGnsys <opengnsys@opengnsys.es>
|
||||
Rules-Requires-Root: no
|
||||
Build-Depends:
|
||||
debhelper-compat (= 13),
|
||||
Standards-Version: 4.6.2
|
||||
Homepage: https://opengnsys.es
|
||||
#Vcs-Browser: https://salsa.debian.org/debian/ogboot
|
||||
#Vcs-Git: https://salsa.debian.org/debian/ogboot.git
|
||||
|
||||
Package: oggit
|
||||
Architecture: any
|
||||
Multi-Arch: foreign
|
||||
Depends:
|
||||
${shlibs:Depends},
|
||||
${misc:Depends},
|
||||
bsdextrautils,
|
||||
debconf (>= 1.5.0),
|
||||
gunicorn,
|
||||
opengnsys-flask-executor,
|
||||
opengnsys-flask-restx,
|
||||
opengnsys-libarchive-c,
|
||||
python3,
|
||||
python3-aniso8601,
|
||||
python3-flasgger,
|
||||
python3-flask,
|
||||
python3-flask,
|
||||
python3-git,
|
||||
python3-paramiko,
|
||||
python3-requests,
|
||||
python3-termcolor,
|
||||
python3-tqdm,
|
||||
opengnsys-forgejo (>= 0.5)
|
||||
Conflicts:
|
||||
Description: Opengnsys Oggit package
|
||||
Files for OpenGnsys Git support
|
|
@ -1,43 +0,0 @@
|
|||
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||
Source: <url://example.com>
|
||||
Upstream-Name: ogboot
|
||||
Upstream-Contact: <preferred name and address to reach the upstream project>
|
||||
|
||||
Files:
|
||||
*
|
||||
Copyright:
|
||||
<years> <put author's name and email here>
|
||||
<years> <likewise for another author>
|
||||
License: GPL-3.0+
|
||||
|
||||
Files:
|
||||
debian/*
|
||||
Copyright:
|
||||
2025 vagrant <vagrant@build>
|
||||
License: GPL-3.0+
|
||||
|
||||
License: GPL-3.0+
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
.
|
||||
This package is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
Comment:
|
||||
On Debian systems, the complete text of the GNU General
|
||||
Public License version 3 can be found in "/usr/share/common-licenses/GPL-3".
|
||||
|
||||
# Please also look if there are files or directories which have a
|
||||
# different copyright/license attached and list them here.
|
||||
# Please avoid picking licenses with terms that are more restrictive than the
|
||||
# packaged work, as it may make Debian's contributions unacceptable upstream.
|
||||
#
|
||||
# If you need, there are some extra license texts available in two places:
|
||||
# /usr/share/debhelper/dh_make/licenses/
|
||||
# /usr/share/common-licenses/
|
|
@ -1,3 +0,0 @@
|
|||
README.source
|
||||
README.Debian
|
||||
README
|
|
@ -1,3 +0,0 @@
|
|||
/opt/opengnsys/images/git
|
||||
/opt/opengnsys/ogrepository/oggit
|
||||
/opt/opengnsys/ogrepository/oggit/api
|
|
@ -1,6 +0,0 @@
|
|||
api_server.py /opt/opengnsys/ogrepository/oggit/api
|
||||
../installer/opengnsys_git_installer.py /opt/opengnsys/oggit/bin
|
||||
blueprints/gitapi.py /opt/opengnsys/ogrepository/oggit/api/blueprints
|
||||
blueprints/repo_api.py /opt/opengnsys/ogrepository/oggit/api/blueprints
|
||||
opengnsys-repository-api.service /etc/systemd/system
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
#!/bin/bash
|
||||
/usr/bin/systemctl daemon-reload
|
||||
/usr/bin/systemctl enable --now opengnsys-repository-api
|
|
@ -1,2 +0,0 @@
|
|||
misc:Depends=
|
||||
misc:Pre-Depends=
|
|
@ -1,12 +0,0 @@
|
|||
Package: oggit
|
||||
Version: 0.5
|
||||
Architecture: amd64
|
||||
Maintainer: OpenGnsys <opengnsys@opengnsys.es>
|
||||
Installed-Size: 193
|
||||
Depends: bsdextrautils, debconf (>= 1.5.0), gunicorn, opengnsys-flask-executor, opengnsys-flask-restx, opengnsys-libarchive-c, python3, python3-aniso8601, python3-flasgger, python3-flask, python3-git, python3-paramiko, python3-requests, python3-termcolor, python3-tqdm
|
||||
Section: unknown
|
||||
Priority: optional
|
||||
Multi-Arch: foreign
|
||||
Homepage: https://opengnsys.es
|
||||
Description: Opengnsys Oggit package
|
||||
Files for OpenGnsys Git support
|
|
@ -1,6 +0,0 @@
|
|||
1a0024adb1d5e54ecff27759c5ac4a7d opt/opengnsys/oggit/bin/api_server.py
|
||||
bd0a968737c2d62ce44490414426ccbb opt/opengnsys/oggit/bin/opengnsys_git_installer.py
|
||||
af5f26474949def90af8794458f3f08d opt/opengnsys/oggit/blueprints/gitapi.py
|
||||
61618848e4caca8b22e3cc7b9c8706b8 opt/opengnsys/oggit/blueprints/repo_api.py
|
||||
48b531f72dec218fcdd61dce26f6b5ab usr/share/doc/oggit/changelog.gz
|
||||
8a13e4a3eb6149d56094319bbed84d0c usr/share/doc/oggit/copyright
|
|
@ -1,139 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
import os
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, "/usr/share/opengnsys-modules/python3/dist-packages")
|
||||
|
||||
|
||||
import importlib
|
||||
import logging
|
||||
import uuid
|
||||
import argparse
|
||||
from flask import Flask, request
|
||||
from flask_executor import Executor
|
||||
from flask_restx import Api
|
||||
from werkzeug.exceptions import HTTPException
|
||||
from systemd.journal import JournalHandler
|
||||
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
prog="api_server.py",
|
||||
description="OpenGnsys Repository API Server",
|
||||
)
|
||||
|
||||
debug_enabled = False
|
||||
listen_host = '0.0.0.0'
|
||||
parser.add_argument('--debug', action='store_true', help="Enable debug output")
|
||||
parser.add_argument('--listen', metavar="HOST", help="Listen address")
|
||||
parser.add_argument("-v", "--verbose", action="store_true", help = "Verbose console output")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
|
||||
|
||||
log = logging.getLogger('api_server')
|
||||
log.addHandler(JournalHandler())
|
||||
|
||||
if args.verbose:
|
||||
log.addHandler(logging.StreamHandler(stream=sys.stderr))
|
||||
log.setLevel(logging.DEBUG)
|
||||
else:
|
||||
log.setLevel(logging.INFO)
|
||||
|
||||
if args.listen:
|
||||
listen_host = args.listen
|
||||
|
||||
|
||||
if args.debug:
|
||||
debug_enabled = True
|
||||
|
||||
|
||||
api_base_dir = os.path.dirname(os.path.realpath(__file__))
|
||||
blueprints_dir = os.path.join(api_base_dir, 'blueprints')
|
||||
installer_dir = os.path.join(api_base_dir, '../installer')
|
||||
|
||||
|
||||
|
||||
|
||||
sys.path.insert(0, installer_dir)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# 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/")
|
||||
|
||||
|
||||
executor = Executor(app)
|
||||
|
||||
log.info("Loading blueprints from %s", blueprints_dir)
|
||||
sys.path.insert(0, blueprints_dir)
|
||||
|
||||
for filename in os.listdir(blueprints_dir):
|
||||
if filename.endswith('.py'):
|
||||
|
||||
log.info("Loading %s/%s", blueprints_dir, filename)
|
||||
|
||||
module_name = filename.replace(".py", "")
|
||||
|
||||
log.info("Importing %s", module_name)
|
||||
importlib.invalidate_caches()
|
||||
module = importlib.import_module(module_name)
|
||||
log.debug("Returned: %s", module)
|
||||
|
||||
app.register_blueprint(module.blueprint)
|
||||
|
||||
|
||||
@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 })
|
||||
|
||||
return response, 500
|
||||
|
||||
@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
|
||||
|
||||
|
||||
|
||||
|
||||
# Run the Flask app
|
||||
if __name__ == '__main__':
|
||||
print(f"Map: {app.url_map}")
|
||||
app.run(debug=debug_enabled, host=listen_host)
|
File diff suppressed because it is too large
Load Diff
|
@ -1,573 +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 time
|
||||
import logging
|
||||
import traceback
|
||||
|
||||
import git
|
||||
from opengnsys_git_installer import OpengnsysGitInstaller
|
||||
from flask import Blueprint, request
|
||||
from flask_restx import Resource, Api
|
||||
import paramiko
|
||||
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
|
||||
|
||||
blueprint = Blueprint('git_api', __name__, template_folder='templates', url_prefix = '/oggit/v1')
|
||||
api = Api(blueprint)
|
||||
git_ns = api
|
||||
|
||||
|
||||
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()
|
||||
|
||||
|
||||
|
||||
# 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/<repo>/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/<repo>/backup')
|
||||
class GitRepoBackup(Resource):
|
||||
def backup_repository(self, repo):
|
||||
"""
|
||||
Backup a specified repository.
|
||||
|
||||
Endpoint: POST /repositories/<repo>/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/<repo>/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/<task_id>/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/<repo>', 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/<repo>/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/<repo>/branches/<branch>')
|
||||
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)
|
||||
}
|
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
@ -1,43 +0,0 @@
|
|||
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||
Source: <url://example.com>
|
||||
Upstream-Name: ogboot
|
||||
Upstream-Contact: <preferred name and address to reach the upstream project>
|
||||
|
||||
Files:
|
||||
*
|
||||
Copyright:
|
||||
<years> <put author's name and email here>
|
||||
<years> <likewise for another author>
|
||||
License: GPL-3.0+
|
||||
|
||||
Files:
|
||||
debian/*
|
||||
Copyright:
|
||||
2025 vagrant <vagrant@build>
|
||||
License: GPL-3.0+
|
||||
|
||||
License: GPL-3.0+
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
.
|
||||
This package is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
Comment:
|
||||
On Debian systems, the complete text of the GNU General
|
||||
Public License version 3 can be found in "/usr/share/common-licenses/GPL-3".
|
||||
|
||||
# Please also look if there are files or directories which have a
|
||||
# different copyright/license attached and list them here.
|
||||
# Please avoid picking licenses with terms that are more restrictive than the
|
||||
# packaged work, as it may make Debian's contributions unacceptable upstream.
|
||||
#
|
||||
# If you need, there are some extra license texts available in two places:
|
||||
# /usr/share/debhelper/dh_make/licenses/
|
||||
# /usr/share/common-licenses/
|
|
@ -1,33 +0,0 @@
|
|||
#!/usr/bin/make -f
|
||||
|
||||
# See debhelper(7) (uncomment to enable).
|
||||
# Output every command that modifies files on the build system.
|
||||
#export DH_VERBOSE = 1
|
||||
|
||||
|
||||
# See FEATURE AREAS in dpkg-buildflags(1).
|
||||
#export DEB_BUILD_MAINT_OPTIONS = hardening=+all
|
||||
|
||||
# See ENVIRONMENT in dpkg-buildflags(1).
|
||||
# Package maintainers to append CFLAGS.
|
||||
#export DEB_CFLAGS_MAINT_APPEND = -Wall -pedantic
|
||||
# Package maintainers to append LDFLAGS.
|
||||
#export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed
|
||||
|
||||
|
||||
%:
|
||||
dh $@
|
||||
|
||||
|
||||
%:
|
||||
dh $@
|
||||
|
||||
# Ejecutar composer install durante la fase de construcción
|
||||
override_dh_auto_build:
|
||||
|
||||
|
||||
# dh_make generated override targets.
|
||||
# This is an example for Cmake (see <https://bugs.debian.org/641051>).
|
||||
#override_dh_auto_configure:
|
||||
# dh_auto_configure -- \
|
||||
# -DCMAKE_LIBRARY_PATH=$(DEB_HOST_MULTIARCH)
|
|
@ -1,33 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
if [ ! -f "/etc/apt/sources.list.d/opengnsys.sources" ] ; then
|
||||
|
||||
cat > /etc/apt/sources.list.d/opengnsys.sources <<HERE
|
||||
Types: deb
|
||||
URIs: https://ognproject.evlt.uma.es/debian-opengnsys/
|
||||
Suites: noble
|
||||
Components: main
|
||||
Signed-By:
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
.
|
||||
mDMEZzx/SxYJKwYBBAHaRw8BAQdAa83CuAJ5/+7Pn9LHT/k34EAGpx5FnT/ExHSj
|
||||
XZG1JES0Ik9wZW5HbnN5cyA8b3Blbmduc3lzQG9wZW5nbnN5cy5lcz6ImQQTFgoA
|
||||
QRYhBC+J38Xsso227ZbDVt2S5xJQRhKDBQJnPH9LAhsDBQkFo5qABQsJCAcCAiIC
|
||||
BhUKCQgLAgQWAgMBAh4HAheAAAoJEN2S5xJQRhKDW/MBAO6swnpwdrbm48ypMyPh
|
||||
NboxvF7rCqBqHWwRHvkvrq7pAP9zd98r7z2AvqVXZxnaCsLTUNMEL12+DVZAUZ1G
|
||||
EquRBbg4BGc8f0sSCisGAQQBl1UBBQEBB0B6D6tkrwXSHi7ebGYsiMPntqwdkQ/S
|
||||
84SFTlSxRqdXfgMBCAeIfgQYFgoAJhYhBC+J38Xsso227ZbDVt2S5xJQRhKDBQJn
|
||||
PH9LAhsMBQkFo5qAAAoJEN2S5xJQRhKDJ+cBAM9jYbeq5VXkHLfODeVztgSXnSUe
|
||||
yklJ18oQmpeK5eWeAQDKYk/P0R+1ZJDItxkeP6pw62bCDYGQDvdDGPMAaIT6CA==
|
||||
=xcNc
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
||||
HERE
|
||||
fi
|
||||
|
||||
|
||||
apt update
|
||||
apt install -y python3-git opengnsys-libarchive-c python3-termcolor python3-requests python3-tqdm bsdextrautils python3-paramiko python3-aniso8601 opengnsys-flask-restx opengnsys-flask-executor python3-flask python3-psutil
|
|
@ -1,11 +0,0 @@
|
|||
[Service]
|
||||
RestartSec=10s
|
||||
Type=simple
|
||||
User=oggit
|
||||
Group=oggit
|
||||
WorkingDirectory=/opt/opengnsys/ogrepository/oggit/api/
|
||||
ExecStart=/usr/bin/gunicorn -w 4 -b 0.0.0.0:8006 api_server:app
|
||||
Restart=always
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
|
@ -1,59 +1,32 @@
|
|||
# Git component installer
|
||||
# Installing Dependencies for Python
|
||||
|
||||
This directory contains the installer for the git component for OpenGnsys.
|
||||
Converting the code to Python 3 currently requires the packages specified in `requirements.txt`.
|
||||
|
||||
It downloads, installs and configures Forgejo, creates the default repositories and configures SSH keys.
|
||||
To install Python dependencies, the `venv` module (https://docs.python.org/3/library/venv.html) is used, which installs all dependencies in an isolated environment separate from the system.
|
||||
|
||||
# Quick Installation
|
||||
|
||||
## Ubuntu 24.04
|
||||
|
||||
### Add the repository
|
||||
sudo apt install python3-git opengnsys-libarchive-c python3-termcolor bsdextrautils
|
||||
|
||||
## Add SSH Keys to oglive
|
||||
|
||||
Create the file `/etc/apt/sources.list.d/opengnsys.sources` with these contents:
|
||||
The Git system accesses the ogrepository via SSH. To work, it needs the oglive to have an SSH key, and the ogrepository must accept it.
|
||||
|
||||
Types: deb
|
||||
URIs: https://ognproject.evlt.uma.es/debian-opengnsys/
|
||||
Suites: noble
|
||||
Components: main
|
||||
Signed-By:
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
.
|
||||
mDMEZzx/SxYJKwYBBAHaRw8BAQdAa83CuAJ5/+7Pn9LHT/k34EAGpx5FnT/ExHSj
|
||||
XZG1JES0Ik9wZW5HbnN5cyA8b3Blbmduc3lzQG9wZW5nbnN5cy5lcz6ImQQTFgoA
|
||||
QRYhBC+J38Xsso227ZbDVt2S5xJQRhKDBQJnPH9LAhsDBQkFo5qABQsJCAcCAiIC
|
||||
BhUKCQgLAgQWAgMBAh4HAheAAAoJEN2S5xJQRhKDW/MBAO6swnpwdrbm48ypMyPh
|
||||
NboxvF7rCqBqHWwRHvkvrq7pAP9zd98r7z2AvqVXZxnaCsLTUNMEL12+DVZAUZ1G
|
||||
EquRBbg4BGc8f0sSCisGAQQBl1UBBQEBB0B6D6tkrwXSHi7ebGYsiMPntqwdkQ/S
|
||||
84SFTlSxRqdXfgMBCAeIfgQYFgoAJhYhBC+J38Xsso227ZbDVt2S5xJQRhKDBQJn
|
||||
PH9LAhsMBQkFo5qAAAoJEN2S5xJQRhKDJ+cBAM9jYbeq5VXkHLfODeVztgSXnSUe
|
||||
yklJ18oQmpeK5eWeAQDKYk/P0R+1ZJDItxkeP6pw62bCDYGQDvdDGPMAaIT6CA==
|
||||
=xcNc
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
||||
The Git installer can make the required changes with:
|
||||
|
||||
It's required to run `apt update` after creating this file
|
||||
./opengnsys_git_installer.py --set-ssh-key
|
||||
|
||||
### Install packages
|
||||
Or to do it for a specific oglive:
|
||||
|
||||
sudo apt install -y python3-git opengnsys-libarchive-c python3-termcolor python3-requests python3-tqdm bsdextrautils
|
||||
./opengnsys_git_installer.py --set-ssh-key --oglive 1 # oglive number
|
||||
|
||||
## Adding SSH Keys to oglive
|
||||
Running this command automatically adds the SSH key to Forgejo.
|
||||
|
||||
The Git system accesses the ogrepository via SSH. To function, it needs the oglive to have an SSH key, and for the ogrepository to accept it.
|
||||
|
||||
The Git installer can make the required changes by extracting an SSH key from an oglive and installing it in Forgejo. If there is a local ogboot installation, the installer will do this automatically. If there is not, it is necessary to provide the installer with an oglive from which to extract the key using the `--oglive-file` or `--oglive-url` parameter.
|
||||
|
||||
For example:
|
||||
|
||||
./opengnsys_git_installer.py --oglive-url https://example.com/ogLive-noble.iso
|
||||
|
||||
The installer will proceed to download the file, mount the ISO, and extract the key.
|
||||
|
||||
To perform the process after completing the installation and only add a key to an existing installation, use the `--set-ssh-key` parameter:
|
||||
|
||||
./opengnsys_git_installer.py --set-ssh-key --oglive-url https://example.com/ogLive-noble.iso
|
||||
The existing key can be extracted with:
|
||||
|
||||
./opengnsys_git_installer.py --extract-ssh-key --quiet
|
||||
|
||||
# Running the Installer
|
||||
|
||||
|
|
|
@ -1,58 +1,35 @@
|
|||
# Instalador de componente Git
|
||||
# Instalación de dependencias para python
|
||||
|
||||
Este directorio contiene el instalador de Git para OpenGnsys.
|
||||
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.
|
||||
|
||||
Descarga, instala y configura Forgejo, crea los repositorios por defecto, y configura claves de SSH.
|
||||
|
||||
# Instalación rápida
|
||||
|
||||
## Ubuntu 24.04
|
||||
|
||||
### Agregar repositorio
|
||||
|
||||
Crear el archivo `/etc/apt/sources.list.d/opengnsys.sources` con este contenido:
|
||||
|
||||
Types: deb
|
||||
URIs: https://ognproject.evlt.uma.es/debian-opengnsys/opengnsys
|
||||
Suites: noble
|
||||
Components: main
|
||||
Signed-By:
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
.
|
||||
mDMEZzx/SxYJKwYBBAHaRw8BAQdAa83CuAJ5/+7Pn9LHT/k34EAGpx5FnT/ExHSj
|
||||
XZG1JES0Ik9wZW5HbnN5cyA8b3Blbmduc3lzQG9wZW5nbnN5cy5lcz6ImQQTFgoA
|
||||
QRYhBC+J38Xsso227ZbDVt2S5xJQRhKDBQJnPH9LAhsDBQkFo5qABQsJCAcCAiIC
|
||||
BhUKCQgLAgQWAgMBAh4HAheAAAoJEN2S5xJQRhKDW/MBAO6swnpwdrbm48ypMyPh
|
||||
NboxvF7rCqBqHWwRHvkvrq7pAP9zd98r7z2AvqVXZxnaCsLTUNMEL12+DVZAUZ1G
|
||||
EquRBbg4BGc8f0sSCisGAQQBl1UBBQEBB0B6D6tkrwXSHi7ebGYsiMPntqwdkQ/S
|
||||
84SFTlSxRqdXfgMBCAeIfgQYFgoAJhYhBC+J38Xsso227ZbDVt2S5xJQRhKDBQJn
|
||||
PH9LAhsMBQkFo5qAAAoJEN2S5xJQRhKDJ+cBAM9jYbeq5VXkHLfODeVztgSXnSUe
|
||||
yklJ18oQmpeK5eWeAQDKYk/P0R+1ZJDItxkeP6pw62bCDYGQDvdDGPMAaIT6CA==
|
||||
=xcNc
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
Es necesario ejecutar `apt update` después de crear el archivo.
|
||||
|
||||
### Instalar paquetes:
|
||||
|
||||
sudo apt install -y python3-git opengnsys-libarchive-c python3-termcolor python3-requests python3-tqdm bsdextrautils
|
||||
sudo apt install python3-git opengnsys-libarchive-c python3-termcolor bsdextrautils
|
||||
|
||||
|
||||
## Agregar claves de SSH a oglive
|
||||
|
||||
El sistema de Git accede al ogrepository por SSH. Para funcionar, necesita que el oglive tenga una clave de SSH, y que el ogrepository la acepte.
|
||||
|
||||
El instalador de Git puede realizar los cambios requeridos, extrayendo una clave de SSH de un oglive e instalándola en Forgejo. Si hay una instalación de ogboot local, el instalador lo hará automáticamente. Si no la hay, es necesario darle al instalador un oglive del que extraer la clave con el parámetro `--oglive-file` o `--oglive-url`.
|
||||
El instalador de Git puede realizar los cambios requeridos, con:
|
||||
|
||||
Por ejemplo:
|
||||
./opengnsys_git_installer.py --set-ssh-key
|
||||
|
||||
./opengnsys_git_installer.py --oglive-url https://example.com/ogLive-noble.iso
|
||||
O para hacerlo contra un oglive especifico:
|
||||
|
||||
El instalador procederá a descargar el archivo, montar el ISO, y extraer la clave.
|
||||
./opengnsys_git_installer.py --set-ssh-key --oglive 1 # numero de oglive
|
||||
|
||||
Para hacer el proceso después de haber completado la instalación y solo agregar una clave a una instalación existente, usar el parámetro `--set-ssh-key`:
|
||||
Ejecutar este comando agrega la clave de SSH a Forgejo automáticamente.
|
||||
|
||||
./opengnsys_git_installer.py --set-ssh-key --oglive-url https://example.com/ogLive-noble.iso
|
||||
|
||||
La clave existente puede extraerse con:
|
||||
|
||||
./opengnsys_git_installer.py --extract-ssh-key --quiet
|
||||
|
||||
# Ejecutar
|
||||
|
||||
|
@ -72,8 +49,6 @@ El usuario por defecto es `oggit` con password `opengnsys`.
|
|||
|
||||
El sistema OgGit requiere módulos de Python que no vienen en Ubuntu 24.04 o tienen versiones demasiado antiguas.
|
||||
|
||||
Los paquetes se pueden obtener desde el repositorio de OpenGnsys (ver arriba).
|
||||
|
||||
Los fuentes de los paquetes se encuentran en oggit/packages.
|
||||
|
||||
# Documentación de código fuente
|
||||
|
|
|
@ -1,656 +0,0 @@
|
|||
opengnsys-gitinstaller (0.5dev3) UNRELEASED; urgency=medium
|
||||
|
||||
[ OpenGnsys ]
|
||||
* Initial release.
|
||||
|
||||
[ Vadim Troshchinskiy ]
|
||||
* First commit
|
||||
* Add installer
|
||||
* Add requirements file
|
||||
|
||||
[ lgromero ]
|
||||
* refs #734 Creates first skeleton of symfony+swagger project
|
||||
|
||||
[ Vadim Troshchinskiy ]
|
||||
* Add Gitlib
|
||||
|
||||
[ lgromero ]
|
||||
* refs #734 Changes OgBootBundle name and adds a first endpoint to test
|
||||
* refs #734 Adds template of repository and branch endpoints
|
||||
|
||||
[ Vadim Troshchinskiy ]
|
||||
* Update docs to account for changes
|
||||
* Trivial API server
|
||||
* Ticket #753: Add repository listing
|
||||
* Ticket #735: List branches in repo
|
||||
* Add testing instructions
|
||||
* Agregar manejo de errrores
|
||||
* Ticket #741: Crear repo Ticket #736: Eliminar repo
|
||||
|
||||
[ lgromero ]
|
||||
* refs #734 Adds README for Api installation
|
||||
* refs #734 Control of errores and http codes in controler
|
||||
* refs #734 Renemas oggitservice
|
||||
|
||||
[ Vadim Troshchinskiy ]
|
||||
* Ticket #738, ticket #739: repo and sync backup protoype
|
||||
|
||||
[ lgromero ]
|
||||
* refs #734 Adds new endpoints sync and backup and status endpoint
|
||||
* refs #734 Adds nelmio api doc configuration
|
||||
* Adds .env file to root
|
||||
* refs #734 use environment variables in .env files and disable web depuration toolbar
|
||||
* refs #734 fix typo in .env and use oggit_url environment variable
|
||||
|
||||
[ Vadim Troshchinskiy ]
|
||||
* Ticket #738, ticket #739: git sync and backup
|
||||
|
||||
[ Nicolas Arenas ]
|
||||
* Add docker container files
|
||||
|
||||
[ Vadim Troshchinskiy ]
|
||||
* Ticket #737: GC
|
||||
* Use Paramiko and Gitpython for backups
|
||||
|
||||
[ Nicolas Arenas ]
|
||||
* Add mock api for testing dockerfile
|
||||
|
||||
[ Vadim Troshchinskiy ]
|
||||
* Ticket #740, listen on all hosts
|
||||
|
||||
[ lgromero ]
|
||||
* refs #734 Removes innecesaries parameters and changes php platform to 8.2
|
||||
* refs #734 just changes name and description in swagger web page
|
||||
|
||||
[ Vadim Troshchinskiy ]
|
||||
* Remove duplicated import
|
||||
* Documentation prototype
|
||||
* Update to 24.04, solves deployment issue
|
||||
* Add more documentation
|
||||
* Add API README
|
||||
* Add API examples
|
||||
* Update list of package requirements in oglive
|
||||
* Fix commandline parsing bug
|
||||
* Revert experimental Windows change
|
||||
* Fix ticket #770: Re-parse filesystems list after mounting
|
||||
* Use oglive server if ogrepository is not set
|
||||
* Ticket #770: Add sanity check
|
||||
* Ticket #771: Correctly create directories on metadata restoration
|
||||
* Ticket #780: Unmount before clone if needed
|
||||
* Fix ticket #800: sudo doesn't work
|
||||
|
||||
[ Vadim Trochinsky ]
|
||||
* Fix ticket #802: .git directory in filesystem root
|
||||
|
||||
[ Vadim Troshchinskiy ]
|
||||
* Fix ticket #805: Remove .git directory if it already exists when checking out
|
||||
* Ticket #770: Correctly update metadata when mounting and unmounting
|
||||
* Ticket #804: Move log
|
||||
* Fix ticket #902: .git directories can't be checked out
|
||||
* Lint fixes
|
||||
* Remove unused code
|
||||
* Lint fixes
|
||||
* Lint fixes
|
||||
* Lint fixes
|
||||
* Additional logging message
|
||||
* Lint fix
|
||||
* Fix ticket #907: mknod fails due to path not found
|
||||
* Initial implementation for commit, push, fetch.
|
||||
* Don't fail on empty lines in metadata, just skip them
|
||||
* Add documentation and functionality to progress hook (not used yet)
|
||||
* Pylint fixes
|
||||
* Ticket #908: Remove some unneeded warnings
|
||||
* Fix progress report
|
||||
* Ticket #906: Fix permissions on directories
|
||||
* Make pylint happy
|
||||
* Mount fix
|
||||
* Ticket #808: Initial implementation
|
||||
* Initial forgejo install
|
||||
* Deduplicate key extraction
|
||||
* Fix installer bugs and add documentation
|
||||
* Change user to oggit
|
||||
* Fix NTFS ID modification implementation
|
||||
* Implement system-specific EFI data support
|
||||
* Fix encoding when reading system uuid
|
||||
* Fix and refactor slightly EFI implementation
|
||||
* Add Windows BCD decoding tool
|
||||
* Check module loading and unloading, modprobe works on oglive now
|
||||
* Make EFI deployment more flexible
|
||||
* Add organization API call
|
||||
* Fix bash library path
|
||||
* Fix repo paths for forgejo
|
||||
* Update documentation
|
||||
* Sync to ensure everything is written
|
||||
* Refactoring and more pydoc
|
||||
* Add more documentation
|
||||
* Improve installer documentation
|
||||
* Improve gitlib instructions
|
||||
* Add missing files
|
||||
* Partial setsshkey implementation
|
||||
* Fix SSH key generation and extraction
|
||||
* Initial package contents
|
||||
* Add Debian packaging
|
||||
* Add pylkid
|
||||
* Add pyblkid debian files
|
||||
* Use packaged pyblkid
|
||||
* More detailed API logging
|
||||
* Improve logging
|
||||
* Add oglive key to forgejo
|
||||
* Add original source
|
||||
* Always re-download forgejo, even if installed.
|
||||
* Remove obsolete code that stopped being relevant with Forgejo
|
||||
* Move python modules to /opt/opengnsys-modules
|
||||
* Use absolute paths in initrd modification
|
||||
* Add timestamp to ssh key title, forgejo doesn't like duplicates
|
||||
* Skip past symlinks and problems in oglive modification
|
||||
* Get keys from squashfs instead of initrd to work with current oglive packaging
|
||||
* Fix trivial bug
|
||||
* Move modules to /usr/share/opengnsys
|
||||
* Move packages to /usr/share
|
||||
|
||||
[ Angel Rodriguez ]
|
||||
* Add gitlib/README-en.md
|
||||
* Add api/README-en.md
|
||||
* Add installer/README-en.md
|
||||
|
||||
[ Vadim Troshchinskiy ]
|
||||
* Skip NTFS code on non-Windows
|
||||
* Store and restore GPT partition UUIDs
|
||||
* Update READMEs
|
||||
* BCD constants
|
||||
* Use tqdm
|
||||
* Constants
|
||||
* Add extra mounts update
|
||||
* Better status reports
|
||||
* Make log filename machine-dependent Move kernel args parsing
|
||||
* Make unmounting more robust
|
||||
* Improve repository initialization
|
||||
* Make --pull work like the other commands
|
||||
* Add packages
|
||||
* Update documentation
|
||||
* Ignore python cache
|
||||
* Ignore more files
|
||||
* add python libarchive-c original package
|
||||
* Add pyblkid copyright file
|
||||
* Add make_orig script
|
||||
* Reorder and fix for ogrepository reorganization
|
||||
* Restructure git installer to work without ogboot on the same machine, update docs
|
||||
* Update english documentation
|
||||
* Improve installation process, make it possible to extract keys from oglive
|
||||
* Fix namespaces
|
||||
* Fix ogrepository paths
|
||||
* Change git repo path
|
||||
* Improvements for logging and error handling
|
||||
* Fix HTTP exception handling
|
||||
* Improve task management, cleanup when there are too many
|
||||
* More error logging
|
||||
* Mark git repo as a safe directory
|
||||
* Rework the ability to use a custom SSH key
|
||||
* Log every request
|
||||
* Branch deletion
|
||||
* Make branch deletion RESTful
|
||||
* Initial version of the API server
|
||||
* Add original repo_api
|
||||
* Convert to blueprint
|
||||
* Add port argument
|
||||
* Fix error handling
|
||||
* Add README
|
||||
* Load swagger from disk
|
||||
* Fix repository URL
|
||||
* Bump forgejo version
|
||||
* Add helpful script
|
||||
* Fix port argument
|
||||
* Refactoring for package support
|
||||
* Remove old code
|
||||
* Refactoring for packaging
|
||||
* opengnsys-forgejo package
|
||||
* Fix post-install for forgejo deployment
|
||||
* Fixes for running under gunicorn
|
||||
* Debian packaging
|
||||
* Add branches and tags creation endpoints
|
||||
* Add missing file
|
||||
* Rename service
|
||||
* Add templates
|
||||
* Disable tests
|
||||
* Fix permission problem
|
||||
* Fix ini path
|
||||
* Update changelog
|
||||
* Update changelog
|
||||
* Add package files
|
||||
* Add git image creation script
|
||||
* Slightly improve API for ogrepo usability
|
||||
* First commit
|
||||
* Add installer
|
||||
* Add requirements file
|
||||
|
||||
[ lgromero ]
|
||||
* refs #734 Creates first skeleton of symfony+swagger project
|
||||
|
||||
[ Vadim Troshchinskiy ]
|
||||
* Add Gitlib
|
||||
|
||||
[ lgromero ]
|
||||
* refs #734 Changes OgBootBundle name and adds a first endpoint to test
|
||||
* refs #734 Adds template of repository and branch endpoints
|
||||
|
||||
[ Vadim Troshchinskiy ]
|
||||
* Update docs to account for changes
|
||||
* Trivial API server
|
||||
* Ticket #753: Add repository listing
|
||||
* Ticket #735: List branches in repo
|
||||
* Add testing instructions
|
||||
* Agregar manejo de errrores
|
||||
* Ticket #741: Crear repo Ticket #736: Eliminar repo
|
||||
|
||||
[ lgromero ]
|
||||
* refs #734 Adds README for Api installation
|
||||
* refs #734 Control of errores and http codes in controler
|
||||
* refs #734 Renemas oggitservice
|
||||
|
||||
[ Vadim Troshchinskiy ]
|
||||
* Ticket #738, ticket #739: repo and sync backup protoype
|
||||
|
||||
[ lgromero ]
|
||||
* refs #734 Adds new endpoints sync and backup and status endpoint
|
||||
* refs #734 Adds nelmio api doc configuration
|
||||
* Adds .env file to root
|
||||
* refs #734 use environment variables in .env files and disable web depuration toolbar
|
||||
* refs #734 fix typo in .env and use oggit_url environment variable
|
||||
|
||||
[ Vadim Troshchinskiy ]
|
||||
* Ticket #738, ticket #739: git sync and backup
|
||||
|
||||
[ Nicolas Arenas ]
|
||||
* Add docker container files
|
||||
|
||||
[ Vadim Troshchinskiy ]
|
||||
* Ticket #737: GC
|
||||
* Use Paramiko and Gitpython for backups
|
||||
|
||||
[ Nicolas Arenas ]
|
||||
* Add mock api for testing dockerfile
|
||||
|
||||
[ Vadim Troshchinskiy ]
|
||||
* Ticket #740, listen on all hosts
|
||||
|
||||
[ lgromero ]
|
||||
* refs #734 Removes innecesaries parameters and changes php platform to 8.2
|
||||
* refs #734 just changes name and description in swagger web page
|
||||
|
||||
[ Vadim Troshchinskiy ]
|
||||
* Remove duplicated import
|
||||
* Documentation prototype
|
||||
* Update to 24.04, solves deployment issue
|
||||
* Add more documentation
|
||||
* Add API README
|
||||
* Add API examples
|
||||
* Update list of package requirements in oglive
|
||||
* Fix commandline parsing bug
|
||||
* Revert experimental Windows change
|
||||
* Fix ticket #770: Re-parse filesystems list after mounting
|
||||
* Use oglive server if ogrepository is not set
|
||||
* Ticket #770: Add sanity check
|
||||
* Ticket #771: Correctly create directories on metadata restoration
|
||||
* Ticket #780: Unmount before clone if needed
|
||||
* Fix ticket #800: sudo doesn't work
|
||||
|
||||
[ Vadim Trochinsky ]
|
||||
* Fix ticket #802: .git directory in filesystem root
|
||||
|
||||
[ Vadim Troshchinskiy ]
|
||||
* Fix ticket #805: Remove .git directory if it already exists when checking out
|
||||
* Ticket #770: Correctly update metadata when mounting and unmounting
|
||||
* Ticket #804: Move log
|
||||
* Fix ticket #902: .git directories can't be checked out
|
||||
* Lint fixes
|
||||
* Remove unused code
|
||||
* Lint fixes
|
||||
* Lint fixes
|
||||
* Lint fixes
|
||||
* Additional logging message
|
||||
* Lint fix
|
||||
* Fix ticket #907: mknod fails due to path not found
|
||||
* Initial implementation for commit, push, fetch.
|
||||
* Don't fail on empty lines in metadata, just skip them
|
||||
* Add documentation and functionality to progress hook (not used yet)
|
||||
* Pylint fixes
|
||||
* Ticket #908: Remove some unneeded warnings
|
||||
* Fix progress report
|
||||
* Ticket #906: Fix permissions on directories
|
||||
* Make pylint happy
|
||||
* Mount fix
|
||||
* Ticket #808: Initial implementation
|
||||
* Initial forgejo install
|
||||
* Deduplicate key extraction
|
||||
* Fix installer bugs and add documentation
|
||||
* Change user to oggit
|
||||
* Fix NTFS ID modification implementation
|
||||
* Implement system-specific EFI data support
|
||||
* Fix encoding when reading system uuid
|
||||
* Fix and refactor slightly EFI implementation
|
||||
* Add Windows BCD decoding tool
|
||||
* Check module loading and unloading, modprobe works on oglive now
|
||||
* Make EFI deployment more flexible
|
||||
* Add organization API call
|
||||
* Fix bash library path
|
||||
* Fix repo paths for forgejo
|
||||
* Update documentation
|
||||
* Sync to ensure everything is written
|
||||
* Refactoring and more pydoc
|
||||
* Add more documentation
|
||||
* Improve installer documentation
|
||||
* Improve gitlib instructions
|
||||
* Add missing files
|
||||
* Partial setsshkey implementation
|
||||
* Fix SSH key generation and extraction
|
||||
* Initial package contents
|
||||
* Add Debian packaging
|
||||
* Add pylkid
|
||||
* Add pyblkid debian files
|
||||
* Use packaged pyblkid
|
||||
* More detailed API logging
|
||||
* Improve logging
|
||||
* Add oglive key to forgejo
|
||||
* Add original source
|
||||
* Always re-download forgejo, even if installed.
|
||||
* Remove obsolete code that stopped being relevant with Forgejo
|
||||
* Move python modules to /opt/opengnsys-modules
|
||||
* Use absolute paths in initrd modification
|
||||
* Add timestamp to ssh key title, forgejo doesn't like duplicates
|
||||
* Skip past symlinks and problems in oglive modification
|
||||
* Get keys from squashfs instead of initrd to work with current oglive packaging
|
||||
* Fix trivial bug
|
||||
* Move modules to /usr/share/opengnsys
|
||||
* Move packages to /usr/share
|
||||
|
||||
[ Angel Rodriguez ]
|
||||
* Add gitlib/README-en.md
|
||||
* Add api/README-en.md
|
||||
* Add installer/README-en.md
|
||||
|
||||
[ Vadim Troshchinskiy ]
|
||||
* Skip NTFS code on non-Windows
|
||||
* Store and restore GPT partition UUIDs
|
||||
* Update READMEs
|
||||
* BCD constants
|
||||
* Use tqdm
|
||||
* Constants
|
||||
* Add extra mounts update
|
||||
* Better status reports
|
||||
* Make log filename machine-dependent Move kernel args parsing
|
||||
* Make unmounting more robust
|
||||
* Improve repository initialization
|
||||
* Make --pull work like the other commands
|
||||
* Add packages
|
||||
* Update documentation
|
||||
* Ignore python cache
|
||||
* Ignore more files
|
||||
* add python libarchive-c original package
|
||||
* Add pyblkid copyright file
|
||||
* Add make_orig script
|
||||
* Reorder and fix for ogrepository reorganization
|
||||
* Restructure git installer to work without ogboot on the same machine, update docs
|
||||
* Update english documentation
|
||||
* Improve installation process, make it possible to extract keys from oglive
|
||||
* Fix namespaces
|
||||
* Fix ogrepository paths
|
||||
* Change git repo path
|
||||
* Improvements for logging and error handling
|
||||
* Fix HTTP exception handling
|
||||
* Improve task management, cleanup when there are too many
|
||||
* More error logging
|
||||
* Mark git repo as a safe directory
|
||||
* Rework the ability to use a custom SSH key
|
||||
* Log every request
|
||||
* Branch deletion
|
||||
* Make branch deletion RESTful
|
||||
* Initial version of the API server
|
||||
* Add original repo_api
|
||||
* Convert to blueprint
|
||||
* Add port argument
|
||||
* Fix error handling
|
||||
* Add README
|
||||
* Load swagger from disk
|
||||
* Fix repository URL
|
||||
* Bump forgejo version
|
||||
* Add helpful script
|
||||
* Fix port argument
|
||||
* Refactoring for package support
|
||||
* Remove old code
|
||||
* Refactoring for packaging
|
||||
* opengnsys-forgejo package
|
||||
* Fix post-install for forgejo deployment
|
||||
* Fixes for running under gunicorn
|
||||
* Debian packaging
|
||||
* Add branches and tags creation endpoints
|
||||
* Add missing file
|
||||
* Rename service
|
||||
* Add templates
|
||||
* Disable tests
|
||||
* Fix permission problem
|
||||
* Fix ini path
|
||||
* Update changelog
|
||||
* Update changelog
|
||||
* Add package files
|
||||
* Add git image creation script
|
||||
* Slightly improve API for ogrepo usability
|
||||
* Update changelog
|
||||
* First commit
|
||||
* Add installer
|
||||
* Add requirements file
|
||||
|
||||
[ lgromero ]
|
||||
* refs #734 Creates first skeleton of symfony+swagger project
|
||||
|
||||
[ Vadim Troshchinskiy ]
|
||||
* Add Gitlib
|
||||
|
||||
[ lgromero ]
|
||||
* refs #734 Changes OgBootBundle name and adds a first endpoint to test
|
||||
* refs #734 Adds template of repository and branch endpoints
|
||||
|
||||
[ Vadim Troshchinskiy ]
|
||||
* Update docs to account for changes
|
||||
* Trivial API server
|
||||
* Ticket #753: Add repository listing
|
||||
* Ticket #735: List branches in repo
|
||||
* Add testing instructions
|
||||
* Agregar manejo de errrores
|
||||
* Ticket #741: Crear repo Ticket #736: Eliminar repo
|
||||
|
||||
[ lgromero ]
|
||||
* refs #734 Adds README for Api installation
|
||||
* refs #734 Control of errores and http codes in controler
|
||||
* refs #734 Renemas oggitservice
|
||||
|
||||
[ Vadim Troshchinskiy ]
|
||||
* Ticket #738, ticket #739: repo and sync backup protoype
|
||||
|
||||
[ lgromero ]
|
||||
* refs #734 Adds new endpoints sync and backup and status endpoint
|
||||
* refs #734 Adds nelmio api doc configuration
|
||||
* Adds .env file to root
|
||||
* refs #734 use environment variables in .env files and disable web depuration toolbar
|
||||
* refs #734 fix typo in .env and use oggit_url environment variable
|
||||
|
||||
[ Vadim Troshchinskiy ]
|
||||
* Ticket #738, ticket #739: git sync and backup
|
||||
|
||||
[ Nicolas Arenas ]
|
||||
* Add docker container files
|
||||
|
||||
[ Vadim Troshchinskiy ]
|
||||
* Ticket #737: GC
|
||||
* Use Paramiko and Gitpython for backups
|
||||
|
||||
[ Nicolas Arenas ]
|
||||
* Add mock api for testing dockerfile
|
||||
|
||||
[ Vadim Troshchinskiy ]
|
||||
* Ticket #740, listen on all hosts
|
||||
|
||||
[ lgromero ]
|
||||
* refs #734 Removes innecesaries parameters and changes php platform to 8.2
|
||||
* refs #734 just changes name and description in swagger web page
|
||||
|
||||
[ Vadim Troshchinskiy ]
|
||||
* Remove duplicated import
|
||||
* Documentation prototype
|
||||
* Update to 24.04, solves deployment issue
|
||||
* Add more documentation
|
||||
* Add API README
|
||||
* Add API examples
|
||||
* Update list of package requirements in oglive
|
||||
* Fix commandline parsing bug
|
||||
* Revert experimental Windows change
|
||||
* Fix ticket #770: Re-parse filesystems list after mounting
|
||||
* Use oglive server if ogrepository is not set
|
||||
* Ticket #770: Add sanity check
|
||||
* Ticket #771: Correctly create directories on metadata restoration
|
||||
* Ticket #780: Unmount before clone if needed
|
||||
* Fix ticket #800: sudo doesn't work
|
||||
|
||||
[ Vadim Trochinsky ]
|
||||
* Fix ticket #802: .git directory in filesystem root
|
||||
|
||||
[ Vadim Troshchinskiy ]
|
||||
* Fix ticket #805: Remove .git directory if it already exists when checking out
|
||||
* Ticket #770: Correctly update metadata when mounting and unmounting
|
||||
* Ticket #804: Move log
|
||||
* Fix ticket #902: .git directories can't be checked out
|
||||
* Lint fixes
|
||||
* Remove unused code
|
||||
* Lint fixes
|
||||
* Lint fixes
|
||||
* Lint fixes
|
||||
* Additional logging message
|
||||
* Lint fix
|
||||
* Fix ticket #907: mknod fails due to path not found
|
||||
* Initial implementation for commit, push, fetch.
|
||||
* Don't fail on empty lines in metadata, just skip them
|
||||
* Add documentation and functionality to progress hook (not used yet)
|
||||
* Pylint fixes
|
||||
* Ticket #908: Remove some unneeded warnings
|
||||
* Fix progress report
|
||||
* Ticket #906: Fix permissions on directories
|
||||
* Make pylint happy
|
||||
* Mount fix
|
||||
* Ticket #808: Initial implementation
|
||||
* Initial forgejo install
|
||||
* Deduplicate key extraction
|
||||
* Fix installer bugs and add documentation
|
||||
* Change user to oggit
|
||||
* Fix NTFS ID modification implementation
|
||||
* Implement system-specific EFI data support
|
||||
* Fix encoding when reading system uuid
|
||||
* Fix and refactor slightly EFI implementation
|
||||
* Add Windows BCD decoding tool
|
||||
* Check module loading and unloading, modprobe works on oglive now
|
||||
* Make EFI deployment more flexible
|
||||
* Add organization API call
|
||||
* Fix bash library path
|
||||
* Fix repo paths for forgejo
|
||||
* Update documentation
|
||||
* Sync to ensure everything is written
|
||||
* Refactoring and more pydoc
|
||||
* Add more documentation
|
||||
* Improve installer documentation
|
||||
* Improve gitlib instructions
|
||||
* Add missing files
|
||||
* Partial setsshkey implementation
|
||||
* Fix SSH key generation and extraction
|
||||
* Initial package contents
|
||||
* Add Debian packaging
|
||||
* Add pylkid
|
||||
* Add pyblkid debian files
|
||||
* Use packaged pyblkid
|
||||
* More detailed API logging
|
||||
* Improve logging
|
||||
* Add oglive key to forgejo
|
||||
* Add original source
|
||||
* Always re-download forgejo, even if installed.
|
||||
* Remove obsolete code that stopped being relevant with Forgejo
|
||||
* Move python modules to /opt/opengnsys-modules
|
||||
* Use absolute paths in initrd modification
|
||||
* Add timestamp to ssh key title, forgejo doesn't like duplicates
|
||||
* Skip past symlinks and problems in oglive modification
|
||||
* Get keys from squashfs instead of initrd to work with current oglive packaging
|
||||
* Fix trivial bug
|
||||
* Move modules to /usr/share/opengnsys
|
||||
* Move packages to /usr/share
|
||||
|
||||
[ Angel Rodriguez ]
|
||||
* Add gitlib/README-en.md
|
||||
* Add api/README-en.md
|
||||
* Add installer/README-en.md
|
||||
|
||||
[ Vadim Troshchinskiy ]
|
||||
* Skip NTFS code on non-Windows
|
||||
* Store and restore GPT partition UUIDs
|
||||
* Update READMEs
|
||||
* BCD constants
|
||||
* Use tqdm
|
||||
* Constants
|
||||
* Add extra mounts update
|
||||
* Better status reports
|
||||
* Make log filename machine-dependent Move kernel args parsing
|
||||
* Make unmounting more robust
|
||||
* Improve repository initialization
|
||||
* Make --pull work like the other commands
|
||||
* Add packages
|
||||
* Update documentation
|
||||
* Ignore python cache
|
||||
* Ignore more files
|
||||
* add python libarchive-c original package
|
||||
* Add pyblkid copyright file
|
||||
* Add make_orig script
|
||||
* Reorder and fix for ogrepository reorganization
|
||||
* Restructure git installer to work without ogboot on the same machine, update docs
|
||||
* Update english documentation
|
||||
* Improve installation process, make it possible to extract keys from oglive
|
||||
* Fix namespaces
|
||||
* Fix ogrepository paths
|
||||
* Change git repo path
|
||||
* Improvements for logging and error handling
|
||||
* Fix HTTP exception handling
|
||||
* Improve task management, cleanup when there are too many
|
||||
* More error logging
|
||||
* Mark git repo as a safe directory
|
||||
* Rework the ability to use a custom SSH key
|
||||
* Log every request
|
||||
* Branch deletion
|
||||
* Make branch deletion RESTful
|
||||
* Initial version of the API server
|
||||
* Add original repo_api
|
||||
* Convert to blueprint
|
||||
* Add port argument
|
||||
* Fix error handling
|
||||
* Add README
|
||||
* Load swagger from disk
|
||||
* Fix repository URL
|
||||
* Bump forgejo version
|
||||
* Add helpful script
|
||||
* Fix port argument
|
||||
* Refactoring for package support
|
||||
* Remove old code
|
||||
* Refactoring for packaging
|
||||
* opengnsys-forgejo package
|
||||
* Fix post-install for forgejo deployment
|
||||
* Fixes for running under gunicorn
|
||||
* Debian packaging
|
||||
* Add branches and tags creation endpoints
|
||||
* Add missing file
|
||||
* Rename service
|
||||
* Add templates
|
||||
* Disable tests
|
||||
* Fix permission problem
|
||||
* Fix ini path
|
||||
* Update changelog
|
||||
* Update changelog
|
||||
* Add package files
|
||||
* Add git image creation script
|
||||
* Slightly improve API for ogrepo usability
|
||||
* Update changelog
|
||||
* Update changelog
|
||||
|
||||
-- OpenGnsys <opengnsys@opengnsys.com> Mon, 16 Jun 2025 21:23:34 +0000
|
|
@ -1,29 +0,0 @@
|
|||
Source: opengnsys-gitinstaller
|
||||
Section: unknown
|
||||
Priority: optional
|
||||
Maintainer: OpenGnsys <opengnsys@opengnsys.es>
|
||||
Rules-Requires-Root: no
|
||||
Build-Depends:
|
||||
debhelper-compat (= 13),
|
||||
Standards-Version: 4.6.2
|
||||
Homepage: https://opengnsys.es
|
||||
#Vcs-Browser: https://salsa.debian.org/debian/ogboot
|
||||
#Vcs-Git: https://salsa.debian.org/debian/ogboot.git
|
||||
|
||||
Package: opengnsys-gitinstaller
|
||||
Architecture: any
|
||||
Multi-Arch: foreign
|
||||
Depends:
|
||||
${shlibs:Depends},
|
||||
${misc:Depends},
|
||||
bsdextrautils,
|
||||
debconf (>= 1.5.0),
|
||||
opengnsys-libarchive-c,
|
||||
python3,
|
||||
python3-aniso8601,
|
||||
python3-git,
|
||||
python3-termcolor,
|
||||
python3-tqdm
|
||||
Conflicts:
|
||||
Description: Opengnsys installer library for OgGit
|
||||
Files for OpenGnsys Git support
|
|
@ -1,43 +0,0 @@
|
|||
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||
Source: <url://example.com>
|
||||
Upstream-Name: ogboot
|
||||
Upstream-Contact: <preferred name and address to reach the upstream project>
|
||||
|
||||
Files:
|
||||
*
|
||||
Copyright:
|
||||
<years> <put author's name and email here>
|
||||
<years> <likewise for another author>
|
||||
License: GPL-3.0+
|
||||
|
||||
Files:
|
||||
debian/*
|
||||
Copyright:
|
||||
2025 vagrant <vagrant@build>
|
||||
License: GPL-3.0+
|
||||
|
||||
License: GPL-3.0+
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
.
|
||||
This package is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
Comment:
|
||||
On Debian systems, the complete text of the GNU General
|
||||
Public License version 3 can be found in "/usr/share/common-licenses/GPL-3".
|
||||
|
||||
# Please also look if there are files or directories which have a
|
||||
# different copyright/license attached and list them here.
|
||||
# Please avoid picking licenses with terms that are more restrictive than the
|
||||
# packaged work, as it may make Debian's contributions unacceptable upstream.
|
||||
#
|
||||
# If you need, there are some extra license texts available in two places:
|
||||
# /usr/share/debhelper/dh_make/licenses/
|
||||
# /usr/share/common-licenses/
|
|
@ -1,2 +0,0 @@
|
|||
opengnsys-gitinstaller_0.5_amd64.buildinfo unknown optional
|
||||
opengnsys-gitinstaller_0.5_amd64.deb unknown optional
|
|
@ -1 +0,0 @@
|
|||
/opt/opengnsys/ogrepository/oggit/lib
|
|
@ -1 +0,0 @@
|
|||
opengnsys_git_installer.py /opt/opengnsys/ogrepository/oggit/lib
|
|
@ -1,2 +0,0 @@
|
|||
misc:Depends=
|
||||
misc:Pre-Depends=
|
|
@ -1,33 +0,0 @@
|
|||
#!/usr/bin/make -f
|
||||
|
||||
# See debhelper(7) (uncomment to enable).
|
||||
# Output every command that modifies files on the build system.
|
||||
#export DH_VERBOSE = 1
|
||||
|
||||
|
||||
# See FEATURE AREAS in dpkg-buildflags(1).
|
||||
#export DEB_BUILD_MAINT_OPTIONS = hardening=+all
|
||||
|
||||
# See ENVIRONMENT in dpkg-buildflags(1).
|
||||
# Package maintainers to append CFLAGS.
|
||||
#export DEB_CFLAGS_MAINT_APPEND = -Wall -pedantic
|
||||
# Package maintainers to append LDFLAGS.
|
||||
#export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed
|
||||
|
||||
|
||||
%:
|
||||
dh $@
|
||||
|
||||
|
||||
%:
|
||||
dh $@
|
||||
|
||||
# Ejecutar composer install durante la fase de construcción
|
||||
override_dh_auto_build:
|
||||
|
||||
|
||||
# dh_make generated override targets.
|
||||
# This is an example for Cmake (see <https://bugs.debian.org/641051>).
|
||||
#override_dh_auto_configure:
|
||||
# dh_auto_configure -- \
|
||||
# -DCMAKE_LIBRARY_PATH=$(DEB_HOST_MULTIARCH)
|
|
@ -1,11 +0,0 @@
|
|||
[Service]
|
||||
RestartSec=10s
|
||||
Type=simple
|
||||
User={gitapi_user}
|
||||
Group={gitapi_group}
|
||||
WorkingDirectory={gitapi_work_path}
|
||||
ExecStart=/usr/bin/gunicorn -w 4 -b {gitapi_host}:{gitapi_port} gitapi:app
|
||||
Restart=always
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
|
@ -1,31 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
if [ ! -f "/etc/apt/sources.list.d/opengnsys.sources" ] ; then
|
||||
|
||||
cat > /etc/apt/sources.list.d/opengnsys.sources <<HERE
|
||||
Types: deb
|
||||
URIs: https://ognproject.evlt.uma.es/debian-opengnsys/opengnsys
|
||||
Suites: noble
|
||||
Components: main
|
||||
Signed-By:
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
.
|
||||
mDMEZzx/SxYJKwYBBAHaRw8BAQdAa83CuAJ5/+7Pn9LHT/k34EAGpx5FnT/ExHSj
|
||||
XZG1JES0Ik9wZW5HbnN5cyA8b3Blbmduc3lzQG9wZW5nbnN5cy5lcz6ImQQTFgoA
|
||||
QRYhBC+J38Xsso227ZbDVt2S5xJQRhKDBQJnPH9LAhsDBQkFo5qABQsJCAcCAiIC
|
||||
BhUKCQgLAgQWAgMBAh4HAheAAAoJEN2S5xJQRhKDW/MBAO6swnpwdrbm48ypMyPh
|
||||
NboxvF7rCqBqHWwRHvkvrq7pAP9zd98r7z2AvqVXZxnaCsLTUNMEL12+DVZAUZ1G
|
||||
EquRBbg4BGc8f0sSCisGAQQBl1UBBQEBB0B6D6tkrwXSHi7ebGYsiMPntqwdkQ/S
|
||||
84SFTlSxRqdXfgMBCAeIfgQYFgoAJhYhBC+J38Xsso227ZbDVt2S5xJQRhKDBQJn
|
||||
PH9LAhsMBQkFo5qAAAoJEN2S5xJQRhKDJ+cBAM9jYbeq5VXkHLfODeVztgSXnSUe
|
||||
yklJ18oQmpeK5eWeAQDKYk/P0R+1ZJDItxkeP6pw62bCDYGQDvdDGPMAaIT6CA==
|
||||
=xcNc
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
||||
HERE
|
||||
fi
|
||||
|
||||
|
||||
apt update
|
||||
apt install -y python3-git opengnsys-libarchive-c python3-termcolor python3-requests python3-tqdm bsdextrautils
|
|
@ -28,28 +28,13 @@ import requests
|
|||
import tempfile
|
||||
import hashlib
|
||||
import datetime
|
||||
import tqdm
|
||||
|
||||
#FORGEJO_VERSION="8.0.3"
|
||||
FORGEJO_VERSION="10.0.3"
|
||||
FORGEJO_VERSION="9.0.0"
|
||||
FORGEJO_URL=f"https://codeberg.org/forgejo/forgejo/releases/download/v{FORGEJO_VERSION}/forgejo-{FORGEJO_VERSION}-linux-amd64"
|
||||
|
||||
|
||||
|
||||
def download_with_progress(url, output_file):
|
||||
|
||||
with requests.get(url, stream=True, timeout=60) as req:
|
||||
progress = tqdm.tqdm()
|
||||
progress.total = int(req.headers["Content-Length"])
|
||||
progress.unit_scale = True
|
||||
progress.desc = "Downloading"
|
||||
|
||||
for chunk in req.iter_content(chunk_size=8192):
|
||||
output_file.write(chunk)
|
||||
progress.n = progress.n + len(chunk)
|
||||
progress.refresh()
|
||||
|
||||
progress.close()
|
||||
|
||||
def show_error(*args):
|
||||
"""
|
||||
|
@ -80,23 +65,6 @@ class RequirementException(Exception):
|
|||
super().__init__(message)
|
||||
self.message = message
|
||||
|
||||
|
||||
class OptionalDependencyException(Exception):
|
||||
"""Excepción que indica que nos falta algún requisito opcional
|
||||
|
||||
Attributes:
|
||||
message (str): Mensaje de error mostrado al usuario
|
||||
"""
|
||||
|
||||
def __init__(self, message):
|
||||
"""Inicializar OptionalDependencyException.
|
||||
|
||||
Args:
|
||||
message (str): Mensaje de error mostrado al usuario
|
||||
"""
|
||||
super().__init__(message)
|
||||
self.message = message
|
||||
|
||||
class FakeTemporaryDirectory:
|
||||
"""Imitación de TemporaryDirectory para depuración"""
|
||||
def __init__(self, dirname):
|
||||
|
@ -106,57 +74,6 @@ class FakeTemporaryDirectory:
|
|||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class OgliveMounter:
|
||||
"""
|
||||
A class to handle mounting of Oglive images from a given URL or local file.
|
||||
|
||||
Attributes:
|
||||
logger (logging.Logger): Logger instance for logging messages.
|
||||
squashfs (str): Path to the squashfs file within the mounted Oglive image.
|
||||
initrd (str): Path to the initrd image within the mounted Oglive image.
|
||||
kernel (str): Path to the kernel image within the mounted Oglive image.
|
||||
|
||||
Methods:
|
||||
__init__(url):
|
||||
Initializes the OgliveMounter instance, downloads the Oglive image if URL is provided,
|
||||
and mounts the image to a temporary directory.
|
||||
|
||||
__del__():
|
||||
Unmounts the mounted directory and cleans up resources.
|
||||
"""
|
||||
def __init__(self, url):
|
||||
self.logger = logging.getLogger("OgliveMounter")
|
||||
self.mountdir = tempfile.TemporaryDirectory()
|
||||
|
||||
self.logger.info("Will mount oglive found at %s", url)
|
||||
|
||||
if url.startswith("http://") or url.startswith("https://"):
|
||||
self.logger.debug("We got an URL, downloading %s", url)
|
||||
self.tempfile = tempfile.NamedTemporaryFile(mode='wb')
|
||||
filename = self.tempfile.name
|
||||
|
||||
download_with_progress(url, self.tempfile)
|
||||
else:
|
||||
self.logger.debug("We got a filename")
|
||||
filename = url
|
||||
|
||||
self.logger.debug("Mounting %s at %s", filename, self.mountdir.name)
|
||||
subprocess.run(["/usr/bin/mount", filename, self.mountdir.name], check=True)
|
||||
|
||||
self.squashfs = os.path.join(self.mountdir.name, "ogclient", "ogclient.sqfs")
|
||||
self.initrd = os.path.join(self.mountdir.name, "ogclient", "oginitrd.img")
|
||||
self.kernel = os.path.join(self.mountdir.name, "ogclient", "ogvmlinuz")
|
||||
|
||||
|
||||
def __del__(self):
|
||||
self.logger.debug("Unmounting directory %s", self.mountdir.name)
|
||||
subprocess.run(["/usr/bin/umount", self.mountdir.name], check=True)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class Oglive:
|
||||
"""Interfaz a utilidad oglivecli
|
||||
|
||||
|
@ -171,10 +88,6 @@ class Oglive:
|
|||
|
||||
def _cmd(self, args):
|
||||
cmd = [self.binary] + args
|
||||
|
||||
if not os.path.exists(self.binary):
|
||||
raise OptionalDependencyException("Missing oglivecli command. Please use --squashfs-file (see README.md for more details)")
|
||||
|
||||
self.__logger.debug("comando: %s", cmd)
|
||||
|
||||
proc = subprocess.run(cmd, shell=False, check=True, capture_output=True)
|
||||
|
@ -209,46 +122,19 @@ class OpengnsysGitInstaller:
|
|||
self.__logger.debug("Inicializando")
|
||||
self.testmode = False
|
||||
self.base_path = "/opt/opengnsys"
|
||||
self.ogrepository_base_path = os.path.join(self.base_path, "ogrepository")
|
||||
self.git_basedir = "base.git"
|
||||
self.email = "OpenGnsys@opengnsys.com"
|
||||
|
||||
self.opengnsys_bin_path = os.path.join(self.base_path, "bin")
|
||||
self.opengnsys_etc_path = os.path.join(self.base_path, "etc")
|
||||
|
||||
self.forgejo_user = "oggit"
|
||||
self.forgejo_password = "opengnsys"
|
||||
self.forgejo_organization = "opengnsys"
|
||||
self.forgejo_port = 3100
|
||||
|
||||
self.forgejo_bin_path = os.path.join(self.ogrepository_base_path, "bin")
|
||||
self.forgejo_exe = os.path.join(self.forgejo_bin_path, "forgejo")
|
||||
self.forgejo_conf_dir_path = os.path.join(self.ogrepository_base_path, "etc", "forgejo")
|
||||
|
||||
self.lfs_dir_path = os.path.join(self.ogrepository_base_path, "oggit", "git-lfs")
|
||||
self.git_dir_path = os.path.join(self.ogrepository_base_path, "oggit", "git")
|
||||
|
||||
self.forgejo_var_dir_path = os.path.join(self.ogrepository_base_path, "var", "lib", "forgejo")
|
||||
self.forgejo_work_dir_path = os.path.join(self.forgejo_var_dir_path, "work")
|
||||
self.forgejo_work_custom_dir_path = os.path.join(self.forgejo_work_dir_path, "custom")
|
||||
self.forgejo_db_dir_path = os.path.join(self.forgejo_var_dir_path, "db")
|
||||
self.forgejo_data_dir_path = os.path.join(self.forgejo_var_dir_path, "data")
|
||||
|
||||
self.forgejo_db_path = os.path.join(self.forgejo_db_dir_path, "forgejo.db")
|
||||
|
||||
self.forgejo_log_dir_path = os.path.join(self.ogrepository_base_path, "log", "forgejo")
|
||||
|
||||
|
||||
self.dependencies = ["git", "python3-flask", "python3-flasgger", "gunicorn", ]
|
||||
self.forgejo_port = 3000
|
||||
|
||||
self.set_ssh_user_group("oggit", "oggit")
|
||||
|
||||
self.temp_dir = None
|
||||
self.script_path = os.path.realpath(os.path.dirname(__file__))
|
||||
|
||||
# Where we look for forgejo-app.ini and similar templates.
|
||||
self.template_path = self.script_path
|
||||
|
||||
# Possible names for SSH public keys
|
||||
self.ssh_key_users = ["root", "opengnsys"]
|
||||
self.key_names = ["id_rsa.pub", "id_ed25519.pub", "id_ecdsa.pub", "id_ed25519_sk.pub", "id_ecdsa_sk.pub"]
|
||||
|
@ -261,14 +147,10 @@ class OpengnsysGitInstaller:
|
|||
for kp in self.key_paths:
|
||||
self.key_paths_dict[kp] = 1
|
||||
|
||||
os.environ["PATH"] += os.pathsep + os.path.join(self.base_path, "bin")
|
||||
|
||||
self.oglive = Oglive()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def set_testmode(self, value):
|
||||
"""Establece el modo de prueba"""
|
||||
self.testmode = value
|
||||
|
@ -277,6 +159,10 @@ class OpengnsysGitInstaller:
|
|||
"""Ignorar requisito de clave de ssh para el instalador"""
|
||||
self.ignoresshkey = value
|
||||
|
||||
def set_usesshkey(self, value):
|
||||
"""Usar clave de ssh especificada"""
|
||||
self.usesshkey = value
|
||||
|
||||
def set_basepath(self, value):
|
||||
"""Establece ruta base de OpenGnsys
|
||||
Valor por defecto: /opt/opengnsys
|
||||
|
@ -332,7 +218,7 @@ class OpengnsysGitInstaller:
|
|||
def init_git_repo(self, reponame):
|
||||
"""Inicializa un repositorio Git"""
|
||||
# Creamos repositorio
|
||||
ogdir_images = os.path.join(self.ogrepository_base_path, "oggit")
|
||||
ogdir_images = os.path.join(self.base_path, "images")
|
||||
self.__logger.info("Creando repositorio de GIT %s", reponame)
|
||||
|
||||
os.makedirs(os.path.join(ogdir_images, self.git_basedir), exist_ok=True)
|
||||
|
@ -408,60 +294,42 @@ class OpengnsysGitInstaller:
|
|||
raise TimeoutError("Timed out waiting for connection!")
|
||||
|
||||
|
||||
def add_ssh_key_from_squashfs(self, oglive_num = None, squashfs_file = None, oglive_file = None):
|
||||
|
||||
name = "(unknown)"
|
||||
mounter = None
|
||||
|
||||
if not oglive_file is None:
|
||||
mounter = OgliveMounter(oglive_file)
|
||||
squashfs_file = mounter.squashfs
|
||||
|
||||
if squashfs_file is None:
|
||||
if oglive_num is None:
|
||||
self.__logger.info("Using default oglive")
|
||||
oglive_num = self.oglive.get_default()
|
||||
else:
|
||||
self.__logger.info("Using oglive %i", oglive_num)
|
||||
|
||||
name = self.oglive.get_clients()[str(oglive_num)]
|
||||
def add_ssh_key_from_squashfs(self, oglive_num = None):
|
||||
|
||||
if oglive_num is None:
|
||||
self.__logger.info("Using default oglive")
|
||||
oglive_num = int(self.oglive.get_default())
|
||||
else:
|
||||
self.__logger.info("Using specified squashfs file %s", squashfs_file)
|
||||
name = os.path.basename(squashfs_file)
|
||||
self.__logger.info("Using oglive %i", oglive_num)
|
||||
|
||||
oglive_client = self.oglive.get_clients()[str(oglive_num)]
|
||||
self.__logger.info("Oglive is %s", oglive_client)
|
||||
|
||||
keys = self.extract_ssh_keys_from_squashfs(oglive_num = oglive_num, squashfs_file=squashfs_file)
|
||||
retvals = []
|
||||
keys = installer.extract_ssh_keys(oglive_num = oglive_num)
|
||||
for k in keys:
|
||||
timestamp = '{:%Y-%m-%d %H:%M:%S}'.format(datetime.datetime.now())
|
||||
retvals = retvals + [self.add_forgejo_sshkey(k, f"Key for {name} ({timestamp})")]
|
||||
|
||||
return retvals
|
||||
installer.add_forgejo_sshkey(k, f"Key for {oglive_client} ({timestamp})")
|
||||
|
||||
|
||||
def extract_ssh_keys_from_squashfs(self, oglive_num = None, squashfs_file = None):
|
||||
|
||||
def extract_ssh_keys(self, oglive_num = None):
|
||||
public_keys = []
|
||||
|
||||
|
||||
squashfs = "ogclient.sqfs"
|
||||
|
||||
if squashfs_file is None:
|
||||
tftp_dir = os.path.join(self.base_path, "tftpboot")
|
||||
tftp_dir = os.path.join(self.base_path, "tftpboot")
|
||||
|
||||
if oglive_num is None:
|
||||
self.__logger.info("Reading from default oglive")
|
||||
oglive_num = self.oglive.get_default()
|
||||
else:
|
||||
self.__logger.info("Reading from oglive %i", oglive_num)
|
||||
|
||||
oglive_client = self.oglive.get_clients()[str(oglive_num)]
|
||||
self.__logger.info("Oglive is %s", oglive_client)
|
||||
|
||||
client_squashfs_path = os.path.join(tftp_dir, oglive_client, squashfs)
|
||||
if oglive_num is None:
|
||||
self.__logger.info("Reading from default oglive")
|
||||
oglive_num = self.oglive.get_default()
|
||||
else:
|
||||
self.__logger.info("Using specified squashfs file %s", squashfs_file)
|
||||
client_squashfs_path = squashfs_file
|
||||
self.__logger.info("Reading from oglive %i", oglive_num)
|
||||
|
||||
oglive_client = self.oglive.get_clients()[str(oglive_num)]
|
||||
self.__logger.info("Oglive is %s", oglive_client)
|
||||
|
||||
client_squashfs_path = os.path.join(tftp_dir, oglive_client, squashfs)
|
||||
|
||||
self.__logger.info("Mounting %s", client_squashfs_path)
|
||||
mount_tempdir = tempfile.TemporaryDirectory()
|
||||
|
@ -484,75 +352,49 @@ class OpengnsysGitInstaller:
|
|||
return public_keys
|
||||
|
||||
|
||||
def extract_ssh_key_from_initrd(self, oglive_number = None, initrd_file = None):
|
||||
def _extract_ssh_key_from_initrd(self):
|
||||
public_key=""
|
||||
|
||||
INITRD = "oginitrd.img"
|
||||
|
||||
self.__logger.debug("Extracting ssh key from initrd")
|
||||
tftp_dir = os.path.join(self.base_path, "tftpboot")
|
||||
default_num = self.oglive.get_default()
|
||||
default_client = self.oglive.get_clients()[default_num]
|
||||
client_initrd_path = os.path.join(tftp_dir, default_client, INITRD)
|
||||
|
||||
if initrd_file is None:
|
||||
self.__logger.debug("Looking for initrd file")
|
||||
tftp_dir = os.path.join(self.base_path, "tftpboot")
|
||||
if oglive_number is None:
|
||||
oglive_number = self.oglive.get_default()
|
||||
#self.temp_dir = self._get_tempdir()
|
||||
|
||||
oglive_client = self.oglive.get_clients()[oglive_number]
|
||||
client_initrd_path = os.path.join(tftp_dir, oglive_client, INITRD)
|
||||
if self.usesshkey:
|
||||
with open(self.usesshkey, 'r') as f:
|
||||
public_key = f.read().strip()
|
||||
|
||||
self.__logger.debug("Found at %s", client_initrd_path)
|
||||
else:
|
||||
self.__logger.debug("Using provided initrd file %s", initrd_file)
|
||||
client_initrd_path = initrd_file
|
||||
if os.path.isfile(client_initrd_path):
|
||||
#os.makedirs(temp_dir, exist_ok=True)
|
||||
#os.chdir(self.temp_dir.name)
|
||||
self.__logger.debug("Descomprimiendo %s", client_initrd_path)
|
||||
public_key = None
|
||||
with libarchive.file_reader(client_initrd_path) as initrd:
|
||||
for file in initrd:
|
||||
self.__logger.debug("Archivo: %s", file)
|
||||
|
||||
self.__logger.debug("Extracting key from %s", client_initrd_path)
|
||||
pathname = file.pathname;
|
||||
if pathname.startswith("./"):
|
||||
pathname = pathname[2:]
|
||||
|
||||
if os.path.isfile(client_initrd_path):
|
||||
#os.makedirs(temp_dir, exist_ok=True)
|
||||
#os.chdir(self.temp_dir.name)
|
||||
self.__logger.debug("Uncompressing %s", client_initrd_path)
|
||||
public_key = None
|
||||
with libarchive.file_reader(client_initrd_path) as initrd:
|
||||
for file in initrd:
|
||||
self.__logger.debug("File: %s", file)
|
||||
if pathname in self.key_paths_dict:
|
||||
data = bytearray()
|
||||
for block in file.get_blocks():
|
||||
data = data + block
|
||||
public_key = data.decode('utf-8').strip()
|
||||
|
||||
pathname = file.pathname;
|
||||
if pathname.startswith("./"):
|
||||
pathname = pathname[2:]
|
||||
|
||||
if pathname in self.key_paths_dict:
|
||||
self.__logger.info("Found key %s, extracting", pathname)
|
||||
|
||||
data = bytearray()
|
||||
for block in file.get_blocks():
|
||||
data = data + block
|
||||
public_key = data.decode('utf-8').strip()
|
||||
|
||||
break
|
||||
else:
|
||||
print(f"Failed to find initrd at {client_initrd_path}")
|
||||
exit(2)
|
||||
|
||||
if not public_key:
|
||||
self.__logger.warning("Failed to find a SSH key")
|
||||
break
|
||||
else:
|
||||
print(f"No se encuentra la imagen de initrd {client_initrd_path}")
|
||||
exit(2)
|
||||
|
||||
return public_key
|
||||
|
||||
def get_image_paths(self, oglive_num = None):
|
||||
squashfs = "ogclient.sqfs"
|
||||
|
||||
if oglive_num is None:
|
||||
self.__logger.info("Will modify default client")
|
||||
oglive_num = self.oglive.get_default()
|
||||
|
||||
tftp_dir = os.path.join(self.base_path, "tftpboot")
|
||||
oglive_client = self.oglive.get_clients()[str(oglive_num)]
|
||||
|
||||
client_squashfs_path = os.path.join(tftp_dir, oglive_client, squashfs)
|
||||
|
||||
self.__logger.info("Squashfs: %s", client_squashfs_path)
|
||||
|
||||
|
||||
def set_ssh_key_in_initrd(self, client_num = None):
|
||||
INITRD = "oginitrd.img"
|
||||
|
||||
|
@ -692,25 +534,7 @@ class OpengnsysGitInstaller:
|
|||
|
||||
self.add_forgejo_sshkey(oglive_public_key, f"Key for {ogclient} ({timestamp})")
|
||||
|
||||
|
||||
def verify_requirements(self):
|
||||
self.__logger.info("verify_requirements()")
|
||||
|
||||
# Control básico de errores.
|
||||
self.__logger.debug("Comprobando euid")
|
||||
if os.geteuid() != 0:
|
||||
raise RequirementException("Sólo ejecutable por root")
|
||||
|
||||
if not os.path.exists("/etc/debian_version"):
|
||||
raise RequirementException("Instalación sólo soportada en Debian y Ubuntu")
|
||||
|
||||
|
||||
MIN_PYTHON = (3, 8)
|
||||
if sys.version_info < MIN_PYTHON:
|
||||
raise RequirementException(f"Python %s.%s mínimo requerido.\n" % MIN_PYTHON)
|
||||
|
||||
|
||||
def install_dependencies(self):
|
||||
def install(self):
|
||||
"""Instalar
|
||||
|
||||
Ejecuta todo el proceso de instalación incluyendo:
|
||||
|
@ -727,11 +551,32 @@ class OpengnsysGitInstaller:
|
|||
"""
|
||||
self.__logger.info("install()")
|
||||
|
||||
ogdir_images = os.path.join(self.base_path, "images")
|
||||
ENGINECFG = os.path.join(self.base_path, "client/etc/engine.cfg")
|
||||
|
||||
self.verify_requirements()
|
||||
os.environ["PATH"] += os.pathsep + os.path.join(self.base_path, "bin")
|
||||
tftp_dir = os.path.join(self.base_path, "tftpboot")
|
||||
INITRD = "oginitrd.img"
|
||||
self.temp_dir = self._get_tempdir()
|
||||
SSHUSER = "opengnsys"
|
||||
|
||||
self.__logger.debug("Installing dependencies")
|
||||
subprocess.run(["apt-get", "install", "-y"] + self.dependencies, check=True)
|
||||
|
||||
# Control básico de errores.
|
||||
self.__logger.debug("Comprobando euid")
|
||||
if os.geteuid() != 0:
|
||||
raise RequirementException("Sólo ejecutable por root")
|
||||
|
||||
if not os.path.exists("/etc/debian_version"):
|
||||
raise RequirementException("Instalación sólo soportada en Debian y Ubuntu")
|
||||
|
||||
|
||||
MIN_PYTHON = (3, 8)
|
||||
if sys.version_info < MIN_PYTHON:
|
||||
raise RequirementException(f"Python %s.%s mínimo requerido.\n" % MIN_PYTHON)
|
||||
|
||||
|
||||
self.__logger.debug("Instalando dependencias")
|
||||
subprocess.run(["apt-get", "install", "-y", "git"], check=True)
|
||||
|
||||
def _install_template(self, template, destination, keysvalues):
|
||||
|
||||
|
@ -742,10 +587,7 @@ class OpengnsysGitInstaller:
|
|||
data = template_file.read()
|
||||
|
||||
for key in keysvalues.keys():
|
||||
if isinstance(keysvalues[key], int):
|
||||
data = data.replace("{" + key + "}", str(keysvalues[key]))
|
||||
else:
|
||||
data = data.replace("{" + key + "}", keysvalues[key])
|
||||
data = data.replace("{" + key + "}", keysvalues[key])
|
||||
|
||||
with open(destination, "w+", encoding="utf-8") as out_file:
|
||||
out_file.write(data)
|
||||
|
@ -756,112 +598,88 @@ class OpengnsysGitInstaller:
|
|||
ret = subprocess.run(cmd, check=True,capture_output=True, encoding='utf-8')
|
||||
return ret.stdout.strip()
|
||||
|
||||
def install_api(self):
|
||||
self.__logger.info("Installing Git API")
|
||||
|
||||
opengnsys_bin_path = os.path.join(self.base_path, "bin")
|
||||
opengnsys_etc_path = os.path.join(self.base_path, "etc")
|
||||
|
||||
pathlib.Path(opengnsys_bin_path).mkdir(parents=True, exist_ok=True)
|
||||
|
||||
|
||||
data = {
|
||||
"gitapi_user" : "opengnsys",
|
||||
"gitapi_group" : "opengnsys",
|
||||
"gitapi_host" : "0.0.0.0",
|
||||
"gitapi_port" : 8087,
|
||||
"gitapi_work_path" : opengnsys_bin_path
|
||||
}
|
||||
|
||||
shutil.copy("../api/gitapi.py", opengnsys_bin_path + "/gitapi.py")
|
||||
shutil.copy("opengnsys_git_installer.py", opengnsys_bin_path + "/opengnsys_git_installer.py")
|
||||
|
||||
self._install_template(os.path.join(self.template_path, "gitapi.service"), "/etc/systemd/system/gitapi.service", data)
|
||||
def install_forgejo(self):
|
||||
self.__logger.info("Installing Forgejo")
|
||||
|
||||
|
||||
|
||||
self.__logger.debug("Reloading systemd and starting service")
|
||||
subprocess.run(["systemctl", "daemon-reload"], check=True)
|
||||
subprocess.run(["systemctl", "enable", "gitapi"], check=True)
|
||||
subprocess.run(["systemctl", "restart", "gitapi"], check=True)
|
||||
|
||||
bin_path = os.path.join(self.base_path, "bin", "forgejo")
|
||||
conf_dir_path = os.path.join(self.base_path, "etc", "forgejo")
|
||||
|
||||
|
||||
def _get_forgejo_data(self):
|
||||
conf_path = os.path.join(self.forgejo_conf_dir_path, "app.ini")
|
||||
lfs_dir_path = os.path.join(self.base_path, "images", "git-lfs")
|
||||
git_dir_path = os.path.join(self.base_path, "images", "git")
|
||||
|
||||
data = {
|
||||
"forgejo_user" : self.ssh_user,
|
||||
"forgejo_group" : self.ssh_group,
|
||||
"forgejo_port" : str(self.forgejo_port),
|
||||
"forgejo_bin" : self.forgejo_exe,
|
||||
"forgejo_app_ini" : conf_path,
|
||||
"forgejo_work_path" : self.forgejo_work_dir_path,
|
||||
"forgejo_data_path" : self.forgejo_data_dir_path,
|
||||
"forgejo_db_path" : self.forgejo_db_path,
|
||||
"forgejo_repository_root" : self.git_dir_path,
|
||||
"forgejo_lfs_path" : self.lfs_dir_path,
|
||||
"forgejo_log_path" : self.forgejo_log_dir_path,
|
||||
"forgejo_hostname" : self._runcmd("hostname"),
|
||||
"forgejo_lfs_jwt_secret" : self._runcmd([self.forgejo_exe,"generate", "secret", "LFS_JWT_SECRET"]),
|
||||
"forgejo_jwt_secret" : self._runcmd([self.forgejo_exe,"generate", "secret", "JWT_SECRET"]),
|
||||
"forgejo_internal_token" : self._runcmd([self.forgejo_exe,"generate", "secret", "INTERNAL_TOKEN"]),
|
||||
"forgejo_secret_key" : self._runcmd([self.forgejo_exe,"generate", "secret", "SECRET_KEY"])
|
||||
}
|
||||
forgejo_work_dir_path = os.path.join(self.base_path, "var", "lib", "forgejo/work")
|
||||
forgejo_db_dir_path = os.path.join(self.base_path, "var", "lib", "forgejo/db")
|
||||
forgejo_data_dir_path = os.path.join(self.base_path, "var", "lib", "forgejo/data")
|
||||
|
||||
return data
|
||||
forgejo_db_path = os.path.join(forgejo_db_dir_path, "forgejo.db")
|
||||
|
||||
def install_forgejo(self, download=True):
|
||||
self.__logger.info("Installing Forgejo version %s", FORGEJO_VERSION)
|
||||
forgejo_log_dir_path = os.path.join(self.base_path, "log", "forgejo")
|
||||
|
||||
conf_path = os.path.join(self.forgejo_conf_dir_path, "app.ini")
|
||||
|
||||
self.__logger.info("Stopping opengnsys-forgejo service. This may cause a harmless warning.")
|
||||
conf_path = os.path.join(conf_dir_path, "app.ini")
|
||||
|
||||
subprocess.run(["/usr/bin/systemctl", "stop", "opengnsys-forgejo"], check=False)
|
||||
self.__logger.debug("Stopping opengnsys-forgejo service")
|
||||
subprocess.run(["systemctl", "stop", "opengnsys-forgejo"], check=False)
|
||||
|
||||
self.__logger.debug("Downloading from %s into %s", FORGEJO_URL, self.forgejo_exe)
|
||||
pathlib.Path(self.forgejo_bin_path).mkdir(parents=True, exist_ok=True)
|
||||
self.__logger.debug("Downloading from %s into %s", FORGEJO_URL, bin_path)
|
||||
urllib.request.urlretrieve(FORGEJO_URL, bin_path)
|
||||
os.chmod(bin_path, 0o755)
|
||||
|
||||
with open(self.forgejo_exe, "wb") as forgejo_bin:
|
||||
download_with_progress(FORGEJO_URL, forgejo_bin)
|
||||
|
||||
os.chmod(self.forgejo_exe, 0o755)
|
||||
|
||||
if os.path.exists(self.forgejo_db_path):
|
||||
if os.path.exists(forgejo_db_path):
|
||||
self.__logger.debug("Removing old configuration")
|
||||
os.unlink(self.forgejo_db_path)
|
||||
os.unlink(forgejo_db_path)
|
||||
else:
|
||||
self.__logger.debug("Old configuration not present, ok.")
|
||||
|
||||
self.__logger.debug("Wiping old data")
|
||||
for dir in [self.forgejo_conf_dir_path, self.git_dir_path, self.lfs_dir_path, self.forgejo_work_dir_path, self.forgejo_data_dir_path, self.forgejo_db_dir_path]:
|
||||
for dir in [conf_dir_path, git_dir_path, lfs_dir_path, forgejo_work_dir_path, forgejo_data_dir_path, forgejo_db_dir_path]:
|
||||
if os.path.exists(dir):
|
||||
self.__logger.debug("Removing %s", dir)
|
||||
shutil.rmtree(dir)
|
||||
|
||||
self.__logger.debug("Creating directories")
|
||||
|
||||
pathlib.Path(self.opengnsys_etc_path).mkdir(parents=True, exist_ok=True)
|
||||
pathlib.Path(self.forgejo_conf_dir_path).mkdir(parents=True, exist_ok=True)
|
||||
pathlib.Path(self.git_dir_path).mkdir(parents=True, exist_ok=True)
|
||||
pathlib.Path(self.lfs_dir_path).mkdir(parents=True, exist_ok=True)
|
||||
pathlib.Path(self.forgejo_work_dir_path).mkdir(parents=True, exist_ok=True)
|
||||
pathlib.Path(self.forgejo_data_dir_path).mkdir(parents=True, exist_ok=True)
|
||||
pathlib.Path(self.forgejo_db_dir_path).mkdir(parents=True, exist_ok=True)
|
||||
pathlib.Path(self.forgejo_log_dir_path).mkdir(parents=True, exist_ok=True)
|
||||
pathlib.Path(conf_dir_path).mkdir(parents=True, exist_ok=True)
|
||||
pathlib.Path(git_dir_path).mkdir(parents=True, exist_ok=True)
|
||||
pathlib.Path(lfs_dir_path).mkdir(parents=True, exist_ok=True)
|
||||
pathlib.Path(forgejo_work_dir_path).mkdir(parents=True, exist_ok=True)
|
||||
pathlib.Path(forgejo_data_dir_path).mkdir(parents=True, exist_ok=True)
|
||||
pathlib.Path(forgejo_db_dir_path).mkdir(parents=True, exist_ok=True)
|
||||
pathlib.Path(forgejo_log_dir_path).mkdir(parents=True, exist_ok=True)
|
||||
|
||||
|
||||
os.chown(self.lfs_dir_path, self.ssh_uid, self.ssh_gid)
|
||||
os.chown(self.git_dir_path, self.ssh_uid, self.ssh_gid)
|
||||
os.chown(self.forgejo_data_dir_path, self.ssh_uid, self.ssh_gid)
|
||||
os.chown(self.forgejo_work_dir_path, self.ssh_uid, self.ssh_gid)
|
||||
os.chown(self.forgejo_db_dir_path, self.ssh_uid, self.ssh_gid)
|
||||
os.chown(self.forgejo_log_dir_path, self.ssh_uid, self.ssh_gid)
|
||||
os.chown(lfs_dir_path, self.ssh_uid, self.ssh_gid)
|
||||
os.chown(git_dir_path, self.ssh_uid, self.ssh_gid)
|
||||
os.chown(forgejo_data_dir_path, self.ssh_uid, self.ssh_gid)
|
||||
os.chown(forgejo_work_dir_path, self.ssh_uid, self.ssh_gid)
|
||||
os.chown(forgejo_db_dir_path, self.ssh_uid, self.ssh_gid)
|
||||
os.chown(forgejo_log_dir_path, self.ssh_uid, self.ssh_gid)
|
||||
|
||||
data = self._get_forgejo_data()
|
||||
data = {
|
||||
"forgejo_user" : self.ssh_user,
|
||||
"forgejo_group" : self.ssh_group,
|
||||
"forgejo_port" : str(self.forgejo_port),
|
||||
"forgejo_bin" : bin_path,
|
||||
"forgejo_app_ini" : conf_path,
|
||||
"forgejo_work_path" : forgejo_work_dir_path,
|
||||
"forgejo_data_path" : forgejo_data_dir_path,
|
||||
"forgejo_db_path" : forgejo_db_path,
|
||||
"forgejo_repository_root" : git_dir_path,
|
||||
"forgejo_lfs_path" : lfs_dir_path,
|
||||
"forgejo_log_path" : forgejo_log_dir_path,
|
||||
"forgejo_hostname" : self._runcmd("hostname"),
|
||||
"forgejo_lfs_jwt_secret" : self._runcmd([bin_path,"generate", "secret", "LFS_JWT_SECRET"]),
|
||||
"forgejo_jwt_secret" : self._runcmd([bin_path,"generate", "secret", "JWT_SECRET"]),
|
||||
"forgejo_internal_token" : self._runcmd([bin_path,"generate", "secret", "INTERNAL_TOKEN"]),
|
||||
"forgejo_secret_key" : self._runcmd([bin_path,"generate", "secret", "SECRET_KEY"])
|
||||
}
|
||||
|
||||
self._install_template(os.path.join(self.template_path, "forgejo-app.ini"), conf_path, data)
|
||||
self._install_template(os.path.join(self.template_path, "opengnsys-forgejo.service"), "/etc/systemd/system/opengnsys-forgejo.service", data)
|
||||
self._install_template(os.path.join(self.script_path, "forgejo-app.ini"), conf_path, data)
|
||||
self._install_template(os.path.join(self.script_path, "forgejo.service"), "/etc/systemd/system/opengnsys-forgejo.service", data)
|
||||
|
||||
|
||||
self.__logger.debug("Reloading systemd and starting service")
|
||||
|
@ -876,7 +694,7 @@ class OpengnsysGitInstaller:
|
|||
self.__logger.info("Configuring forgejo")
|
||||
|
||||
def run_forge_cmd(args):
|
||||
cmd = [self.forgejo_exe, "--config", conf_path] + args
|
||||
cmd = [bin_path, "--config", conf_path] + args
|
||||
self.__logger.debug("Running command: %s", cmd)
|
||||
|
||||
ret = subprocess.run(cmd, check=False, capture_output=True, encoding='utf-8', user=self.ssh_user)
|
||||
|
@ -897,80 +715,10 @@ class OpengnsysGitInstaller:
|
|||
with open(os.path.join(self.base_path, "etc", "ogGitApiToken.cfg"), "w+", encoding='utf-8') as token_file:
|
||||
token_file.write(token)
|
||||
|
||||
def configure_forgejo(self):
|
||||
data = self._get_forgejo_data()
|
||||
self.__logger.debug("Creating directories")
|
||||
|
||||
pathlib.Path(self.opengnsys_etc_path).mkdir(parents=True, exist_ok=True)
|
||||
pathlib.Path(self.forgejo_conf_dir_path).mkdir(parents=True, exist_ok=True)
|
||||
pathlib.Path(self.git_dir_path).mkdir(parents=True, exist_ok=True)
|
||||
pathlib.Path(self.lfs_dir_path).mkdir(parents=True, exist_ok=True)
|
||||
pathlib.Path(self.forgejo_work_dir_path).mkdir(parents=True, exist_ok=True)
|
||||
pathlib.Path(self.forgejo_work_custom_dir_path).mkdir(parents=True, exist_ok=True)
|
||||
pathlib.Path(self.forgejo_data_dir_path).mkdir(parents=True, exist_ok=True)
|
||||
pathlib.Path(self.forgejo_db_dir_path).mkdir(parents=True, exist_ok=True)
|
||||
pathlib.Path(self.forgejo_log_dir_path).mkdir(parents=True, exist_ok=True)
|
||||
ssh_key = self._extract_ssh_key_from_initrd()
|
||||
|
||||
|
||||
os.chown(self.lfs_dir_path, self.ssh_uid, self.ssh_gid)
|
||||
os.chown(self.git_dir_path, self.ssh_uid, self.ssh_gid)
|
||||
os.chown(self.forgejo_data_dir_path, self.ssh_uid, self.ssh_gid)
|
||||
os.chown(self.forgejo_work_dir_path, self.ssh_uid, self.ssh_gid)
|
||||
os.chown(self.forgejo_db_dir_path, self.ssh_uid, self.ssh_gid)
|
||||
os.chown(self.forgejo_log_dir_path, self.ssh_uid, self.ssh_gid)
|
||||
|
||||
|
||||
|
||||
conf_path = os.path.join(self.forgejo_conf_dir_path, "app.ini")
|
||||
self._install_template(os.path.join(self.template_path, "forgejo-app.ini"), conf_path, data)
|
||||
self._install_template(os.path.join(self.template_path, "opengnsys-forgejo.service"), "/etc/systemd/system/opengnsys-forgejo.service", data)
|
||||
|
||||
|
||||
|
||||
self.__logger.debug("Reloading systemd and starting service")
|
||||
subprocess.run(["systemctl", "daemon-reload"], check=True)
|
||||
subprocess.run(["systemctl", "enable", "opengnsys-forgejo"], check=True)
|
||||
|
||||
subprocess.run(["systemctl", "restart", "opengnsys-forgejo"], check=True)
|
||||
|
||||
self.__logger.info("Waiting for forgejo to start")
|
||||
self._wait_for_port("localhost", self.forgejo_port)
|
||||
|
||||
|
||||
self.__logger.info("Configuring forgejo")
|
||||
|
||||
def run_forge_cmd(args, ignore_errors = []):
|
||||
cmd = [self.forgejo_exe, "--config", conf_path] + args
|
||||
self.__logger.info("Running command: %s", cmd)
|
||||
|
||||
ret = subprocess.run(cmd, check=False, capture_output=True, encoding='utf-8', user=self.ssh_user)
|
||||
if ret.returncode == 0:
|
||||
return ret.stdout.strip()
|
||||
else:
|
||||
self.__logger.error("Failed to run command: %s, return code %i", cmd, ret.returncode)
|
||||
self.__logger.error("stdout: %s", ret.stdout.strip())
|
||||
self.__logger.error("stderr: %s", ret.stderr.strip())
|
||||
|
||||
for err in ignore_errors:
|
||||
if err in ret.stderr:
|
||||
self.__logger.info("Ignoring error, it's in the ignore list")
|
||||
return ret.stdout.strip()
|
||||
|
||||
raise RuntimeError("Failed to run necessary command")
|
||||
|
||||
run_forge_cmd(["migrate"])
|
||||
|
||||
run_forge_cmd(["admin", "doctor", "check"])
|
||||
|
||||
run_forge_cmd(["admin", "user", "create", "--username", self.forgejo_user, "--password", self.forgejo_password, "--email", self.email], ignore_errors=["user already exists"])
|
||||
|
||||
token = run_forge_cmd(["admin", "user", "generate-access-token", "--username", self.forgejo_user, "-t", "gitapi", "--scopes", "all", "--raw"], ignore_errors = ["access token name has been used already"])
|
||||
|
||||
if token:
|
||||
with open(os.path.join(self.base_path, "etc", "ogGitApiToken.cfg"), "w+", encoding='utf-8') as token_file:
|
||||
token_file.write(token)
|
||||
else:
|
||||
self.__logger.info("Keeping the old token")
|
||||
self.add_forgejo_sshkey(ssh_key, "Default key")
|
||||
|
||||
|
||||
def add_forgejo_repo(self, repository_name, description = ""):
|
||||
|
@ -1016,7 +764,6 @@ class OpengnsysGitInstaller:
|
|||
)
|
||||
|
||||
self.__logger.info("Request status was %i, content %s", r.status_code, r.content)
|
||||
return r.status_code, r.content.decode('utf-8')
|
||||
|
||||
def add_forgejo_organization(self, pubkey, description = ""):
|
||||
token = ""
|
||||
|
@ -1052,7 +799,8 @@ if __name__ == '__main__':
|
|||
streamLog = logging.StreamHandler()
|
||||
streamLog.setLevel(logging.INFO)
|
||||
|
||||
pathlib.Path(opengnsys_log_dir).mkdir(parents=True, exist_ok=True)
|
||||
if not os.path.exists(opengnsys_log_dir):
|
||||
os.mkdir(opengnsys_log_dir)
|
||||
|
||||
logFilePath = f"{opengnsys_log_dir}/git_installer.log"
|
||||
fileLog = logging.FileHandler(logFilePath)
|
||||
|
@ -1067,16 +815,6 @@ if __name__ == '__main__':
|
|||
logger.addHandler(fileLog)
|
||||
|
||||
|
||||
if "postinst" in os.path.basename(__file__):
|
||||
logger.info("Running as post-install script")
|
||||
installer=OpengnsysGitInstaller()
|
||||
|
||||
# Templates get installed here
|
||||
installer.template_path = "/usr/share/opengnsys-forgejo/"
|
||||
installer.configure_forgejo()
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
prog="OpenGnsys Installer",
|
||||
description="Script para la instalación del repositorio git",
|
||||
|
@ -1086,23 +824,15 @@ if __name__ == '__main__':
|
|||
|
||||
parser.add_argument('--testmode', action='store_true', help="Modo de prueba")
|
||||
parser.add_argument('--ignoresshkey', action='store_true', help="Ignorar clave de SSH")
|
||||
parser.add_argument('--use-ssh-key', metavar="FILE", type=str, help="Add the SSH key from the specified file")
|
||||
parser.add_argument('--usesshkey', type=str, help="Usar clave SSH especificada")
|
||||
parser.add_argument('--test-createuser', action='store_true')
|
||||
parser.add_argument('--extract-ssh-key', action='store_true', help="Extract SSH key from oglive squashfs")
|
||||
parser.add_argument('--set-ssh-key', action='store_true', help="Read SSH key from oglive squashfs and set it in Forgejo")
|
||||
|
||||
parser.add_argument('--extract-ssh-key-from-initrd', action='store_true', help="Extract SSH key from oglive initrd (obsolete)")
|
||||
|
||||
parser.add_argument('--initrd-file', metavar="FILE", help="Initrd file to extract SSH key from")
|
||||
parser.add_argument('--squashfs-file', metavar="FILE", help="Squashfs file to extract SSH key from")
|
||||
parser.add_argument('--oglive-file', metavar="FILE", help="Oglive file (ISO) to extract SSH key from")
|
||||
parser.add_argument('--oglive-url', metavar="URL", help="URL to oglive file (ISO) to extract SSH key from")
|
||||
|
||||
|
||||
parser.add_argument('--set-ssh-key-in-initrd', action='store_true', help="Configure SSH key in oglive (obsolete)")
|
||||
parser.add_argument('--oglive', type=int, metavar='NUM', help = "Do SSH key manipulation on this oglive")
|
||||
parser.add_argument('--quiet', action='store_true', help="Quiet console output")
|
||||
parser.add_argument('--get-image-paths', action='store_true', help="Get paths to image files")
|
||||
parser.add_argument("-v", "--verbose", action="store_true", help = "Verbose console output")
|
||||
|
||||
|
||||
|
@ -1118,6 +848,7 @@ if __name__ == '__main__':
|
|||
installer = OpengnsysGitInstaller()
|
||||
installer.set_testmode(args.testmode)
|
||||
installer.set_ignoresshkey(args.ignoresshkey)
|
||||
installer.set_usesshkey(args.usesshkey)
|
||||
|
||||
logger.debug("Inicio de instalación")
|
||||
|
||||
|
@ -1129,40 +860,25 @@ if __name__ == '__main__':
|
|||
elif args.test_createuser:
|
||||
installer.set_ssh_user_group("oggit2", "oggit2")
|
||||
elif args.extract_ssh_key:
|
||||
keys = installer.extract_ssh_keys_from_squashfs(oglive_num = args.oglive)
|
||||
keys = installer.extract_ssh_keys(oglive_num = args.oglive)
|
||||
print(f"{keys}")
|
||||
elif args.extract_ssh_key_from_initrd:
|
||||
key = installer.extract_ssh_key_from_initrd(oglive_number = args.oglive, initrd_file = args.initrd_file)
|
||||
key = installer._extract_ssh_key_from_initrd()
|
||||
print(f"{key}")
|
||||
elif args.set_ssh_key:
|
||||
installer.add_ssh_key_from_squashfs(oglive_num=args.oglive, squashfs_file=args.squashfs_file, oglive_file = args.oglive_file or args.oglive_url)
|
||||
elif args.use_ssh_key:
|
||||
with open(args.use_ssh_key, 'r', encoding='utf-8') as ssh_key_file:
|
||||
ssh_key_data = ssh_key_file.read().strip()
|
||||
(keytype, keydata, description) = ssh_key_data.split(" ", 2)
|
||||
|
||||
installer.add_forgejo_sshkey(f"{keytype} {keydata}", description)
|
||||
|
||||
installer.add_ssh_key_from_squashfs(oglive_num=args.oglive)
|
||||
elif args.set_ssh_key_in_initrd:
|
||||
installer.set_ssh_key_in_initrd()
|
||||
elif args.get_image_paths:
|
||||
installer.get_image_paths(oglive_num = args.oglive)
|
||||
else:
|
||||
installer.install_dependencies()
|
||||
installer.install_api()
|
||||
installer.install()
|
||||
installer.install_forgejo()
|
||||
|
||||
installer.add_forgejo_repo("windows", "Windows")
|
||||
installer.add_forgejo_repo("linux", "Linux")
|
||||
installer.add_forgejo_repo("mac", "Mac")
|
||||
|
||||
installer.add_ssh_key_from_squashfs(oglive_num = args.oglive, squashfs_file=args.squashfs_file, oglive_file = args.oglive_file or args.oglive_url)
|
||||
|
||||
except RequirementException as req:
|
||||
show_error(f"Requisito para la instalación no satisfecho: {req.message}")
|
||||
exit(1)
|
||||
except OptionalDependencyException as optreq:
|
||||
show_error(optreq.message)
|
||||
exit(1)
|
||||
|
||||
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
|
||||
|
||||
sys.path.insert(0, "/opt/oglive/rootfs/opt/opengnsys/lib/python3/")
|
||||
sys.path.insert(0, "/opt/opengnsys/interfaceAdm/git/")
|
||||
sys.path.insert(0, "/opt/opengnsys/ogrepository/oggit/lib/")
|
||||
|
||||
import NetLib
|
||||
import ogGlobals
|
||||
import SystemLib
|
||||
|
||||
|
||||
from gitlib import OpengnsysGitLibrary, NTFSImplementation
|
||||
|
||||
|
||||
def create_image(disk_num, partition_num, repo, image_name):
|
||||
|
||||
ntfs_impl = NTFSImplementation.NTFS3G
|
||||
og_git = OpengnsysGitLibrary(ntfs_implementation = ntfs_impl)
|
||||
device = og_git._runBashFunction("ogDiskToDev", [str(disk_num), str(partition_num)])
|
||||
og_git.initRepo(device, image_name)
|
||||
|
||||
def main():
|
||||
if len(sys.argv) != 6:
|
||||
sys.exit(SystemLib.ogRaiseError(OG_ERR_FORMAT, "Incorrect number of arguments"))
|
||||
|
||||
disk_num, partition_num, image_name, repo, tag = sys.argv[1:6]
|
||||
|
||||
retval = create_image(disk_num, partition_num, repo, image_name)
|
||||
|
||||
sys.exit(retval)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
|
@ -18,8 +18,5 @@ override_dh_gencontrol:
|
|||
override_dh_installdocs:
|
||||
# Nothing, we don't want docs
|
||||
|
||||
override_dh_auto_test:
|
||||
# Nothing
|
||||
#
|
||||
override_dh_installchangelogs:
|
||||
# Nothing, we don't want the changelog
|
||||
|
|
Binary file not shown.
|
@ -1,220 +0,0 @@
|
|||
opengnsys-forgejo (0.5.1dev1) UNRELEASED; urgency=medium
|
||||
|
||||
[ OpenGnsys ]
|
||||
* Fix config path
|
||||
*
|
||||
|
||||
[ Vadim Troshchinskiy ]
|
||||
* First commit
|
||||
* Add installer
|
||||
* Add requirements file
|
||||
|
||||
[ lgromero ]
|
||||
* refs #734 Creates first skeleton of symfony+swagger project
|
||||
|
||||
[ Vadim Troshchinskiy ]
|
||||
* Add Gitlib
|
||||
|
||||
[ lgromero ]
|
||||
* refs #734 Changes OgBootBundle name and adds a first endpoint to test
|
||||
* refs #734 Adds template of repository and branch endpoints
|
||||
|
||||
[ Vadim Troshchinskiy ]
|
||||
* Update docs to account for changes
|
||||
* Trivial API server
|
||||
* Ticket #753: Add repository listing
|
||||
* Ticket #735: List branches in repo
|
||||
* Add testing instructions
|
||||
* Agregar manejo de errrores
|
||||
* Ticket #741: Crear repo Ticket #736: Eliminar repo
|
||||
|
||||
[ lgromero ]
|
||||
* refs #734 Adds README for Api installation
|
||||
* refs #734 Control of errores and http codes in controler
|
||||
* refs #734 Renemas oggitservice
|
||||
|
||||
[ Vadim Troshchinskiy ]
|
||||
* Ticket #738, ticket #739: repo and sync backup protoype
|
||||
|
||||
[ lgromero ]
|
||||
* refs #734 Adds new endpoints sync and backup and status endpoint
|
||||
* refs #734 Adds nelmio api doc configuration
|
||||
* Adds .env file to root
|
||||
* refs #734 use environment variables in .env files and disable web depuration toolbar
|
||||
* refs #734 fix typo in .env and use oggit_url environment variable
|
||||
|
||||
[ Vadim Troshchinskiy ]
|
||||
* Ticket #738, ticket #739: git sync and backup
|
||||
|
||||
[ Nicolas Arenas ]
|
||||
* Add docker container files
|
||||
|
||||
[ Vadim Troshchinskiy ]
|
||||
* Ticket #737: GC
|
||||
* Use Paramiko and Gitpython for backups
|
||||
|
||||
[ Nicolas Arenas ]
|
||||
* Add mock api for testing dockerfile
|
||||
|
||||
[ Vadim Troshchinskiy ]
|
||||
* Ticket #740, listen on all hosts
|
||||
|
||||
[ lgromero ]
|
||||
* refs #734 Removes innecesaries parameters and changes php platform to 8.2
|
||||
* refs #734 just changes name and description in swagger web page
|
||||
|
||||
[ Vadim Troshchinskiy ]
|
||||
* Remove duplicated import
|
||||
* Documentation prototype
|
||||
* Update to 24.04, solves deployment issue
|
||||
* Add more documentation
|
||||
* Add API README
|
||||
* Add API examples
|
||||
* Update list of package requirements in oglive
|
||||
* Fix commandline parsing bug
|
||||
* Revert experimental Windows change
|
||||
* Fix ticket #770: Re-parse filesystems list after mounting
|
||||
* Use oglive server if ogrepository is not set
|
||||
* Ticket #770: Add sanity check
|
||||
* Ticket #771: Correctly create directories on metadata restoration
|
||||
* Ticket #780: Unmount before clone if needed
|
||||
* Fix ticket #800: sudo doesn't work
|
||||
|
||||
[ Vadim Trochinsky ]
|
||||
* Fix ticket #802: .git directory in filesystem root
|
||||
|
||||
[ Vadim Troshchinskiy ]
|
||||
* Fix ticket #805: Remove .git directory if it already exists when checking out
|
||||
* Ticket #770: Correctly update metadata when mounting and unmounting
|
||||
* Ticket #804: Move log
|
||||
* Fix ticket #902: .git directories can't be checked out
|
||||
* Lint fixes
|
||||
* Remove unused code
|
||||
* Lint fixes
|
||||
* Lint fixes
|
||||
* Lint fixes
|
||||
* Additional logging message
|
||||
* Lint fix
|
||||
* Fix ticket #907: mknod fails due to path not found
|
||||
* Initial implementation for commit, push, fetch.
|
||||
* Don't fail on empty lines in metadata, just skip them
|
||||
* Add documentation and functionality to progress hook (not used yet)
|
||||
* Pylint fixes
|
||||
* Ticket #908: Remove some unneeded warnings
|
||||
* Fix progress report
|
||||
* Ticket #906: Fix permissions on directories
|
||||
* Make pylint happy
|
||||
* Mount fix
|
||||
* Ticket #808: Initial implementation
|
||||
* Initial forgejo install
|
||||
* Deduplicate key extraction
|
||||
* Fix installer bugs and add documentation
|
||||
* Change user to oggit
|
||||
* Fix NTFS ID modification implementation
|
||||
* Implement system-specific EFI data support
|
||||
* Fix encoding when reading system uuid
|
||||
* Fix and refactor slightly EFI implementation
|
||||
* Add Windows BCD decoding tool
|
||||
* Check module loading and unloading, modprobe works on oglive now
|
||||
* Make EFI deployment more flexible
|
||||
* Add organization API call
|
||||
* Fix bash library path
|
||||
* Fix repo paths for forgejo
|
||||
* Update documentation
|
||||
* Sync to ensure everything is written
|
||||
* Refactoring and more pydoc
|
||||
* Add more documentation
|
||||
* Improve installer documentation
|
||||
* Improve gitlib instructions
|
||||
* Add missing files
|
||||
* Partial setsshkey implementation
|
||||
* Fix SSH key generation and extraction
|
||||
* Initial package contents
|
||||
* Add Debian packaging
|
||||
* Add pylkid
|
||||
* Add pyblkid debian files
|
||||
* Use packaged pyblkid
|
||||
* More detailed API logging
|
||||
* Improve logging
|
||||
* Add oglive key to forgejo
|
||||
* Add original source
|
||||
* Always re-download forgejo, even if installed.
|
||||
* Remove obsolete code that stopped being relevant with Forgejo
|
||||
* Move python modules to /opt/opengnsys-modules
|
||||
* Use absolute paths in initrd modification
|
||||
* Add timestamp to ssh key title, forgejo doesn't like duplicates
|
||||
* Skip past symlinks and problems in oglive modification
|
||||
* Get keys from squashfs instead of initrd to work with current oglive packaging
|
||||
* Fix trivial bug
|
||||
* Move modules to /usr/share/opengnsys
|
||||
* Move packages to /usr/share
|
||||
|
||||
[ Angel Rodriguez ]
|
||||
* Add gitlib/README-en.md
|
||||
* Add api/README-en.md
|
||||
* Add installer/README-en.md
|
||||
|
||||
[ Vadim Troshchinskiy ]
|
||||
* Skip NTFS code on non-Windows
|
||||
* Store and restore GPT partition UUIDs
|
||||
* Update READMEs
|
||||
* BCD constants
|
||||
* Use tqdm
|
||||
* Constants
|
||||
* Add extra mounts update
|
||||
* Better status reports
|
||||
* Make log filename machine-dependent Move kernel args parsing
|
||||
* Make unmounting more robust
|
||||
* Improve repository initialization
|
||||
* Make --pull work like the other commands
|
||||
* Add packages
|
||||
* Update documentation
|
||||
* Ignore python cache
|
||||
* Ignore more files
|
||||
* add python libarchive-c original package
|
||||
* Add pyblkid copyright file
|
||||
* Add make_orig script
|
||||
* Reorder and fix for ogrepository reorganization
|
||||
* Restructure git installer to work without ogboot on the same machine, update docs
|
||||
* Update english documentation
|
||||
* Improve installation process, make it possible to extract keys from oglive
|
||||
* Fix namespaces
|
||||
* Fix ogrepository paths
|
||||
* Change git repo path
|
||||
* Improvements for logging and error handling
|
||||
* Fix HTTP exception handling
|
||||
* Improve task management, cleanup when there are too many
|
||||
* More error logging
|
||||
* Mark git repo as a safe directory
|
||||
* Rework the ability to use a custom SSH key
|
||||
* Log every request
|
||||
* Branch deletion
|
||||
* Make branch deletion RESTful
|
||||
* Initial version of the API server
|
||||
* Add original repo_api
|
||||
* Convert to blueprint
|
||||
* Add port argument
|
||||
* Fix error handling
|
||||
* Add README
|
||||
* Load swagger from disk
|
||||
* Fix repository URL
|
||||
* Bump forgejo version
|
||||
* Add helpful script
|
||||
* Fix port argument
|
||||
* Refactoring for package support
|
||||
* Remove old code
|
||||
* Refactoring for packaging
|
||||
* opengnsys-forgejo package
|
||||
* Fix post-install for forgejo deployment
|
||||
* Fixes for running under gunicorn
|
||||
* Debian packaging
|
||||
* Add branches and tags creation endpoints
|
||||
* Add missing file
|
||||
* Rename service
|
||||
* Add templates
|
||||
* Disable tests
|
||||
* Fix permission problem
|
||||
* Fix ini path
|
||||
* Update changelog
|
||||
|
||||
-- OpenGnsys <opengnsys@opengnsys.com> Thu, 05 Jun 2025 21:46:30 +0000
|
|
@ -1,37 +0,0 @@
|
|||
Source: opengnsys-forgejo
|
||||
Section: unknown
|
||||
Priority: optional
|
||||
Maintainer: OpenGnsys <opengnsys@opengnsys.es>
|
||||
Rules-Requires-Root: no
|
||||
Build-Depends:
|
||||
debhelper-compat (= 13),
|
||||
Standards-Version: 4.6.2
|
||||
Homepage: https://opengnsys.es
|
||||
#Vcs-Browser: https://salsa.debian.org/debian/ogboot
|
||||
#Vcs-Git: https://salsa.debian.org/debian/ogboot.git
|
||||
|
||||
Package: opengnsys-forgejo
|
||||
Architecture: any
|
||||
Multi-Arch: foreign
|
||||
Depends:
|
||||
${shlibs:Depends},
|
||||
${misc:Depends},
|
||||
bsdextrautils,
|
||||
debconf (>= 1.5.0),
|
||||
gunicorn,
|
||||
opengnsys-flask-executor,
|
||||
opengnsys-flask-restx,
|
||||
opengnsys-libarchive-c,
|
||||
python3,
|
||||
python3-aniso8601,
|
||||
python3-flasgger,
|
||||
python3-flask,
|
||||
python3-flask,
|
||||
python3-git,
|
||||
python3-paramiko,
|
||||
python3-requests,
|
||||
python3-termcolor,
|
||||
python3-tqdm
|
||||
Conflicts:
|
||||
Description: Opengnsys Forgejo package for OgGit
|
||||
Files for OpenGnsys Git support
|
|
@ -1,43 +0,0 @@
|
|||
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||
Source: <url://example.com>
|
||||
Upstream-Name: ogboot
|
||||
Upstream-Contact: <preferred name and address to reach the upstream project>
|
||||
|
||||
Files:
|
||||
*
|
||||
Copyright:
|
||||
<years> <put author's name and email here>
|
||||
<years> <likewise for another author>
|
||||
License: GPL-3.0+
|
||||
|
||||
Files:
|
||||
debian/*
|
||||
Copyright:
|
||||
2025 vagrant <vagrant@build>
|
||||
License: GPL-3.0+
|
||||
|
||||
License: GPL-3.0+
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
.
|
||||
This package is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
Comment:
|
||||
On Debian systems, the complete text of the GNU General
|
||||
Public License version 3 can be found in "/usr/share/common-licenses/GPL-3".
|
||||
|
||||
# Please also look if there are files or directories which have a
|
||||
# different copyright/license attached and list them here.
|
||||
# Please avoid picking licenses with terms that are more restrictive than the
|
||||
# packaged work, as it may make Debian's contributions unacceptable upstream.
|
||||
#
|
||||
# If you need, there are some extra license texts available in two places:
|
||||
# /usr/share/debhelper/dh_make/licenses/
|
||||
# /usr/share/common-licenses/
|
|
@ -1,2 +0,0 @@
|
|||
opengnsys-forgejo_0.5_amd64.buildinfo unknown optional
|
||||
opengnsys-forgejo_0.5_amd64.deb unknown optional
|
|
@ -1,2 +0,0 @@
|
|||
/opt/opengnsys/oggit/bin
|
||||
/opt/opengnsys/ogrepository/etc/forgejo/
|
|
@ -1,3 +0,0 @@
|
|||
forgejo /opt/opengnsys/ogrepository/bin
|
||||
forgejo-app.ini /usr/share/opengnsys-forgejo/
|
||||
opengnsys-forgejo.service /usr/share/opengnsys-forgejo/
|
|
@ -1,2 +0,0 @@
|
|||
misc:Depends=
|
||||
misc:Pre-Depends=
|
|
@ -1,25 +0,0 @@
|
|||
Template: opengnsys/forgejo_organization
|
||||
Type: string
|
||||
Default: opegnsys
|
||||
Description: Organizacion de Forgejo
|
||||
|
||||
Template: opengnsys/forgejo_user
|
||||
Type: string
|
||||
Default: oggit
|
||||
Description: Usuario de oggit Forgejo
|
||||
|
||||
Template: opengnsys/forgejo_password
|
||||
Type: password
|
||||
Default: opegnsys
|
||||
Description: Password de cuenta de oggit de Forgejo
|
||||
|
||||
Template: opengnsys/forgejo_email
|
||||
Type: string
|
||||
Default: opegnsys@opengnsys.com
|
||||
Description: Email de cuenta de oggit de Forgejo
|
||||
|
||||
Template: opengnsys/forgejo_port
|
||||
Type: string
|
||||
Default: 3100
|
||||
Description: Puerto TCP de Forgejo
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
data = {
|
||||
"forgejo_user" : self.ssh_user,
|
||||
"forgejo_group" : self.ssh_group,
|
||||
"forgejo_port" : str(self.forgejo_port),
|
||||
"forgejo_bin" : bin_path,
|
||||
"forgejo_app_ini" : conf_path,
|
||||
"forgejo_work_path" : forgejo_work_dir_path,
|
||||
"forgejo_data_path" : forgejo_data_dir_path,
|
||||
"forgejo_db_path" : forgejo_db_path,
|
||||
"forgejo_repository_root" : git_dir_path,
|
||||
"forgejo_lfs_path" : lfs_dir_path,
|
||||
"forgejo_log_path" : forgejo_log_dir_path,
|
||||
"forgejo_hostname" : _runcmd("hostname"),
|
||||
"forgejo_lfs_jwt_secret" : _runcmd([bin_path,"generate", "secret", "LFS_JWT_SECRET"]),
|
||||
"forgejo_jwt_secret" : _runcmd([bin_path,"generate", "secret", "JWT_SECRET"]),
|
||||
"forgejo_internal_token" : _runcmd([bin_path,"generate", "secret", "INTERNAL_TOKEN"]),
|
||||
"forgejo_secret_key" : _runcmd([bin_path,"generate", "secret", "SECRET_KEY"])
|
||||
}
|
||||
|
||||
ini_template = "/usr/share/opengnsys-forgejo/forgejo-app.ini"
|
||||
|
||||
|
||||
def _install_template(self, template, destination, keysvalues):
|
||||
data = ""
|
||||
with open(template, "r", encoding="utf-8") as template_file:
|
||||
data = template_file.read()
|
||||
|
||||
for key in keysvalues.keys():
|
||||
if isinstance(keysvalues[key], int):
|
||||
data = data.replace("{" + key + "}", str(keysvalues[key]))
|
||||
else:
|
||||
data = data.replace("{" + key + "}", keysvalues[key])
|
||||
|
||||
with open(destination, "w+", encoding="utf-8") as out_file:
|
||||
out_file.write(data)
|
||||
|
||||
|
||||
|
||||
|
||||
_install_template(os.path.join(self.script_path, "forgejo-app.ini"), conf_path, data)
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
#!/usr/bin/make -f
|
||||
|
||||
# See debhelper(7) (uncomment to enable).
|
||||
# Output every command that modifies files on the build system.
|
||||
#export DH_VERBOSE = 1
|
||||
|
||||
|
||||
# See FEATURE AREAS in dpkg-buildflags(1).
|
||||
#export DEB_BUILD_MAINT_OPTIONS = hardening=+all
|
||||
|
||||
# See ENVIRONMENT in dpkg-buildflags(1).
|
||||
# Package maintainers to append CFLAGS.
|
||||
#export DEB_CFLAGS_MAINT_APPEND = -Wall -pedantic
|
||||
# Package maintainers to append LDFLAGS.
|
||||
#export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed
|
||||
|
||||
|
||||
%:
|
||||
dh $@
|
||||
|
||||
|
||||
%:
|
||||
dh $@
|
||||
|
||||
# Ejecutar composer install durante la fase de construcción
|
||||
override_dh_auto_build:
|
||||
cp -v ../../installer/opengnsys_git_installer.py debian/opengnsys-forgejo.postinst
|
||||
|
||||
override_dh_auto_install:
|
||||
dh_auto_install
|
||||
mkdir -p debian/opengnsys-forgejo/opt/opengnsys/ogrepository/var/lib/forgejo
|
||||
mkdir -p debian/opengnsys-forgejo/opt/opengnsys/ogrepository/var/lib/forgejo/work
|
||||
# fails under fakeroot for some reason, fix in postinst
|
||||
# chown -R oggit:oggit debian/opengnsys-forgejo/opt/opengnsys/ogrepository/var/lib/forgejo
|
||||
|
||||
|
||||
# dh_make generated override targets.
|
||||
# This is an example for Cmake (see <https://bugs.debian.org/641051>).
|
||||
#override_dh_auto_configure:
|
||||
# dh_auto_configure -- \
|
||||
# -DCMAKE_LIBRARY_PATH=$(DEB_HOST_MULTIARCH)
|
|
@ -1,6 +0,0 @@
|
|||
#!/bin/bash
|
||||
VERSION=10.0.1
|
||||
|
||||
wget https://codeberg.org/forgejo/forgejo/releases/download/v${VERSION}/forgejo-${VERSION}-linux-amd64 -O forgejo
|
||||
chmod 755 forgejo
|
||||
|
|
@ -1,78 +0,0 @@
|
|||
APP_NAME = OpenGnsys Git
|
||||
APP_SLOGAN =
|
||||
RUN_USER = {forgejo_user}
|
||||
WORK_PATH = {forgejo_work_path}
|
||||
RUN_MODE = prod
|
||||
|
||||
[database]
|
||||
DB_TYPE = sqlite3
|
||||
HOST = 127.0.0.1:3306
|
||||
NAME = forgejo
|
||||
USER = forgejo
|
||||
PASSWD =
|
||||
SCHEMA =
|
||||
SSL_MODE = disable
|
||||
PATH = {forgejo_db_path}
|
||||
LOG_SQL = false
|
||||
|
||||
[repository]
|
||||
ROOT = {forgejo_repository_root}
|
||||
|
||||
[server]
|
||||
SSH_DOMAIN = og-admin
|
||||
DOMAIN = og-admin
|
||||
HTTP_PORT = {forgejo_port}
|
||||
ROOT_URL = http://{forgejo_hostname}:{forgejo_port}/
|
||||
APP_DATA_PATH = {forgejo_data_path}
|
||||
DISABLE_SSH = false
|
||||
SSH_PORT = 22
|
||||
LFS_START_SERVER = true
|
||||
LFS_JWT_SECRET = {forgejo_lfs_jwt_secret}
|
||||
OFFLINE_MODE = true
|
||||
|
||||
[lfs]
|
||||
PATH = {forgejo_lfs_path}
|
||||
|
||||
[mailer]
|
||||
ENABLED = false
|
||||
|
||||
[service]
|
||||
REGISTER_EMAIL_CONFIRM = false
|
||||
ENABLE_NOTIFY_MAIL = false
|
||||
DISABLE_REGISTRATION = true
|
||||
ALLOW_ONLY_EXTERNAL_REGISTRATION = false
|
||||
ENABLE_CAPTCHA = false
|
||||
REQUIRE_SIGNIN_VIEW = false
|
||||
DEFAULT_KEEP_EMAIL_PRIVATE = false
|
||||
DEFAULT_ALLOW_CREATE_ORGANIZATION = true
|
||||
DEFAULT_ENABLE_TIMETRACKING = true
|
||||
NO_REPLY_ADDRESS = noreply.localhost
|
||||
|
||||
[openid]
|
||||
ENABLE_OPENID_SIGNIN = true
|
||||
ENABLE_OPENID_SIGNUP = true
|
||||
|
||||
[cron.update_checker]
|
||||
ENABLED = true
|
||||
|
||||
[session]
|
||||
PROVIDER = file
|
||||
|
||||
[log]
|
||||
MODE = console
|
||||
LEVEL = info
|
||||
ROOT_PATH = {forgejo_log_path} #/tmp/log
|
||||
|
||||
[repository.pull-request]
|
||||
DEFAULT_MERGE_STYLE = merge
|
||||
|
||||
[repository.signing]
|
||||
DEFAULT_TRUST_MODEL = committer
|
||||
|
||||
[security]
|
||||
INSTALL_LOCK = true
|
||||
INTERNAL_TOKEN = {forgejo_internal_token}
|
||||
PASSWORD_HASH_ALGO = pbkdf2_hi
|
||||
|
||||
[oauth2]
|
||||
JWT_SECRET = {forgejo_jwt_secret}
|
|
@ -1,11 +0,0 @@
|
|||
[Service]
|
||||
RestartSec=10s
|
||||
Type=simple
|
||||
User=oggit
|
||||
Group=oggit
|
||||
WorkingDirectory=/opt/opengnsys/ogrepository/var/lib/forgejo/work
|
||||
ExecStart=/opt/opengnsys/ogrepository/bin/forgejo web --config /opt/opengnsys/ogrepository/etc/forgejo/app.ini
|
||||
Restart=always
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
|
@ -1,17 +0,0 @@
|
|||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
git clone https://github.com/vojtechtrefny/pyblkid opengnsys-pyblkid
|
||||
cd opengnsys-pyblkid
|
||||
version=`python3 ./setup.py --version`
|
||||
cd ..
|
||||
|
||||
if [ -d "opengnsys-pyblkid-${version}" ] ; then
|
||||
echo "Directory opengnsys-pyblkid-${version} already exists, won't overwrite"
|
||||
exit 1
|
||||
else
|
||||
rm -rf opengnsys-pyblkid/.git
|
||||
mv opengnsys-pyblkid "opengnsys-pyblkid-${version}"
|
||||
tar -c --xz -v -f "opengnsys-pyblkid_${version}.orig.tar.xz" "opengnsys-pyblkid-${version}"
|
||||
fi
|
||||
|
|
@ -1,208 +0,0 @@
|
|||
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||
Upstream-Name: python-libarchive-c
|
||||
Source: https://github.com/Changaco/python-libarchive-c
|
||||
|
||||
Files: *
|
||||
Copyright: 2014-2018 Changaco <changaco@changaco.oy.lc>
|
||||
License: CC-0
|
||||
|
||||
Files: tests/surrogateescape.py
|
||||
Copyright: 2015 Changaco <changaco@changaco.oy.lc>
|
||||
2011-2013 Victor Stinner <victor.stinner@gmail.com>
|
||||
License: BSD-2-clause or PSF-2
|
||||
|
||||
Files: debian/*
|
||||
Copyright: 2015 Jerémy Bobbio <lunar@debian.org>
|
||||
2019 Mattia Rizzolo <mattia@debian.org>
|
||||
License: permissive
|
||||
Copying and distribution of this package, with or without
|
||||
modification, are permitted in any medium without royalty
|
||||
provided the copyright notice and this notice are
|
||||
preserved.
|
||||
|
||||
License: BSD-2-clause
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
.
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||
OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGE.
|
||||
|
||||
License: PSF-2
|
||||
1. This LICENSE AGREEMENT is between the Python Software Foundation ("PSF"),
|
||||
and the Individual or Organization ("Licensee") accessing and otherwise using
|
||||
this software ("Python") in source or binary form and its associated
|
||||
documentation.
|
||||
.
|
||||
2. Subject to the terms and conditions of this License Agreement, PSF hereby
|
||||
grants Licensee a nonexclusive, royalty-free, world-wide license to
|
||||
reproduce, analyze, test, perform and/or display publicly, prepare derivative
|
||||
works, distribute, and otherwise use Python alone or in any derivative
|
||||
version, provided, however, that PSF's License Agreement and PSF's notice of
|
||||
copyright, i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006 Python
|
||||
Software Foundation; All Rights Reserved" are retained in Python alone or in
|
||||
any derivative version prepared by Licensee.
|
||||
.
|
||||
3. In the event Licensee prepares a derivative work that is based on or
|
||||
incorporates Python or any part thereof, and wants to make the derivative
|
||||
work available to others as provided herein, then Licensee hereby agrees to
|
||||
include in any such work a brief summary of the changes made to Python.
|
||||
.
|
||||
4. PSF is making Python available to Licensee on an "AS IS" basis. PSF MAKES
|
||||
NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF EXAMPLE, BUT
|
||||
NOT LIMITATION, PSF MAKES NO AND DISCLAIMS ANY REPRESENTATION OR WARRANTY OF
|
||||
MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF
|
||||
PYTHON WILL NOT INFRINGE ANY THIRD PARTY RIGHTS.
|
||||
.
|
||||
5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON FOR ANY
|
||||
INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF
|
||||
MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON, OR ANY DERIVATIVE
|
||||
THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
|
||||
.
|
||||
6. This License Agreement will automatically terminate upon a material breach
|
||||
of its terms and conditions.
|
||||
.
|
||||
7. Nothing in this License Agreement shall be deemed to create any
|
||||
relationship of agency, partnership, or joint venture between PSF and
|
||||
Licensee. This License Agreement does not grant permission to use PSF
|
||||
trademarks or trade name in a trademark sense to endorse or promote products
|
||||
or services of Licensee, or any third party.
|
||||
.
|
||||
8. By copying, installing or otherwise using Python, Licensee agrees to be
|
||||
bound by the terms and conditions of this License Agreement.
|
||||
|
||||
License: CC-0
|
||||
Statement of Purpose
|
||||
.
|
||||
The laws of most jurisdictions throughout the world automatically
|
||||
confer exclusive Copyright and Related Rights (defined below) upon
|
||||
the creator and subsequent owner(s) (each and all, an "owner") of an
|
||||
original work of authorship and/or a database (each, a "Work").
|
||||
.
|
||||
Certain owners wish to permanently relinquish those rights to a Work
|
||||
for the purpose of contributing to a commons of creative, cultural
|
||||
and scientific works ("Commons") that the public can reliably and
|
||||
without fear of later claims of infringement build upon, modify,
|
||||
incorporate in other works, reuse and redistribute as freely as
|
||||
possible in any form whatsoever and for any purposes, including
|
||||
without limitation commercial purposes. These owners may contribute
|
||||
to the Commons to promote the ideal of a free culture and the further
|
||||
production of creative, cultural and scientific works, or to gain
|
||||
reputation or greater distribution for their Work in part through the
|
||||
use and efforts of others.
|
||||
.
|
||||
For these and/or other purposes and motivations, and without any
|
||||
expectation of additional consideration or compensation, the person
|
||||
associating CC0 with a Work (the "Affirmer"), to the extent that he
|
||||
or she is an owner of Copyright and Related Rights in the Work,
|
||||
voluntarily elects to apply CC0 to the Work and publicly distribute
|
||||
the Work under its terms, with knowledge of his or her Copyright and
|
||||
Related Rights in the Work and the meaning and intended legal effect
|
||||
of CC0 on those rights.
|
||||
.
|
||||
1. Copyright and Related Rights. A Work made available under CC0 may
|
||||
be protected by copyright and related or neighboring rights
|
||||
("Copyright and Related Rights"). Copyright and Related Rights
|
||||
include, but are not limited to, the following:
|
||||
.
|
||||
i. the right to reproduce, adapt, distribute, perform, display,
|
||||
communicate, and translate a Work;
|
||||
ii. moral rights retained by the original author(s) and/or
|
||||
performer(s);
|
||||
iii. publicity and privacy rights pertaining to a person's image
|
||||
or likeness depicted in a Work;
|
||||
iv. rights protecting against unfair competition in regards to a
|
||||
Work, subject to the limitations in paragraph 4(a), below;
|
||||
v. rights protecting the extraction, dissemination, use and
|
||||
reuse of data in a Work;
|
||||
vi. database rights (such as those arising under Directive
|
||||
96/9/EC of the European Parliament and of the Council of 11
|
||||
March 1996 on the legal protection of databases, and under
|
||||
any national implementation thereof, including any amended or
|
||||
successor version of such directive); and
|
||||
vii. other similar, equivalent or corresponding rights throughout
|
||||
the world based on applicable law or treaty, and any national
|
||||
implementations thereof.
|
||||
.
|
||||
2. Waiver. To the greatest extent permitted by, but not in
|
||||
contravention of, applicable law, Affirmer hereby overtly, fully,
|
||||
permanently, irrevocably and unconditionally waives, abandons, and
|
||||
surrenders all of Affirmer's Copyright and Related Rights and
|
||||
associated claims and causes of action, whether now known or
|
||||
unknown (including existing as well as future claims and causes of
|
||||
action), in the Work (i) in all territories worldwide, (ii) for
|
||||
the maximum duration provided by applicable law or treaty
|
||||
(including future time extensions), (iii) in any current or future
|
||||
medium and for any number of copies, and (iv) for any purpose
|
||||
whatsoever, including without limitation commercial, advertising
|
||||
or promotional purposes (the "Waiver"). Affirmer makes the Waiver
|
||||
for the benefit of each member of the public at large and to the
|
||||
detriment of Affirmer's heirs and successors, fully intending that
|
||||
such Waiver shall not be subject to revocation, rescission,
|
||||
cancellation, termination, or any other legal or equitable action
|
||||
to disrupt the quiet enjoyment of the Work by the public as
|
||||
contemplated by Affirmer's express Statement of Purpose.
|
||||
.
|
||||
3. Public License Fallback. Should any part of the Waiver for any
|
||||
reason be judged legally invalid or ineffective under applicable law,
|
||||
then the Waiver shall be preserved to the maximum extent permitted
|
||||
taking into account Affirmer's express Statement of Purpose. In
|
||||
addition, to the extent the Waiver is so judged Affirmer hereby
|
||||
grants to each affected person a royalty-free, non transferable, non
|
||||
sublicensable, non exclusive, irrevocable and unconditional license
|
||||
to exercise Affirmer's Copyright and Related Rights in the Work (i)
|
||||
in all territories worldwide, (ii) for the maximum duration provided
|
||||
by applicable law or treaty (including future time extensions), (iii)
|
||||
in any current or future medium and for any number of copies, and
|
||||
(iv) for any purpose whatsoever, including without limitation
|
||||
commercial, advertising or promotional purposes (the "License"). The
|
||||
License shall be deemed effective as of the date CC0 was applied by
|
||||
Affirmer to the Work. Should any part of the License for any reason
|
||||
be judged legally invalid or ineffective under applicable law, such
|
||||
partial invalidity or ineffectiveness shall not invalidate the
|
||||
remainder of the License, and in such case Affirmer hereby affirms
|
||||
that he or she will not (i) exercise any of his or her remaining
|
||||
Copyright and Related Rights in the Work or (ii) assert any
|
||||
associated claims and causes of action with respect to the Work, in
|
||||
either case contrary to Affirmer's express Statement of Purpose.
|
||||
.
|
||||
4. Limitations and Disclaimers.
|
||||
.
|
||||
a. No trademark or patent rights held by Affirmer are waived,
|
||||
abandoned, surrendered, licensed or otherwise affected by
|
||||
this document.
|
||||
b. Affirmer offers the Work as-is and makes no representations
|
||||
or warranties of any kind concerning the Work, express,
|
||||
implied, statutory or otherwise, including without limitation
|
||||
warranties of title, merchantability, fitness for a
|
||||
particular purpose, non infringement, or the absence of
|
||||
latent or other defects, accuracy, or the present or absence
|
||||
of errors, whether or not discoverable, all to the greatest
|
||||
extent permissible under applicable law.
|
||||
c. Affirmer disclaims responsibility for clearing rights of
|
||||
other persons that may apply to the Work or any use thereof,
|
||||
including without limitation any person's Copyright and
|
||||
Related Rights in the Work. Further, Affirmer disclaims
|
||||
responsibility for obtaining any necessary consents,
|
||||
permissions or other rights required for any use of the
|
||||
Work.
|
||||
d. Affirmer understands and acknowledges that Creative Commons
|
||||
is not a party to this document and has no duty or obligation
|
||||
with respect to this CC0 or use of the Work.
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
Tests: upstream-tests
|
||||
Depends: @, python3-mock, python3-pytest
|
|
@ -1,14 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
if ! [ -d "$AUTOPKGTEST_TMP" ]; then
|
||||
echo "AUTOPKGTEST_TMP not set." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cp -rv tests "$AUTOPKGTEST_TMP"
|
||||
cd "$AUTOPKGTEST_TMP"
|
||||
mkdir -v libarchive
|
||||
touch README.rst
|
||||
py.test-3 tests -vv -l -r a
|
Loading…
Reference in New Issue