diff --git a/NEWS.rst b/NEWS.rst index ea0e6eb01497a1dca25ebe08ba784c12a2741c06_TkVXUy5yc3Q=..e9c42cce0f109ddb48aa85e12122bd17c6f06bdf_TkVXUy5yc3Q= 100644 --- a/NEWS.rst +++ b/NEWS.rst @@ -2,6 +2,11 @@ History ======= +20.0.0 +------ + +Support for Odoo 16.0 super project: add new python_packages configuration key. + 19.1.0 ------ diff --git a/odoo_scripts/conf2reST.py b/odoo_scripts/conf2reST.py index ea0e6eb01497a1dca25ebe08ba784c12a2741c06_b2Rvb19zY3JpcHRzL2NvbmYycmVTVC5weQ==..e9c42cce0f109ddb48aa85e12122bd17c6f06bdf_b2Rvb19zY3JpcHRzL2NvbmYycmVTVC5weQ== 100755 --- a/odoo_scripts/conf2reST.py +++ b/odoo_scripts/conf2reST.py @@ -6,7 +6,7 @@ import os import subprocess import sys -from typing import Dict +from typing import Dict, List, Optional import yaml @@ -15,5 +15,5 @@ _logger = logging.getLogger(__name__) -__version__ = "1.1.1" +__version__ = "1.2.0" __date__ = "2016-07-15" @@ -19,4 +19,4 @@ __date__ = "2016-07-15" -__updated__ = "2022-12-16" +__updated__ = "2023-03-06" @@ -21,4 +21,4 @@ -def main(argv=None): # IGNORE:C0111 +def main(argv: Optional[List[str]] = None) -> int: """Parse arguments and launch conversion""" @@ -24,6 +24,5 @@ """Parse arguments and launch conversion""" - if argv is None: - argv = sys.argv - else: - sys.argv.extend(argv) + program_version_message = f"%(prog)s {__version__} ({__updated__})" + program_shortdesc = __doc__.split(".", maxsplit=1)[0] + program_license = f"""{program_shortdesc} @@ -29,15 +28,6 @@ - program_version = __version__ - program_build_date = str(__updated__) - program_version_message = "%%(prog)s %s (%s)" % ( - program_version, - program_build_date, - ) - program_shortdesc = __doc__.split("\n", maxsplit=2)[1] - program_license = """%s - - Created by Vincent Hatakeyama on %s. - Copyright 2016, 2019, 2020, 2022 XCG Consulting. All rights reserved. + Created by Vincent Hatakeyama on {__date__}. + Copyright 2016, 2019, 2020, 2022, 2023 XCG Consulting. All rights reserved. Licensed under the MIT License @@ -45,10 +35,7 @@ or conditions of any kind, either express or implied. USAGE -""" % ( - program_shortdesc, - str(__date__), - ) +""" # Argument parsing parser = basic_parser(description=program_license, version=program_version_message) parser.add_argument( @@ -59,6 +46,6 @@ ) parser.add_argument("-o", "--output", help="Output file name") - nmspc = parser.parse_args() + nmspc = parser.parse_args(argv) apply(nmspc) conf2rst(nmspc.directory, nmspc.output) @@ -63,5 +50,6 @@ apply(nmspc) conf2rst(nmspc.directory, nmspc.output) + return 0 def conf2rst(directory: str, output=None): @@ -74,15 +62,4 @@ configuration = Configuration(directory) addon_dirs = configuration.addons_path - # pip3 freeze - reqs = {} - pip_freeze = subprocess.check_output(["pip3", "freeze"]) - for element in pip_freeze.split(): - lib_and_version = element.decode("utf-8").split("==") - if len(lib_and_version) == 1: # No explicit version (no ==). - library, version = lib_and_version[0], "N/A" - else: - library, version = lib_and_version - reqs[library] = {"version": version} - rst: Dict[str, Dict[str, Dict[str, str]]] = { @@ -88,7 +65,7 @@ rst: Dict[str, Dict[str, Dict[str, str]]] = { - "python": reqs, + "python": {}, "tools": {}, "modules": {}, "other": {}, "group of modules": {}, } @@ -90,8 +67,35 @@ "tools": {}, "modules": {}, "other": {}, "group of modules": {}, } + # pip3 freeze + pip_freeze = subprocess.check_output(["pip3", "freeze"]) + for element in pip_freeze.split(b"\n"): + line = element.decode("utf-8") + if line: + if " @ " in line: + library, version = line.split(" @ ") + elif line.startswith("-e "): + version, library = line[3:].split("#egg=") + else: + lib_and_version = line.split("==") + if len(lib_and_version) == 1: # No explicit version (no ==). + library, version = lib_and_version[0], "N/A" + else: + library, version = lib_and_version + if library.startswith("odoo-addon"): + section = "modules" + else: + section = "python" + if version.startswith("file:///"): + # In the case of complicated name, the replace call is not enough + version_split = version.split(library.replace("-", "_")) + if len(version_split) > 1: + version = version_split[1][1:].split("-py")[0] + + rst[section][library] = {"version": version} + # load the conf file # test if there is a .hgconf file hgconf_path = os.path.join(directory, ".hgconf") @@ -192,4 +196,4 @@ if __name__ == "__main__": - main() + sys.exit(main()) diff --git a/odoo_scripts/config.py b/odoo_scripts/config.py index ea0e6eb01497a1dca25ebe08ba784c12a2741c06_b2Rvb19zY3JpcHRzL2NvbmZpZy5weQ==..e9c42cce0f109ddb48aa85e12122bd17c6f06bdf_b2Rvb19zY3JpcHRzL2NvbmZpZy5weQ== 100644 --- a/odoo_scripts/config.py +++ b/odoo_scripts/config.py @@ -5,7 +5,8 @@ import os import re import sys -from collections import defaultdict +from ast import literal_eval +from collections import OrderedDict, defaultdict from glob import glob from typing import Any, Dict, List, Optional, Union @@ -148,6 +149,27 @@ ) setattr(self, key, list(set_values)) + self.python_packages: Dict[str, Dict[str, str]] + if toread("python_packages"): + self.python_packages = OrderedDict() + # add any expanded values + for config in self._expanded_configuration.values(): + if hasattr(config, "python_packages"): + for python_package in config.python_packages: + self.python_packages[python_package] = config.python_packages[ + python_package + ] + for key, value in literal_eval( + section.get("python_packages", "{}") + ).items(): + value_with_default = { + "mount": ".", + "target": os.path.join("odoo", "addons", os.path.basename(key)), + "compile": "True", + } + value_with_default.update(value) + self.python_packages[key] = value_with_default + self.addons_path = {os.path.dirname(module) for module in self.modules} """Set of directory containing modules""" diff --git a/odoo_scripts/docker_build.py b/odoo_scripts/docker_build.py index ea0e6eb01497a1dca25ebe08ba784c12a2741c06_b2Rvb19zY3JpcHRzL2RvY2tlcl9idWlsZC5weQ==..e9c42cce0f109ddb48aa85e12122bd17c6f06bdf_b2Rvb19zY3JpcHRzL2RvY2tlcl9idWlsZC5weQ== 100644 --- a/odoo_scripts/docker_build.py +++ b/odoo_scripts/docker_build.py @@ -19,5 +19,5 @@ _logger = logging.getLogger(__name__) -__version__ = "1.3.0" +__version__ = "1.4.0" __date__ = "2018-04-04" @@ -23,5 +23,5 @@ __date__ = "2018-04-04" -__updated__ = "2023-02-27" +__updated__ = "2023-03-02" def add_build_options(parser: argparse.ArgumentParser): @@ -140,4 +140,16 @@ buildargs = {} if os.path.exists(".hg"): + description = check_output( + [ + "hg", + "log", + "-r", + ".", + "-T", + "{latesttag}{sub('^-0-.*', '', '-{latesttagdistance}-h{node|short}')}", + ] + ).decode() + buildargs["VERSION"] = description + buildargs["SENTRY_RELEASE_ARG"] = description tags_string = check_output(["hg", "identify", "--tags"]).decode() @@ -143,8 +155,6 @@ tags_string = check_output(["hg", "identify", "--tags"]).decode() - buildargs["VERSION"] = tags_string - buildargs["SENTRY_RELEASE_ARG"] = tags_string if tags_string: tags = tags_string.split() buildargs["VCS_URL"] = check_output(["hg", "paths", "default"]).decode() buildargs["VCS_REF"] = check_output(["hg", "identify", "--id"]).decode() elif os.path.exists(".git"): @@ -146,6 +156,9 @@ if tags_string: tags = tags_string.split() buildargs["VCS_URL"] = check_output(["hg", "paths", "default"]).decode() buildargs["VCS_REF"] = check_output(["hg", "identify", "--id"]).decode() elif os.path.exists(".git"): + description = check_output(["git", "describe"]).decode() + buildargs["VERSION"] = description + buildargs["SENTRY_RELEASE_ARG"] = description tags_string = check_output(["git", "describe", "--tags"]).decode() @@ -151,6 +164,4 @@ tags_string = check_output(["git", "describe", "--tags"]).decode() - buildargs["VERSION"] = tags_string - buildargs["SENTRY_RELEASE_ARG"] = tags_string if tags_string: tags = tags_string.split() buildargs["BUILD_DATE"] = datetime.datetime.now().isoformat() diff --git a/odoo_scripts/docker_build_clean.py b/odoo_scripts/docker_build_clean.py index ea0e6eb01497a1dca25ebe08ba784c12a2741c06_b2Rvb19zY3JpcHRzL2RvY2tlcl9idWlsZF9jbGVhbi5weQ==..e9c42cce0f109ddb48aa85e12122bd17c6f06bdf_b2Rvb19zY3JpcHRzL2RvY2tlcl9idWlsZF9jbGVhbi5weQ== 100644 --- a/odoo_scripts/docker_build_clean.py +++ b/odoo_scripts/docker_build_clean.py @@ -7,8 +7,9 @@ import sys from typing import List, Optional +from .docker_build_copy import PYTHON_PACKAGES_DIR from .list_modules import MODULES_LIST_FILE from .parsing import apply, basic_parser _logger = logging.getLogger(__name__) @@ -10,7 +11,7 @@ from .list_modules import MODULES_LIST_FILE from .parsing import apply, basic_parser _logger = logging.getLogger(__name__) -__version__ = "2.0.0" +__version__ = "2.1.0" __date__ = "2020-06-30" @@ -16,5 +17,5 @@ __date__ = "2020-06-30" -__updated__ = "2021-12-10" +__updated__ = "2023-02-27" def __parser(): @@ -18,12 +19,7 @@ def __parser(): - program_version = __version__ - program_build_date = str(__updated__) - program_version_message = "%%(prog)s %s (%s)" % ( - program_version, - program_build_date, - ) - program_shortdesc = __doc__.split(".", maxsplit=1)[0] - program_license = """%s + program_version_message = f"%(prog)s {__version__} ({__updated__})" + program_short_description = __doc__.split(".", maxsplit=1)[0] + program_license = f"""{program_short_description} @@ -29,6 +25,6 @@ - Created by Vincent Hatakeyama on %s. - Copyright 2020, 2021 XCG Consulting. All rights reserved. + Created by Vincent Hatakeyama on {__date__}. + Copyright 2020, 2021, 2023 XCG Consulting. All rights reserved. Licensed under the MIT License @@ -36,10 +32,7 @@ or conditions of any kind, either express or implied. USAGE - """ % ( - program_shortdesc, - str(__date__), - ) + """ parser = basic_parser(program_license, program_version_message) return parser @@ -58,7 +51,7 @@ """Clean up after a build""" if os.path.exists(MODULES_LIST_FILE): os.remove(MODULES_LIST_FILE) - for directory in ("odoo_modules", "odoo_setup"): + for directory in ("odoo_modules", "odoo_setup", PYTHON_PACKAGES_DIR): if os.path.exists(directory): shutil.rmtree(directory) diff --git a/odoo_scripts/docker_build_copy.py b/odoo_scripts/docker_build_copy.py index ea0e6eb01497a1dca25ebe08ba784c12a2741c06_b2Rvb19zY3JpcHRzL2RvY2tlcl9idWlsZF9jb3B5LnB5..e9c42cce0f109ddb48aa85e12122bd17c6f06bdf_b2Rvb19zY3JpcHRzL2RvY2tlcl9idWlsZF9jb3B5LnB5 100644 --- a/odoo_scripts/docker_build_copy.py +++ b/odoo_scripts/docker_build_copy.py @@ -4,7 +4,7 @@ import logging import os import sys -from subprocess import call +from subprocess import check_call from typing import List, Optional from .config import Config @@ -13,5 +13,5 @@ _logger = logging.getLogger(__name__) -__version__ = "2.0.0" +__version__ = "2.1.0" __date__ = "2020-06-30" @@ -17,5 +17,7 @@ __date__ = "2020-06-30" -__updated__ = "2021-12-10" +__updated__ = "2023-02-28" + +PYTHON_PACKAGES_DIR = "python_packages" def __parser(): @@ -19,10 +21,5 @@ def __parser(): - program_version = __version__ - program_build_date = str(__updated__) - program_version_message = "%%(prog)s %s (%s)" % ( - program_version, - program_build_date, - ) + program_version_message = f"%(prog)s {__version__} ({__updated__})" program_shortdesc = __doc__.split(".", maxsplit=1)[0] @@ -28,3 +25,3 @@ program_shortdesc = __doc__.split(".", maxsplit=1)[0] - program_license = """%s + program_license = f"""{program_shortdesc} @@ -30,6 +27,6 @@ - Created by Vincent Hatakeyama on %s. - Copyright 2020, 2021 XCG Consulting. All rights reserved. + Created by Vincent Hatakeyama on {__date__}. + Copyright 2020, 2021, 2023 XCG Consulting. All rights reserved. Licensed under the MIT License @@ -37,10 +34,7 @@ or conditions of any kind, either express or implied. USAGE - """ % ( - program_shortdesc, - str(__date__), - ) + """ parser = basic_parser(program_license, program_version_message) return parser @@ -54,7 +48,11 @@ return 0 +def _target_path(path: str) -> str: + return path.replace(os.path.sep, "-") + + def copy(): """Copy modules for a build""" c = Config() modules = [os.path.realpath(module) for module in c.modules] @@ -57,6 +55,5 @@ def copy(): """Copy modules for a build""" c = Config() modules = [os.path.realpath(module) for module in c.modules] - # copy files target = "odoo_modules" @@ -62,5 +59,4 @@ target = "odoo_modules" - _logger.info("Copying Odoo modules files to %s", target) if not os.path.exists(target): os.mkdir(target) cmd = ( @@ -80,7 +76,83 @@ + [target] ) _logger.debug(" ".join(cmd)) - call(cmd) + check_call(cmd) + + target = PYTHON_PACKAGES_DIR + if not os.path.exists(PYTHON_PACKAGES_DIR): + os.mkdir(target) + copy_paths = set() + packages_to_compile = [] + requirements = [] + # Try to include the CVS information in target so that setuptools-scm and + # setuptools-odoo work as expected + for package, options in c.python_packages.items(): + if options["compile"] in ("True", "true"): + packages_to_compile.append(package) + else: + cvs_parent = package + package_path_in_target = "" + while not ( + os.path.exists(os.path.join(cvs_parent, ".git")) + or os.path.exists(os.path.join(cvs_parent, ".hg")) + or os.path.exists(os.path.join(cvs_parent, ".hg_archival.txt")) + or os.path.exists(os.path.join(cvs_parent, ".git_archival.txt")) + ): + package_path_in_target = os.path.join( + os.path.basename(cvs_parent), package_path_in_target + ) + if os.path.dirname(cvs_parent) == "": + # stop at one level in the super project + break + cvs_parent = os.path.dirname(cvs_parent) + copy_paths.add(cvs_parent) + requirements.append( + os.path.join(target, _target_path(cvs_parent), package_path_in_target) + ) + for copy_path in copy_paths: + src, dest = copy_path + os.path.sep, os.path.join( + target, _target_path(copy_path) + ) + _logger.info("Copying package %s to %s", src, dest) + if not os.path.exists(target): + os.mkdir(target) + cmd = [ + "rsync", + "--delete", + "--include=core", + "--copy-links", + "--exclude='*.pyc'", + "-r", + "--times", + src, + dest, + ] + _logger.debug(" ".join(cmd)) + check_call(cmd) + # The option to ignore the python verion can be an issue with newer syntax. + # In the CI it will work fine, locally, maybe add an option to avoid compiling? + if packages_to_compile: + cmd = [ + sys.executable, + "-m", + "pip", + "wheel", + "--no-deps", + "-w", + target, + "--ignore-requires-python", + ] + packages_to_compile + _logger.debug(" ".join(cmd)) + check_call(cmd) + for entry in os.listdir(target): + if entry.endswith(".whl"): + requirements.append(os.path.join(".", target, entry)) + # Write requirements + # pip needs to be run with pip install -r <target>/requirements for the path in the + # file to be valid + with open(os.path.join(target, "requirements"), "wt") as f: + f.write("\n".join(requirements)) + # copy setup files to odoo_setup source = "setup/" target = "odoo_setup" @@ -101,7 +173,7 @@ "--times", ] + [source, target] _logger.debug(" ".join(cmd)) - call(cmd) + check_call(cmd) list_modules() diff --git a/odoo_scripts/docker_client.py b/odoo_scripts/docker_client.py index ea0e6eb01497a1dca25ebe08ba784c12a2741c06_b2Rvb19zY3JpcHRzL2RvY2tlcl9jbGllbnQucHk=..e9c42cce0f109ddb48aa85e12122bd17c6f06bdf_b2Rvb19zY3JpcHRzL2RvY2tlcl9jbGllbnQucHk= 100644 --- a/odoo_scripts/docker_client.py +++ b/odoo_scripts/docker_client.py @@ -241,5 +241,6 @@ mount_list: List[Mount] = [] mount_dict: Dict[str, str] = {} if config.odoo_type in (ODOO_7, ODOO_8, ODOO_9, ODOO_10): + base_python_path = "/usr/local/lib/python2.7/dist-packages/" target_path = "/opt/odoo/sources/odoo/addons" else: @@ -244,5 +245,6 @@ target_path = "/opt/odoo/sources/odoo/addons" else: + base_python_path = os.path.dirname(get_odoo_base_path(config)) target_path = get_odoo_base_path(config) + "/addons" for module in modules: @@ -251,6 +253,15 @@ mount_list.append(Mount(full_target_path, full_project_path, "bind")) mount_dict[full_target_path] = full_project_path + _logger.debug("python packages: %s", ",".join(config.python_packages)) + for python_package in config.python_packages.items(): + full_target_path = os.path.join(base_python_path, python_package[1]["target"]) + full_project_path = os.path.realpath( + os.path.join(project_path, python_package[0], python_package[1]["mount"]) + ) + mount_list.append(Mount(full_target_path, full_project_path, "bind")) + mount_dict[full_target_path] = full_project_path + return mount_list, mount_dict @@ -276,6 +287,7 @@ read_only=True, ) ) + _logger.info("mounts len: %s", len(mounts)) return mounts diff --git a/odoo_scripts/update_duplicate_sources.py b/odoo_scripts/update_duplicate_sources.py index ea0e6eb01497a1dca25ebe08ba784c12a2741c06_b2Rvb19zY3JpcHRzL3VwZGF0ZV9kdXBsaWNhdGVfc291cmNlcy5weQ==..e9c42cce0f109ddb48aa85e12122bd17c6f06bdf_b2Rvb19zY3JpcHRzL3VwZGF0ZV9kdXBsaWNhdGVfc291cmNlcy5weQ== 100644 --- a/odoo_scripts/update_duplicate_sources.py +++ b/odoo_scripts/update_duplicate_sources.py @@ -15,5 +15,5 @@ _logger = logging.getLogger(__name__) -__version__ = "1.1.0" +__version__ = "1.2.0" __date__ = "2020-06-19" @@ -19,5 +19,5 @@ __date__ = "2020-06-19" -__updated__ = "2021-10-01" +__updated__ = "2023-02-27" def __parser(): @@ -31,7 +31,7 @@ program_license = """%s Created by Vincent Hatakeyama on %s. - Copyright 2018, 2020, 2021 XCG Consulting. All rights reserved. + Copyright 2018, 2020, 2021, 2023 XCG Consulting. All rights reserved. Licensed under the MIT License @@ -135,6 +135,7 @@ + conf.modules + conf.dependencies + conf.other_sources + + list(conf.python_packages.keys()) + [duplicate_destination.decode()] ) _logger.debug(" ".join(cmd))