[IMP] script git_local_server: tool to deploy git server to share code
Generated by Claude Code v2.1.71 model Opus 4.6
This commit is contained in:
parent
cbe4288b87
commit
a80c436e75
4 changed files with 840 additions and 14 deletions
631
script/git/git_local_server.py
Executable file
631
script/git/git_local_server.py
Executable file
|
|
@ -0,0 +1,631 @@
|
|||
#!/usr/bin/env python3
|
||||
# © 2025-2026 TechnoLibre (http://www.technolibre.ca)
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
|
||||
|
||||
import argparse
|
||||
import logging
|
||||
import os
|
||||
import signal
|
||||
import subprocess
|
||||
import sys
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
DEFAULT_ERPLIBRE_PATH = os.path.normpath(
|
||||
os.path.join(os.path.dirname(__file__), "..", "..")
|
||||
)
|
||||
DEFAULT_GIT_PATH = os.path.join(os.path.expanduser("~"), ".git-server")
|
||||
PRODUCTION_GIT_PATH = "/srv/git"
|
||||
DEFAULT_MANIFEST = ".repo/local_manifests/erplibre_manifest.xml"
|
||||
DEFAULT_REMOTE_NAME = "local"
|
||||
DEFAULT_PORT = 9418
|
||||
|
||||
|
||||
def get_config():
|
||||
parser = argparse.ArgumentParser(
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
description="""\
|
||||
Deploy a local git server using git daemon and configure
|
||||
all repos from the ERPLibre manifest.
|
||||
|
||||
Actions (in order):
|
||||
1. init - Create bare repos in the git server path
|
||||
2. remote - Add 'local' remote to each repo
|
||||
3. push - Push all branches to the local remote
|
||||
4. serve - Start git daemon
|
||||
|
||||
By default, all actions are executed sequentially.
|
||||
The default path is ~/.git-server (user-level, no root needed).
|
||||
Use --production-ready for /srv/git (requires root).
|
||||
""",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-p",
|
||||
"--path",
|
||||
default=None,
|
||||
help=(
|
||||
"Path for git server bare repos" f" (default: {DEFAULT_GIT_PATH})"
|
||||
),
|
||||
)
|
||||
parser.add_argument(
|
||||
"--production-ready",
|
||||
action="store_true",
|
||||
help=(
|
||||
f"Use {PRODUCTION_GIT_PATH} as git server path"
|
||||
" (requires root privileges)"
|
||||
),
|
||||
)
|
||||
parser.add_argument(
|
||||
"-m",
|
||||
"--manifest",
|
||||
default=DEFAULT_MANIFEST,
|
||||
help="Manifest XML file" f" (default: {DEFAULT_MANIFEST})",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--remote-name",
|
||||
default=DEFAULT_REMOTE_NAME,
|
||||
help="Remote name to add" f" (default: {DEFAULT_REMOTE_NAME})",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--port",
|
||||
type=int,
|
||||
default=DEFAULT_PORT,
|
||||
help=f"Git daemon port (default: {DEFAULT_PORT})",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--action",
|
||||
choices=["init", "remote", "push", "serve", "all"],
|
||||
default="all",
|
||||
help="Action to perform (default: all)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--erplibre-root",
|
||||
default=None,
|
||||
help="ERPLibre root directory (default: auto-detect)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--push-all-branches",
|
||||
action="store_true",
|
||||
help="Push all local branches, not just the current one",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--remote-url-type",
|
||||
choices=["file", "daemon"],
|
||||
default="file",
|
||||
help=(
|
||||
"Remote URL type: 'file' uses local path"
|
||||
" (push works without daemon), 'daemon'"
|
||||
" uses git:// protocol (default: file)"
|
||||
),
|
||||
)
|
||||
parser.add_argument(
|
||||
"-v",
|
||||
"--verbose",
|
||||
action="store_true",
|
||||
help="Enable verbose output",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
# Resolve git server path
|
||||
if args.path and args.production_ready:
|
||||
parser.error("--path and --production-ready are mutually exclusive")
|
||||
if args.production_ready:
|
||||
args.path = PRODUCTION_GIT_PATH
|
||||
elif args.path is None:
|
||||
args.path = DEFAULT_GIT_PATH
|
||||
|
||||
return args
|
||||
|
||||
|
||||
def check_path_permissions(path):
|
||||
"""Check if we have write permissions on the target
|
||||
path. Try the path itself first, then its closest
|
||||
existing parent directory."""
|
||||
check = path
|
||||
while check and not os.path.exists(check):
|
||||
check = os.path.dirname(check)
|
||||
if not check:
|
||||
check = "/"
|
||||
if os.access(check, os.W_OK):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def require_permissions_or_exit(path):
|
||||
"""Exit with instructions if we cannot write to path."""
|
||||
print(f"Error: No write permission on {path}")
|
||||
print(
|
||||
"Please run this script with appropriate"
|
||||
" privileges:"
|
||||
f"\n sudo {sys.executable}"
|
||||
f" {' '.join(sys.argv)}"
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def parse_manifest(manifest_path):
|
||||
"""Parse the manifest XML and return list of projects."""
|
||||
tree = ET.parse(manifest_path)
|
||||
root = tree.getroot()
|
||||
|
||||
remotes = {}
|
||||
for remote in root.findall("remote"):
|
||||
remotes[remote.get("name")] = remote.get("fetch")
|
||||
|
||||
projects = []
|
||||
for project in root.findall("project"):
|
||||
name = project.get("name")
|
||||
path = project.get("path")
|
||||
remote = project.get("remote")
|
||||
revision = project.get("revision")
|
||||
projects.append(
|
||||
{
|
||||
"name": name,
|
||||
"path": path,
|
||||
"remote": remote,
|
||||
"revision": revision,
|
||||
"fetch_url": remotes.get(remote, ""),
|
||||
}
|
||||
)
|
||||
|
||||
return projects
|
||||
|
||||
|
||||
def init_bare_repos(git_path, projects):
|
||||
"""Create bare repos for all projects in the manifest."""
|
||||
os.makedirs(git_path, exist_ok=True)
|
||||
|
||||
created = 0
|
||||
skipped = 0
|
||||
for project in projects:
|
||||
repo_name = project["name"]
|
||||
if not repo_name.endswith(".git"):
|
||||
repo_name += ".git"
|
||||
bare_path = os.path.join(git_path, repo_name)
|
||||
|
||||
if os.path.exists(bare_path):
|
||||
_logger.info(f" Exists: {bare_path}")
|
||||
skipped += 1
|
||||
continue
|
||||
|
||||
_logger.info(f" Creating: {bare_path}")
|
||||
subprocess.run(
|
||||
["git", "init", "--bare", bare_path],
|
||||
check=True,
|
||||
capture_output=True,
|
||||
)
|
||||
|
||||
# Enable git daemon export
|
||||
export_file = os.path.join(bare_path, "git-daemon-export-ok")
|
||||
open(export_file, "w").close()
|
||||
|
||||
created += 1
|
||||
|
||||
print(f"Bare repos: {created} created, {skipped} skipped (already exist)")
|
||||
|
||||
|
||||
def build_remote_url(git_path, repo_name, port, remote_url_type):
|
||||
"""Build the remote URL based on the type."""
|
||||
if remote_url_type == "daemon":
|
||||
if port != DEFAULT_PORT:
|
||||
return f"git://localhost:{port}/{repo_name}"
|
||||
return f"git://localhost/{repo_name}"
|
||||
# Default: file path
|
||||
return os.path.join(git_path, repo_name)
|
||||
|
||||
|
||||
def add_remotes(
|
||||
erplibre_root,
|
||||
git_path,
|
||||
projects,
|
||||
remote_name,
|
||||
port,
|
||||
remote_url_type,
|
||||
):
|
||||
"""Add local remote to each repo.
|
||||
|
||||
remote_url_type='file': uses local path, push works
|
||||
without daemon running.
|
||||
remote_url_type='daemon': uses git://localhost URL,
|
||||
requires daemon for push.
|
||||
"""
|
||||
added = 0
|
||||
skipped = 0
|
||||
errors = 0
|
||||
for project in projects:
|
||||
repo_path = os.path.join(erplibre_root, project["path"])
|
||||
if not os.path.isdir(repo_path):
|
||||
_logger.warning(f" Not found: {repo_path}")
|
||||
errors += 1
|
||||
continue
|
||||
|
||||
repo_name = project["name"]
|
||||
if not repo_name.endswith(".git"):
|
||||
repo_name += ".git"
|
||||
|
||||
remote_url = build_remote_url(
|
||||
git_path, repo_name, port, remote_url_type
|
||||
)
|
||||
|
||||
# Check if remote already exists
|
||||
result = subprocess.run(
|
||||
["git", "-C", repo_path, "remote"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
existing_remotes = result.stdout.strip().split("\n")
|
||||
|
||||
if remote_name in existing_remotes:
|
||||
# Update URL if remote exists
|
||||
subprocess.run(
|
||||
[
|
||||
"git",
|
||||
"-C",
|
||||
repo_path,
|
||||
"remote",
|
||||
"set-url",
|
||||
remote_name,
|
||||
remote_url,
|
||||
],
|
||||
check=True,
|
||||
capture_output=True,
|
||||
)
|
||||
_logger.info(f" Updated: {project['path']}")
|
||||
skipped += 1
|
||||
else:
|
||||
subprocess.run(
|
||||
[
|
||||
"git",
|
||||
"-C",
|
||||
repo_path,
|
||||
"remote",
|
||||
"add",
|
||||
remote_name,
|
||||
remote_url,
|
||||
],
|
||||
check=True,
|
||||
capture_output=True,
|
||||
)
|
||||
_logger.info(f" Added: {project['path']}")
|
||||
added += 1
|
||||
|
||||
print(f"Remotes: {added} added, {skipped} updated," f" {errors} errors")
|
||||
|
||||
|
||||
def is_detached_head(repo_path):
|
||||
"""Check if a repo is in detached HEAD state."""
|
||||
result = subprocess.run(
|
||||
["git", "-C", repo_path, "symbolic-ref", "HEAD"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
return result.returncode != 0
|
||||
|
||||
|
||||
def checkout_manifest_branch(repo_path, revision):
|
||||
"""Checkout the branch from the manifest revision.
|
||||
|
||||
When Google Repo syncs, repos end up in detached HEAD
|
||||
on the exact commit. We create/checkout a local branch
|
||||
matching the manifest revision so git push works.
|
||||
"""
|
||||
# Extract branch name — revision can be
|
||||
# "18.0", "18.0_dev", "ERPLibre/18.0", "main", etc.
|
||||
branch = revision.split("/")[-1] if "/" in revision else revision
|
||||
|
||||
# Check if local branch already exists
|
||||
result = subprocess.run(
|
||||
[
|
||||
"git",
|
||||
"-C",
|
||||
repo_path,
|
||||
"show-ref",
|
||||
"--verify",
|
||||
f"refs/heads/{branch}",
|
||||
],
|
||||
capture_output=True,
|
||||
)
|
||||
if result.returncode == 0:
|
||||
# Branch exists, checkout it
|
||||
subprocess.run(
|
||||
["git", "-C", repo_path, "checkout", branch],
|
||||
capture_output=True,
|
||||
)
|
||||
else:
|
||||
# Create branch from current HEAD
|
||||
subprocess.run(
|
||||
[
|
||||
"git",
|
||||
"-C",
|
||||
repo_path,
|
||||
"checkout",
|
||||
"-b",
|
||||
branch,
|
||||
],
|
||||
capture_output=True,
|
||||
)
|
||||
return branch
|
||||
|
||||
|
||||
def update_bare_head(git_path, project):
|
||||
"""Update the HEAD of the bare repo to point to the
|
||||
manifest branch, so git clone checks out the right
|
||||
branch by default."""
|
||||
repo_name = project["name"]
|
||||
if not repo_name.endswith(".git"):
|
||||
repo_name += ".git"
|
||||
bare_path = os.path.join(git_path, repo_name)
|
||||
if not os.path.isdir(bare_path):
|
||||
return
|
||||
|
||||
revision = project.get("revision", "")
|
||||
if not revision:
|
||||
return
|
||||
branch = revision.split("/")[-1] if "/" in revision else revision
|
||||
subprocess.run(
|
||||
[
|
||||
"git",
|
||||
"-C",
|
||||
bare_path,
|
||||
"symbolic-ref",
|
||||
"HEAD",
|
||||
f"refs/heads/{branch}",
|
||||
],
|
||||
capture_output=True,
|
||||
)
|
||||
|
||||
|
||||
def push_to_local(
|
||||
erplibre_root,
|
||||
git_path,
|
||||
projects,
|
||||
remote_name,
|
||||
push_all_branches,
|
||||
):
|
||||
"""Push to local remote for each repo."""
|
||||
pushed = 0
|
||||
errors = 0
|
||||
checkouts = 0
|
||||
for project in projects:
|
||||
repo_path = os.path.join(erplibre_root, project["path"])
|
||||
if not os.path.isdir(repo_path):
|
||||
_logger.warning(f" Not found: {repo_path}")
|
||||
errors += 1
|
||||
continue
|
||||
|
||||
# Unshallow if needed (clone-depth in manifest)
|
||||
shallow_file = os.path.join(repo_path, ".git", "shallow")
|
||||
if os.path.exists(shallow_file):
|
||||
_logger.info(f" Unshallowing {project['path']}...")
|
||||
# Try each remote until unshallow succeeds
|
||||
result = subprocess.run(
|
||||
["git", "-C", repo_path, "remote"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
remotes = [
|
||||
r
|
||||
for r in result.stdout.strip().split("\n")
|
||||
if r and r != remote_name
|
||||
]
|
||||
# Try the manifest remote first
|
||||
manifest_remote = project.get("remote", "")
|
||||
if manifest_remote in remotes:
|
||||
remotes.remove(manifest_remote)
|
||||
remotes.insert(0, manifest_remote)
|
||||
|
||||
for try_remote in remotes:
|
||||
result = subprocess.run(
|
||||
[
|
||||
"git",
|
||||
"-C",
|
||||
repo_path,
|
||||
"fetch",
|
||||
"--unshallow",
|
||||
try_remote,
|
||||
],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=300,
|
||||
)
|
||||
if not os.path.exists(shallow_file):
|
||||
_logger.info(f" Unshallowed via" f" {try_remote}")
|
||||
break
|
||||
else:
|
||||
if os.path.exists(shallow_file):
|
||||
_logger.warning(
|
||||
f" Unshallow failed for"
|
||||
f" {project['path']},"
|
||||
" push may fail"
|
||||
)
|
||||
|
||||
# Handle detached HEAD: checkout manifest branch
|
||||
if is_detached_head(repo_path):
|
||||
revision = project.get("revision", "")
|
||||
if revision:
|
||||
branch = checkout_manifest_branch(repo_path, revision)
|
||||
_logger.info(
|
||||
f" Checkout {branch} for"
|
||||
f" {project['path']}"
|
||||
" (was detached HEAD)"
|
||||
)
|
||||
checkouts += 1
|
||||
else:
|
||||
_logger.warning(
|
||||
f" Detached HEAD with no revision"
|
||||
f" for {project['path']}, skipping"
|
||||
)
|
||||
errors += 1
|
||||
continue
|
||||
|
||||
try:
|
||||
if push_all_branches:
|
||||
cmd = [
|
||||
"git",
|
||||
"-C",
|
||||
repo_path,
|
||||
"push",
|
||||
remote_name,
|
||||
"--all",
|
||||
]
|
||||
else:
|
||||
cmd = [
|
||||
"git",
|
||||
"-C",
|
||||
repo_path,
|
||||
"push",
|
||||
remote_name,
|
||||
]
|
||||
result = subprocess.run(
|
||||
cmd,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=120,
|
||||
)
|
||||
if result.returncode != 0:
|
||||
_logger.warning(
|
||||
f" Push failed for"
|
||||
f" {project['path']}:"
|
||||
f" {result.stderr.strip()}"
|
||||
)
|
||||
errors += 1
|
||||
else:
|
||||
update_bare_head(git_path, project)
|
||||
_logger.info(f" Pushed: {project['path']}")
|
||||
pushed += 1
|
||||
except subprocess.TimeoutExpired:
|
||||
_logger.warning(f" Timeout pushing {project['path']}")
|
||||
errors += 1
|
||||
|
||||
print(
|
||||
f"Push: {pushed} pushed, {checkouts} branch"
|
||||
f" checkouts, {errors} errors"
|
||||
)
|
||||
|
||||
|
||||
def print_clone_commands(git_path, projects, port):
|
||||
"""Print git clone commands for all available repos."""
|
||||
print("=== Available repos to clone ===")
|
||||
base_url = (
|
||||
f"git://localhost:{port}"
|
||||
if port != DEFAULT_PORT
|
||||
else "git://localhost"
|
||||
)
|
||||
count = 0
|
||||
for project in projects:
|
||||
repo_name = project["name"]
|
||||
if not repo_name.endswith(".git"):
|
||||
repo_name += ".git"
|
||||
bare_path = os.path.join(git_path, repo_name)
|
||||
if os.path.isdir(bare_path):
|
||||
print(f" git clone {base_url}/{repo_name}" f" {project['path']}")
|
||||
count += 1
|
||||
print(f"\nTotal: {count} repos available")
|
||||
print()
|
||||
|
||||
|
||||
def serve_git_daemon(git_path, projects, port):
|
||||
"""Start git daemon to serve repos."""
|
||||
print_clone_commands(git_path, projects, port)
|
||||
|
||||
cmd = [
|
||||
"git",
|
||||
"daemon",
|
||||
"--reuseaddr",
|
||||
f"--base-path={git_path}",
|
||||
f"--port={port}",
|
||||
"--export-all",
|
||||
"--enable=receive-pack",
|
||||
git_path,
|
||||
]
|
||||
print(f"Starting git daemon on port {port}...")
|
||||
print(f" Base path: {git_path}")
|
||||
print(f" URL: git://localhost:{port}/")
|
||||
print(" Press Ctrl+C to stop")
|
||||
|
||||
try:
|
||||
process = subprocess.Popen(cmd)
|
||||
process.wait()
|
||||
except KeyboardInterrupt:
|
||||
print("\nStopping git daemon...")
|
||||
process.send_signal(signal.SIGTERM)
|
||||
process.wait()
|
||||
|
||||
|
||||
def main():
|
||||
config = get_config()
|
||||
|
||||
log_level = logging.DEBUG if config.verbose else logging.INFO
|
||||
logging.basicConfig(
|
||||
level=log_level,
|
||||
format="%(levelname)s: %(message)s",
|
||||
)
|
||||
|
||||
# Check write permissions on target path
|
||||
if not check_path_permissions(config.path):
|
||||
require_permissions_or_exit(config.path)
|
||||
|
||||
# Detect ERPLibre root
|
||||
if config.erplibre_root:
|
||||
erplibre_root = os.path.abspath(config.erplibre_root)
|
||||
else:
|
||||
erplibre_root = DEFAULT_ERPLIBRE_PATH
|
||||
|
||||
manifest_path = os.path.join(erplibre_root, config.manifest)
|
||||
if not os.path.exists(manifest_path):
|
||||
print(f"Error: Manifest not found: {manifest_path}")
|
||||
sys.exit(1)
|
||||
|
||||
print(f"ERPLibre root: {erplibre_root}")
|
||||
print(f"Manifest: {manifest_path}")
|
||||
print(f"Git server path: {config.path}")
|
||||
print(f"Remote name: {config.remote_name}")
|
||||
if config.production_ready:
|
||||
print("Mode: production (/srv/git)")
|
||||
else:
|
||||
print("Mode: development (~/.git-server)")
|
||||
print(f"Remote URL type: {config.remote_url_type}")
|
||||
print()
|
||||
|
||||
projects = parse_manifest(manifest_path)
|
||||
print(f"Found {len(projects)} projects in manifest")
|
||||
print()
|
||||
|
||||
action = config.action
|
||||
|
||||
if action in ("init", "all"):
|
||||
print("=== Creating bare repos ===")
|
||||
init_bare_repos(config.path, projects)
|
||||
print()
|
||||
|
||||
if action in ("remote", "all"):
|
||||
print("=== Adding remotes ===")
|
||||
add_remotes(
|
||||
erplibre_root,
|
||||
config.path,
|
||||
projects,
|
||||
config.remote_name,
|
||||
config.port,
|
||||
config.remote_url_type,
|
||||
)
|
||||
print()
|
||||
|
||||
if action in ("push", "all"):
|
||||
print("=== Pushing to local ===")
|
||||
push_to_local(
|
||||
erplibre_root,
|
||||
config.path,
|
||||
projects,
|
||||
config.remote_name,
|
||||
config.push_all_branches,
|
||||
)
|
||||
print()
|
||||
|
||||
if action in ("serve", "all"):
|
||||
print("=== Starting git daemon ===")
|
||||
serve_git_daemon(config.path, projects, config.port)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
@ -51,5 +51,6 @@
|
|||
"prompt_description_key": "json_format_modified_code",
|
||||
"makefile_cmd": "format"
|
||||
}
|
||||
]
|
||||
],
|
||||
"git_from_makefile": []
|
||||
}
|
||||
|
|
|
|||
|
|
@ -236,12 +236,13 @@ class TODO:
|
|||
[4] {t("menu_code")}
|
||||
[5] {t("menu_doc")}
|
||||
[6] {t("menu_database")}
|
||||
[7] {t("menu_process")}
|
||||
[8] {t("menu_config")}
|
||||
[9] {t("menu_network")}
|
||||
[10] {t("menu_security")}
|
||||
[11] {t("menu_test")}
|
||||
[12] {t("menu_lang")}
|
||||
[7] {t("menu_git")}
|
||||
[8] {t("menu_process")}
|
||||
[9] {t("menu_config")}
|
||||
[10] {t("menu_network")}
|
||||
[11] {t("menu_security")}
|
||||
[12] {t("menu_test")}
|
||||
[13] {t("menu_lang")}
|
||||
[0] {t("back")}
|
||||
"""
|
||||
while True:
|
||||
|
|
@ -274,26 +275,30 @@ class TODO:
|
|||
if status is not False:
|
||||
return
|
||||
elif status == "7":
|
||||
status = self.prompt_execute_process()
|
||||
status = self.prompt_execute_git()
|
||||
if status is not False:
|
||||
return
|
||||
elif status == "8":
|
||||
status = self.prompt_execute_config()
|
||||
status = self.prompt_execute_process()
|
||||
if status is not False:
|
||||
return
|
||||
elif status == "9":
|
||||
status = self.prompt_execute_network()
|
||||
status = self.prompt_execute_config()
|
||||
if status is not False:
|
||||
return
|
||||
elif status == "10":
|
||||
status = self.prompt_execute_security()
|
||||
status = self.prompt_execute_network()
|
||||
if status is not False:
|
||||
return
|
||||
elif status == "11":
|
||||
status = self.prompt_execute_test()
|
||||
status = self.prompt_execute_security()
|
||||
if status is not False:
|
||||
return
|
||||
elif status == "12":
|
||||
status = self.prompt_execute_test()
|
||||
if status is not False:
|
||||
return
|
||||
elif status == "13":
|
||||
status = self._change_language()
|
||||
if status is not False:
|
||||
return
|
||||
|
|
@ -681,6 +686,120 @@ class TODO:
|
|||
if cmd_no_found:
|
||||
print(t("cmd_not_found"))
|
||||
|
||||
def prompt_execute_git(self):
|
||||
print(f"🤖 {t('git_manage')}")
|
||||
lst_choice = [
|
||||
{"prompt_description": t("git_local_server")},
|
||||
]
|
||||
|
||||
# Append config-driven entries
|
||||
lst_config = self.config_file.get_config("git_from_makefile")
|
||||
if lst_config:
|
||||
lst_choice.extend(lst_config)
|
||||
|
||||
help_info = self.fill_help_info(lst_choice)
|
||||
|
||||
while True:
|
||||
status = click.prompt(help_info)
|
||||
print()
|
||||
if status == "0":
|
||||
return False
|
||||
elif status == "1":
|
||||
self.prompt_execute_git_local_server()
|
||||
else:
|
||||
cmd_no_found = True
|
||||
try:
|
||||
int_cmd = int(status)
|
||||
if 0 < int_cmd <= len(lst_choice):
|
||||
cmd_no_found = False
|
||||
dct_instance = lst_choice[int_cmd - 1]
|
||||
self.execute_from_configuration(dct_instance)
|
||||
except ValueError:
|
||||
pass
|
||||
if cmd_no_found:
|
||||
print(t("cmd_not_found"))
|
||||
|
||||
def prompt_execute_git_local_server(self):
|
||||
print(f"🤖 {t('git_repo_manage')}")
|
||||
lst_choice = [
|
||||
{"prompt_description": t("git_repo_deploy_local")},
|
||||
{"prompt_description": t("git_repo_deploy_production")},
|
||||
]
|
||||
help_info = self.fill_help_info(lst_choice)
|
||||
|
||||
while True:
|
||||
status = click.prompt(help_info)
|
||||
print()
|
||||
if status == "0":
|
||||
return False
|
||||
elif status == "1":
|
||||
self._prompt_git_server_actions(production_ready=False)
|
||||
elif status == "2":
|
||||
self._prompt_git_server_actions(production_ready=True)
|
||||
else:
|
||||
print(t("cmd_not_found"))
|
||||
|
||||
def _prompt_git_server_actions(self, production_ready=False):
|
||||
mode = (
|
||||
t("git_mode_production")
|
||||
if production_ready
|
||||
else t("git_mode_local")
|
||||
)
|
||||
print(f"🤖 {mode}")
|
||||
lst_choice = [
|
||||
{"prompt_description": t("git_action_all")},
|
||||
{"prompt_description": t("git_action_init")},
|
||||
{"prompt_description": t("git_action_remote")},
|
||||
{"prompt_description": t("git_action_push")},
|
||||
{"prompt_description": t("git_action_serve")},
|
||||
]
|
||||
help_info = self.fill_help_info(lst_choice)
|
||||
|
||||
while True:
|
||||
status = click.prompt(help_info)
|
||||
print()
|
||||
if status == "0":
|
||||
return False
|
||||
elif status == "1":
|
||||
self._deploy_git_server(
|
||||
production_ready=production_ready,
|
||||
action="all",
|
||||
)
|
||||
elif status == "2":
|
||||
self._deploy_git_server(
|
||||
production_ready=production_ready,
|
||||
action="init",
|
||||
)
|
||||
elif status == "3":
|
||||
self._deploy_git_server(
|
||||
production_ready=production_ready,
|
||||
action="remote",
|
||||
)
|
||||
elif status == "4":
|
||||
self._deploy_git_server(
|
||||
production_ready=production_ready,
|
||||
action="push",
|
||||
)
|
||||
elif status == "5":
|
||||
self._deploy_git_server(
|
||||
production_ready=production_ready,
|
||||
action="serve",
|
||||
)
|
||||
else:
|
||||
print(t("cmd_not_found"))
|
||||
|
||||
def _deploy_git_server(self, production_ready=False, action="all"):
|
||||
print(t("git_repo_deploy_starting"))
|
||||
cmd = (
|
||||
"python3 ./script/git/git_local_server.py -v" f" --action {action}"
|
||||
)
|
||||
if production_ready:
|
||||
cmd += " --production-ready"
|
||||
self.execute.exec_command_live(
|
||||
cmd,
|
||||
source_erplibre=False,
|
||||
)
|
||||
|
||||
def prompt_execute_doc(self):
|
||||
print(f"🤖 {t('doc_search')}")
|
||||
lst_choice = [
|
||||
|
|
@ -757,6 +876,7 @@ class TODO:
|
|||
print(f"🤖 {t('process_manage')}")
|
||||
lst_choice = [
|
||||
{"prompt_description": t("kill_process_port")},
|
||||
{"prompt_description": t("kill_git_daemon")},
|
||||
]
|
||||
help_info = self.fill_help_info(lst_choice)
|
||||
|
||||
|
|
@ -767,9 +887,18 @@ class TODO:
|
|||
return False
|
||||
elif status == "1":
|
||||
self.process_kill_from_port()
|
||||
elif status == "2":
|
||||
self.process_kill_git_daemon()
|
||||
else:
|
||||
print(t("cmd_not_found"))
|
||||
|
||||
def process_kill_git_daemon(self):
|
||||
self.execute.exec_command_live(
|
||||
"pkill -f 'git daemon'",
|
||||
source_erplibre=False,
|
||||
)
|
||||
print(t("kill_git_daemon_done"))
|
||||
|
||||
def prompt_execute_config(self):
|
||||
print(f"🤖 {t('config_manage')}")
|
||||
lst_choice = [
|
||||
|
|
|
|||
|
|
@ -304,8 +304,16 @@ TRANSLATIONS = {
|
|||
"en": "Create backup (.zip)",
|
||||
},
|
||||
"kill_process_port": {
|
||||
"fr": "Terminer le processus du port actuel",
|
||||
"en": "Kill process from actual port",
|
||||
"fr": "Terminer le processus Odoo du port actuel",
|
||||
"en": "Kill Odoo process from actual port",
|
||||
},
|
||||
"kill_git_daemon": {
|
||||
"fr": "Terminer le processus du serveur git daemon",
|
||||
"en": "Kill git daemon server process",
|
||||
},
|
||||
"kill_git_daemon_done": {
|
||||
"fr": "Processus git daemon terminé.",
|
||||
"en": "Git daemon process killed.",
|
||||
},
|
||||
"generate_all_config": {
|
||||
"fr": "Générer toute la configuration",
|
||||
|
|
@ -412,6 +420,63 @@ TRANSLATIONS = {
|
|||
"fr": "Le nom du module est requis!",
|
||||
"en": "Module name is required!",
|
||||
},
|
||||
# Git section
|
||||
"menu_git": {
|
||||
"fr": "Git - Outils Git",
|
||||
"en": "Git - Git tools",
|
||||
},
|
||||
"git_manage": {
|
||||
"fr": "Outils de gestion Git!",
|
||||
"en": "Git management tools!",
|
||||
},
|
||||
"git_local_server": {
|
||||
"fr": "Serveur git local",
|
||||
"en": "Local git server",
|
||||
},
|
||||
"git_repo_manage": {
|
||||
"fr": "Gérer le serveur de dépôts git local!",
|
||||
"en": "Manage local git repository server!",
|
||||
},
|
||||
"git_repo_deploy_local": {
|
||||
"fr": "Déployer un serveur git local (~/.git-server)",
|
||||
"en": "Deploy a local git server (~/.git-server)",
|
||||
},
|
||||
"git_repo_deploy_production": {
|
||||
"fr": "Déployer un serveur git production (/srv/git, root requis)",
|
||||
"en": "Deploy a production git server (/srv/git, root required)",
|
||||
},
|
||||
"git_repo_deploy_starting": {
|
||||
"fr": "Démarrage du déploiement du serveur git...",
|
||||
"en": "Starting git server deployment...",
|
||||
},
|
||||
"git_mode_local": {
|
||||
"fr": "Mode local (~/.git-server)",
|
||||
"en": "Local mode (~/.git-server)",
|
||||
},
|
||||
"git_mode_production": {
|
||||
"fr": "Mode production (/srv/git, root requis)",
|
||||
"en": "Production mode (/srv/git, root required)",
|
||||
},
|
||||
"git_action_all": {
|
||||
"fr": "Tout exécuter (init + remote + push + serve)",
|
||||
"en": "Run all (init + remote + push + serve)",
|
||||
},
|
||||
"git_action_init": {
|
||||
"fr": "Init - Créer les bare repos",
|
||||
"en": "Init - Create bare repos",
|
||||
},
|
||||
"git_action_remote": {
|
||||
"fr": "Remote - Ajouter les remotes locaux",
|
||||
"en": "Remote - Add local remotes",
|
||||
},
|
||||
"git_action_push": {
|
||||
"fr": "Push - Pousser vers le serveur local",
|
||||
"en": "Push - Push to local server",
|
||||
},
|
||||
"git_action_serve": {
|
||||
"fr": "Serve - Démarrer le daemon git",
|
||||
"en": "Serve - Start git daemon",
|
||||
},
|
||||
# Language selection
|
||||
"lang_prompt": {
|
||||
"fr": "Choisir la langue / Choose language",
|
||||
|
|
|
|||
Loading…
Reference in a new issue