From 6c2a487ef9cc7efef65abb04780a236da6521eb4 Mon Sep 17 00:00:00 2001 From: Christophe Simonis Date: Thu, 13 Apr 2023 14:25:39 +0000 Subject: [PATCH] [FIX] core: consider `x.y.z` upgrade scripts as majorless Also warn about invalid version numbers in upgrade scripts. closes odoo/odoo#118420 Related: odoo/enterprise#39698 Related: odoo/upgrade#4554 Signed-off-by: Christophe Simonis Co-authored-by: Alvaro Fuentes --- odoo/modules/migration.py | 50 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 47 insertions(+), 3 deletions(-) diff --git a/odoo/modules/migration.py b/odoo/modules/migration.py index 4100831d7ee..37081264f02 100644 --- a/odoo/modules/migration.py +++ b/odoo/modules/migration.py @@ -8,6 +8,7 @@ import glob import importlib.util import logging import os +import re from os.path import join as opj from odoo.modules.module import get_resource_path @@ -18,6 +19,33 @@ from odoo.tools.parse_version import parse_version _logger = logging.getLogger(__name__) +VERSION_RE = re.compile( + r"""^ + # Optional prefix with Odoo version + (( + 6\.1| + + # "x.0" version, with x >= 6. + [6-9]\.0| + + # multi digits "x.0" versions + [1-9]\d+\.0| + + # x.saas~y, where x >= 7 and x <= 10 + (7|8|9|10)\.saas~[1-9]\d*| + + # saas~x.y, where x >= 11 and y between 1 and 9 + # FIXME handle version >= saas~100 (expected in year 2106) + saas~(1[1-9]|[2-9]\d+)\.[1-9] + )\.)? + # After Odoo version we allow precisely 2 or 3 parts + # note this will also allow 0.0.0 which has a special meaning + \d+\.\d+(\.\d+)? + $""", + re.VERBOSE | re.ASCII, +) + + def load_script(path, module_name): full_path = get_resource_path(*path.split(os.path.sep)) if not os.path.isabs(path) else path spec = importlib.util.spec_from_file_location(module_name, full_path) @@ -71,13 +99,27 @@ class MigrationManager(object): if os.path.exists(upgrade_path): yield upgrade_path + def _verify_upgrade_version(path, version): + full_path = opj(path, version) + if not os.path.isdir(full_path): + return False + + if version == "tests": + return False + + if not VERSION_RE.match(version): + _logger.warning("Invalid version for upgrade script %r", full_path) + return False + + return True + def get_scripts(path): if not path: return {} return { version: glob.glob(opj(path, version, '*.py')) for version in os.listdir(path) - if os.path.isdir(opj(path, version)) + if _verify_upgrade_version(path, version) } for pkg in self.graph: @@ -109,8 +151,10 @@ class MigrationManager(object): return def convert_version(version): - if version.count('.') >= 2: - return version # the version number already contains the server version + if version == "0.0.0": + return version + if version.count(".") > 2: + return version # the version number already contains the server version, see VERSION_RE for details return "%s.%s" % (release.major_version, version) def _get_migration_versions(pkg, stage):