erplibre/script/git/git_merge_repo_manifest.py
Mathieu Benoit 32315a2dba [ADD] installation: add optional extra modules (CybroOdoo)
CybroOdoo repos are large and slow to clone, making them unsuitable
for default installation. Moves them to opt-in per-version extra
manifests, introduces .erplibre-state.json to track installation
options per Odoo version, and surfaces the choice in the TODO CLI
sub-menu. Switch auto-detects extra from state and warns when no
state is recorded.

Generated by Claude Code 2.1.87 model claude-sonnet-4-6

Co-Authored-By: Mathieu Benoit <mathben@technolibre.ca>
2026-05-19 15:17:45 -04:00

239 lines
8.1 KiB
Python
Executable file

#!/usr/bin/env python3
# © 2021-2026 TechnoLibre (http://www.technolibre.ca)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
import argparse
import copy
import csv
import logging
import os
import sys
new_path = os.path.normpath(
os.path.join(os.path.dirname(__file__), "..", "..")
)
sys.path.append(new_path)
from script.git.git_tool import GitTool
from script.version.erplibre_state import get_version_extra
_logger = logging.getLogger(__name__)
DEFAULT_PATH_MANIFEST_CONF = os.path.join("conf", "git_manifest.csv")
DEFAULT_PATH_MANIFEST_MOBILE_CONF = os.path.join(
"conf", "git_manifest_mobile.csv"
)
DEFAULT_PATH_MANIFEST_ODOO_CONF = os.path.join("conf", "git_manifest_odoo.csv")
DEFAULT_PATH_MANIFEST_PRIVATE_CONF = os.path.join(
"private", "default_git_manifest.csv"
)
DEFAULT_PATH_INSTALLED_ODOO_VERSION = os.path.join(
".repo", "installed_odoo_version.txt"
)
MOBILE_PATH = os.path.join("mobile", "erplibre_home_mobile")
ODOO_VERSION_PATH = ".odoo-version"
def get_config():
"""Parse command line arguments, extracting the config file name,
returning the union of config file and command line arguments
:return: dict of config file settings and command line arguments
"""
# TODO update description
parser = argparse.ArgumentParser(
formatter_class=argparse.RawDescriptionHelpFormatter,
description="""Replace revision field in input2 from input1 if existing, create an output of new manifest.""",
epilog="""\
""",
)
parser.add_argument(
"--input",
help="First manifest to merge into input2. Second manifest, overwrite by input1.",
)
parser.add_argument(
"--output",
default=".repo/local_manifests/erplibre_manifest.xml",
help="Output of new manifest",
)
parser.add_argument(
"--att_revision_only",
action="store_true",
help="Output of new manifest",
)
parser.add_argument(
"--with_OCA", action="store_true", help="Add OCA manifest"
)
parser.add_argument(
"--with_mobile",
action="store_true",
help="Add mobile project manifest",
)
parser.add_argument(
"--with_extra",
action="store_true",
help=(
"Add extra modules manifest for current Odoo version"
" (e.g. CybroOdoo). Version read from .odoo-version."
),
)
parser.add_argument(
"--with_new_manifest",
action="store_true",
help="Will overwrite local manifest",
)
args = parser.parse_args()
return args
def main():
config = get_config()
git_tool = GitTool()
odoo_version = None
if os.path.isfile(ODOO_VERSION_PATH):
with open(ODOO_VERSION_PATH, "r") as f:
odoo_version = f.readline()
# Add local manifest
if not config.with_new_manifest:
if odoo_version:
config.with_OCA = True
if os.path.isdir(MOBILE_PATH):
config.with_mobile = True
if odoo_version and get_version_extra(odoo_version.strip()):
config.with_extra = True
input_paths = config.input
if not input_paths:
input_paths = []
if config.with_OCA:
append_file_path_manifest(
input_paths, DEFAULT_PATH_MANIFEST_ODOO_CONF
)
if os.path.exists(".odoo-version"):
if odoo_version:
path_manifest_odoo_version = os.path.join(
"manifest", f"git_manifest_odoo{odoo_version}.xml"
)
if os.path.exists(path_manifest_odoo_version):
input_paths.append(path_manifest_odoo_version)
else:
print(
f"ERROR: {path_manifest_odoo_version} does not exist"
)
path_manifest_odoo_version = os.path.join(
"manifest", f"git_manifest_odoo{odoo_version}_dev.xml"
)
if os.path.exists(path_manifest_odoo_version):
input_paths.append(path_manifest_odoo_version)
if os.path.exists(DEFAULT_PATH_INSTALLED_ODOO_VERSION):
with open(DEFAULT_PATH_INSTALLED_ODOO_VERSION, "r") as f:
installed_versions = [a.strip() for a in f.readlines()]
if installed_versions:
for installed_odoo_version in installed_versions:
path_manifest_odoo_version = os.path.join(
"manifest",
f"git_manifest_{installed_odoo_version}.xml",
)
if os.path.exists(path_manifest_odoo_version):
input_paths.append(path_manifest_odoo_version)
else:
print(
f"ERROR: {path_manifest_odoo_version} does not exist"
)
path_manifest_odoo_version = os.path.join(
"manifest",
f"git_manifest_{installed_odoo_version}_dev.xml",
)
if os.path.exists(path_manifest_odoo_version):
input_paths.append(path_manifest_odoo_version)
if config.with_mobile:
append_file_path_manifest(
input_paths, DEFAULT_PATH_MANIFEST_MOBILE_CONF
)
if config.with_extra and odoo_version:
path_extra = os.path.join(
"manifest",
f"git_manifest_extra_odoo{odoo_version.strip()}.xml",
)
if os.path.exists(path_extra):
input_paths.append(path_extra)
else:
print(f"WARNING: {path_extra} does not exist, skipping extra")
if not config.with_mobile or not config.with_OCA:
append_file_path_manifest(input_paths, DEFAULT_PATH_MANIFEST_CONF)
append_file_path_manifest(
input_paths, DEFAULT_PATH_MANIFEST_PRIVATE_CONF
)
remotes_total = {}
projects_total = {}
default_remote_total = None
# Be sure all input is unique
input_paths = list(set(input_paths))
for index, input_path in enumerate(input_paths):
(
remotes,
projects,
default_remote,
) = git_tool.get_manifest_xml_info(filename=input_path, add_root=True)
# Support multiple version odoo
projects_copy = projects
projects = {}
for key, value in projects_copy.items():
new_key = f"{key}+{value.get('@path')}"
projects[new_key] = value
if len(input_paths) == 1:
# Only 1 input, same output
remotes_total = remotes
projects_total = projects
break
elif not index:
# Preparation to accumulate data
remotes_total = copy.deepcopy(remotes)
projects_total = copy.deepcopy(projects)
continue
for key, value in projects.items():
if key in projects_total:
if config.att_revision_only:
revision = value.get("@revision")
if revision:
projects_total[key]["@revision"] = revision
else:
projects_total[key].update(value)
else:
projects_total[key] = copy.deepcopy(value)
for key, value in remotes.items():
if key in remotes_total:
remotes_total[key].update(value)
else:
remotes_total[key] = copy.deepcopy(value)
git_tool.generate_repo_manifest(
remotes_config=remotes_total,
projects_config=projects_total,
output=config.output,
default_remote=default_remote_total,
)
def append_file_path_manifest(input_paths, path_manifest):
if os.path.exists(path_manifest):
with open(path_manifest, "r") as f:
csv_file = csv.DictReader(f)
for row in csv_file:
filepath = row.get("filepath")
input_paths.append(filepath)
if __name__ == "__main__":
main()