Initial forgejo install
parent
9a0faff058
commit
48161614f1
|
@ -240,7 +240,9 @@ class GitRepositories(Resource):
|
||||||
|
|
||||||
|
|
||||||
installer = OpengnsysGitInstaller()
|
installer = OpengnsysGitInstaller()
|
||||||
installer.init_git_repo(repo + ".git")
|
installer.add_forgejo_repo(repo)
|
||||||
|
|
||||||
|
#installer.init_git_repo(repo + ".git")
|
||||||
|
|
||||||
|
|
||||||
return jsonify({"status": "Repository created"}), 201
|
return jsonify({"status": "Repository created"}), 201
|
||||||
|
|
|
@ -1628,6 +1628,7 @@ class OpengnsysGitLibrary:
|
||||||
"""
|
"""
|
||||||
Restore the repository to the state it had before the non-committed modifications
|
Restore the repository to the state it had before the non-committed modifications
|
||||||
"""
|
"""
|
||||||
|
self.logger.info("Undoing any user changes to the filesystem")
|
||||||
repo = git.Repo(path)
|
repo = git.Repo(path)
|
||||||
|
|
||||||
repo.head.reset(index=True, working_tree=True)
|
repo.head.reset(index=True, working_tree=True)
|
||||||
|
|
|
@ -10,9 +10,21 @@ import subprocess
|
||||||
import sys
|
import sys
|
||||||
import pwd
|
import pwd
|
||||||
import grp
|
import grp
|
||||||
from termcolor import colored, cprint
|
from termcolor import cprint
|
||||||
import git
|
import git
|
||||||
import libarchive
|
import libarchive
|
||||||
|
import urllib.request
|
||||||
|
import pathlib
|
||||||
|
import socket
|
||||||
|
import time
|
||||||
|
import requests
|
||||||
|
|
||||||
|
|
||||||
|
#FORGEJO_VERSION="8.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 show_error(*args):
|
def show_error(*args):
|
||||||
|
@ -27,6 +39,7 @@ def show_error(*args):
|
||||||
"""
|
"""
|
||||||
cprint(*args, "red", attrs = ["bold"], file=sys.stderr)
|
cprint(*args, "red", attrs = ["bold"], file=sys.stderr)
|
||||||
|
|
||||||
|
|
||||||
class RequirementException(Exception):
|
class RequirementException(Exception):
|
||||||
"""Excepción que indica que nos falta algún requisito
|
"""Excepción que indica que nos falta algún requisito
|
||||||
|
|
||||||
|
@ -102,11 +115,16 @@ class OpengnsysGitInstaller:
|
||||||
self.git_basedir = "base.git"
|
self.git_basedir = "base.git"
|
||||||
self.ssh_user = "opengnsys"
|
self.ssh_user = "opengnsys"
|
||||||
self.ssh_group = "opengnsys"
|
self.ssh_group = "opengnsys"
|
||||||
|
self.email = "OpenGnsys@opengnsys.com"
|
||||||
|
self.forgejo_password = "opengnsys"
|
||||||
|
self.forgejo_port = 3000
|
||||||
|
|
||||||
|
|
||||||
self.ssh_homedir = pwd.getpwnam(self.ssh_user).pw_dir
|
self.ssh_homedir = pwd.getpwnam(self.ssh_user).pw_dir
|
||||||
self.ssh_uid = pwd.getpwnam(self.ssh_user).pw_uid
|
self.ssh_uid = pwd.getpwnam(self.ssh_user).pw_uid
|
||||||
self.ssh_gid = grp.getgrnam(self.ssh_group).gr_gid
|
self.ssh_gid = grp.getgrnam(self.ssh_group).gr_gid
|
||||||
self.temp_dir = None
|
self.temp_dir = None
|
||||||
|
self.script_path = os.path.realpath(os.path.dirname(__file__))
|
||||||
|
|
||||||
# Possible names for SSH key
|
# Possible names for SSH key
|
||||||
self.key_paths = ["scripts/ssl/id_rsa.pub", "scripts/ssl/id_ed25519.pub", "scripts/ssl/id_ecdsa.pub", "scripts/ssl/id_ed25519_sk.pub", "scripts/ssl/id_ecdsa_sk.pub"]
|
self.key_paths = ["scripts/ssl/id_rsa.pub", "scripts/ssl/id_ed25519.pub", "scripts/ssl/id_ecdsa.pub", "scripts/ssl/id_ed25519_sk.pub", "scripts/ssl/id_ecdsa_sk.pub"]
|
||||||
|
@ -180,7 +198,7 @@ class OpengnsysGitInstaller:
|
||||||
|
|
||||||
self.__logger.info("Configurando repositorio de GIT")
|
self.__logger.info("Configurando repositorio de GIT")
|
||||||
repo.config_writer().set_value("user", "name", "OpenGnsys").release()
|
repo.config_writer().set_value("user", "name", "OpenGnsys").release()
|
||||||
repo.config_writer().set_value("user", "email", "OpenGnsys@opengnsys.com").release()
|
repo.config_writer().set_value("user", "email", self.email).release()
|
||||||
|
|
||||||
self._recursive_chown(repo_path, ouid=self.ssh_uid, ogid=self.ssh_gid)
|
self._recursive_chown(repo_path, ouid=self.ssh_uid, ogid=self.ssh_gid)
|
||||||
|
|
||||||
|
@ -209,6 +227,71 @@ class OpengnsysGitInstaller:
|
||||||
for filename in filenames:
|
for filename in filenames:
|
||||||
os.chown(os.path.join(dirpath, filename), uid=ouid, gid=ogid)
|
os.chown(os.path.join(dirpath, filename), uid=ouid, gid=ogid)
|
||||||
|
|
||||||
|
def _wait_for_port(self, host, port):
|
||||||
|
self.__logger.info("Waiting for %s:%i to be up", host, port)
|
||||||
|
|
||||||
|
timeout = 60
|
||||||
|
start_time = time.time()
|
||||||
|
|
||||||
|
ready = False
|
||||||
|
while not ready and (time.time() - start_time) < 60:
|
||||||
|
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
try:
|
||||||
|
s.connect((host, port))
|
||||||
|
ready = True
|
||||||
|
s.close()
|
||||||
|
except TimeoutError:
|
||||||
|
self.__logger.debug("Timed out, no connection yet.")
|
||||||
|
except OSError as oserr:
|
||||||
|
self.__logger.debug("%s, no connection yet. %.1f seconds left.", oserr.strerror, timeout - (time.time() - start_time))
|
||||||
|
|
||||||
|
time.sleep(0.1)
|
||||||
|
|
||||||
|
if ready:
|
||||||
|
self.__logger.info("Connection established.")
|
||||||
|
else:
|
||||||
|
self.__logger.error("Timed out waiting for connection!")
|
||||||
|
raise TimeoutError("Timed out waiting for connection!")
|
||||||
|
|
||||||
|
|
||||||
|
def _extract_ssh_key(self):
|
||||||
|
public_key=""
|
||||||
|
|
||||||
|
INITRD = "oginitrd.img"
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
#self.temp_dir = self._get_tempdir()
|
||||||
|
|
||||||
|
if self.usesshkey:
|
||||||
|
with open(self.usesshkey, 'r') as f:
|
||||||
|
public_key = f.read().strip()
|
||||||
|
|
||||||
|
else:
|
||||||
|
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)
|
||||||
|
|
||||||
|
if file.pathname in self.key_paths_dict:
|
||||||
|
data = bytearray()
|
||||||
|
for block in file.get_blocks():
|
||||||
|
data = data + block
|
||||||
|
public_key = data.decode('utf-8').strip()
|
||||||
|
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
print(f"No se encuentra la imagen de initrd {client_initrd_path}")
|
||||||
|
exit(2)
|
||||||
|
|
||||||
|
return public_key
|
||||||
|
|
||||||
def install(self):
|
def install(self):
|
||||||
"""Instalar
|
"""Instalar
|
||||||
|
@ -338,6 +421,182 @@ class OpengnsysGitInstaller:
|
||||||
for DIR in ["base.git", "linux.git", "windows.git"]: #, "LinAcl", "WinAcl"]:
|
for DIR in ["base.git", "linux.git", "windows.git"]: #, "LinAcl", "WinAcl"]:
|
||||||
self._recursive_chown(os.path.join(ogdir_images, DIR), ouid=self.ssh_uid, ogid=self.ssh_gid)
|
self._recursive_chown(os.path.join(ogdir_images, DIR), ouid=self.ssh_uid, ogid=self.ssh_gid)
|
||||||
|
|
||||||
|
def _install_template(self, template, destination, keysvalues):
|
||||||
|
|
||||||
|
self.__logger.info("Writing template %s into %s", template, destination)
|
||||||
|
|
||||||
|
data = ""
|
||||||
|
with open(template, "r", encoding="utf-8") as template_file:
|
||||||
|
data = template_file.read()
|
||||||
|
|
||||||
|
for key in keysvalues.keys():
|
||||||
|
data = data.replace("{" + key + "}", keysvalues[key])
|
||||||
|
|
||||||
|
with open(destination, "w+", encoding="utf-8") as out_file:
|
||||||
|
out_file.write(data)
|
||||||
|
|
||||||
|
def _runcmd(self, cmd):
|
||||||
|
self.__logger.debug("Running: %s", cmd)
|
||||||
|
|
||||||
|
ret = subprocess.run(cmd, check=True,capture_output=True, encoding='utf-8')
|
||||||
|
return ret.stdout.strip()
|
||||||
|
|
||||||
|
def install_forgejo(self):
|
||||||
|
self.__logger.info("Installing Forgejo")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
bin_path = os.path.join(self.base_path, "bin", "forgejo")
|
||||||
|
conf_dir_path = os.path.join(self.base_path, "etc", "forgejo")
|
||||||
|
|
||||||
|
|
||||||
|
lfs_dir_path = os.path.join(self.base_path, "images", "lfs")
|
||||||
|
forgejo_work_dir_path = os.path.join(self.base_path, "var", "run", "forgejo")
|
||||||
|
forgejo_db_dir_path = os.path.join(self.base_path, "var", "run", "forgejo")
|
||||||
|
forgejo_db_path = os.path.join(forgejo_db_dir_path, "forgejo.db")
|
||||||
|
|
||||||
|
forgejo_log_dir_path = os.path.join(self.base_path, "log", "forgejo")
|
||||||
|
|
||||||
|
|
||||||
|
conf_path = os.path.join(conf_dir_path, "app.ini")
|
||||||
|
|
||||||
|
self.__logger.debug("Stopping opengnsys-forgejo service")
|
||||||
|
subprocess.run(["systemctl", "stop", "opengnsys-forgejo"], check=False)
|
||||||
|
|
||||||
|
if not os.path.exists(bin_path):
|
||||||
|
self.__logger.debug("Downloading from %s into %s", FORGEJO_URL, bin_path)
|
||||||
|
urllib.request.urlretrieve(FORGEJO_URL, bin_path)
|
||||||
|
os.chmod(bin_path, 0o755)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if os.path.exists(forgejo_db_path):
|
||||||
|
self.__logger.debug("Removing old configuration")
|
||||||
|
os.unlink(forgejo_db_path)
|
||||||
|
else:
|
||||||
|
self.__logger.debug("Old configuration not present, ok.")
|
||||||
|
|
||||||
|
self.__logger.debug("Creating directories")
|
||||||
|
|
||||||
|
pathlib.Path(conf_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_db_dir_path).mkdir(parents=True, exist_ok=True)
|
||||||
|
pathlib.Path(forgejo_log_dir_path).mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
|
||||||
|
os.chown(lfs_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 = {
|
||||||
|
"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_db_path" : forgejo_db_path,
|
||||||
|
"forgejo_repository_root" : os.path.join(self.base_path, "images"),
|
||||||
|
"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.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")
|
||||||
|
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):
|
||||||
|
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)
|
||||||
|
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)
|
||||||
|
self.__logger.error("stderr: %s", ret.stderr)
|
||||||
|
raise RuntimeError("Failed to run necessary command")
|
||||||
|
|
||||||
|
run_forge_cmd(["admin", "doctor", "check"])
|
||||||
|
|
||||||
|
run_forge_cmd(["admin", "user", "create", "--username", self.ssh_user, "--password", self.forgejo_password, "--email", self.email])
|
||||||
|
|
||||||
|
token = run_forge_cmd(["admin", "user", "generate-access-token", "--username", self.ssh_user, "-t", "gitapi", "--scopes", "all", "--raw"])
|
||||||
|
|
||||||
|
with open(os.path.join(self.base_path, "etc", "ogGitApiToken.cfg"), "w+", encoding='utf-8') as token_file:
|
||||||
|
token_file.write(token)
|
||||||
|
|
||||||
|
|
||||||
|
ssh_key = self._extract_ssh_key()
|
||||||
|
|
||||||
|
self.add_forgejo_sshkey(ssh_key, "Default key")
|
||||||
|
|
||||||
|
|
||||||
|
def add_forgejo_repo(self, repository_name, description = ""):
|
||||||
|
token = ""
|
||||||
|
with open(os.path.join(self.base_path, "etc", "ogGitApiToken.cfg"), "r", encoding='utf-8') as token_file:
|
||||||
|
token = token_file.read().strip()
|
||||||
|
|
||||||
|
self.__logger.info("Adding repository %s for Forgejo", repository_name)
|
||||||
|
|
||||||
|
r = requests.post(
|
||||||
|
f"http://localhost:{self.forgejo_port}/api/v1/user/repos",
|
||||||
|
json={
|
||||||
|
"auto_init" : False,
|
||||||
|
"default_branch" : "main",
|
||||||
|
"description" : description,
|
||||||
|
"name" : repository_name,
|
||||||
|
"private" : False
|
||||||
|
}, headers={
|
||||||
|
'Authorization' : f"token {token}"
|
||||||
|
},
|
||||||
|
timeout = 60
|
||||||
|
)
|
||||||
|
|
||||||
|
self.__logger.info("Request status was %i", r.status_code)
|
||||||
|
|
||||||
|
def add_forgejo_sshkey(self, pubkey, description = ""):
|
||||||
|
token = ""
|
||||||
|
with open(os.path.join(self.base_path, "etc", "ogGitApiToken.cfg"), "r", encoding='utf-8') as token_file:
|
||||||
|
token = token_file.read().strip()
|
||||||
|
|
||||||
|
self.__logger.info("Adding SSH key to Forgejo: %s", pubkey)
|
||||||
|
|
||||||
|
r = requests.post(
|
||||||
|
f"http://localhost:{self.forgejo_port}/api/v1/user/keys",
|
||||||
|
json={
|
||||||
|
"key" : pubkey,
|
||||||
|
"read_only" : False,
|
||||||
|
"title" : description
|
||||||
|
}, headers={
|
||||||
|
'Authorization' : f"token {token}"
|
||||||
|
},
|
||||||
|
timeout = 60
|
||||||
|
)
|
||||||
|
|
||||||
|
self.__logger.info("Request status was %i", r.status_code)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
@ -350,6 +609,9 @@ if __name__ == '__main__':
|
||||||
prog="OpenGnsys Installer",
|
prog="OpenGnsys Installer",
|
||||||
description="Script para la instalación del repositorio git",
|
description="Script para la instalación del repositorio git",
|
||||||
)
|
)
|
||||||
|
parser.add_argument('--forgejo-only', action='store_true', help="Solo instalar forgejo")
|
||||||
|
parser.add_argument('--forgejo-addrepos', action='store_true', help="Solo agregar repositorios forgejo")
|
||||||
|
|
||||||
parser.add_argument('--testmode', action='store_true', help="Modo de prueba")
|
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('--ignoresshkey', action='store_true', help="Ignorar clave de SSH")
|
||||||
parser.add_argument('--usesshkey', type=str, help="Usar clave SSH especificada")
|
parser.add_argument('--usesshkey', type=str, help="Usar clave SSH especificada")
|
||||||
|
@ -364,7 +626,13 @@ if __name__ == '__main__':
|
||||||
logger.debug("Inicio de instalación")
|
logger.debug("Inicio de instalación")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
installer.install()
|
if args.forgejo_only:
|
||||||
|
installer.install_forgejo()
|
||||||
|
elif args.forgejo_addrepos:
|
||||||
|
installer.add_forgejo_repo("linux")
|
||||||
|
else:
|
||||||
|
installer.install()
|
||||||
|
installer.install_forgejo()
|
||||||
except RequirementException as req:
|
except RequirementException as req:
|
||||||
show_error(f"Requisito para la instalación no satisfecho: {req.message}")
|
show_error(f"Requisito para la instalación no satisfecho: {req.message}")
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|
Loading…
Reference in New Issue