diff --git a/README.md b/README.md index 292d5a0c..1423ad3d 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,7 @@ external-request-timeout | Use of external request method `%s` without timeout. invalid-commit | Use of cr.commit() directly - More info https://github.com/OCA/odoo-community.org/blob/master/website/Contribution/CONTRIBUTING.rst#never-commit-the-transaction | E8102 license-allowed | License "%s" not allowed in manifest file. | C8105 manifest-author-string | The author key in the manifest file must be a string (with comma separated values) | E8101 +manifest-behind-migrations | Manifest version (%s) is lower than migration scripts (%s) | E8145 manifest-data-duplicated | The file "%s" is duplicated in lines %s from manifest key "%s" | W8125 manifest-deprecated-key | Deprecated key "%s" in manifest file | C8103 manifest-maintainers-list | The maintainers key in the manifest file must be a list of strings | E8104 @@ -203,6 +204,12 @@ Checks valid only for odoo <= 13.0 - https://github.com/OCA/pylint-odoo/blob/v9.0.1/testing/resources/test_repo/broken_module3/__openerp__.py#L5 The author key in the manifest file must be a string (with comma separated values) + * manifest-behind-migrations + + - https://github.com/OCA/pylint-odoo/blob/v9.0.1/testing/resources/test_repo/broken_module2/__openerp__.py#L2 Manifest version (1.0) is lower than migration scripts (2.0) + - https://github.com/OCA/pylint-odoo/blob/v9.0.1/testing/resources/test_repo/eleven_module/__manifest__.py#L1 Manifest version (11.0.1.0.0) is lower than migration scripts (11.0.1.0.1) + - https://github.com/OCA/pylint-odoo/blob/v9.0.1/testing/resources/test_repo/test_module/__openerp__.py#L2 Manifest version (10.0.1.0.0) is lower than migration scripts (11.0.1.0.0) + * manifest-data-duplicated - https://github.com/OCA/pylint-odoo/blob/v9.0.1/testing/resources/test_repo/broken_module/__openerp__.py#L18 The file "duplicated.xml" is duplicated in lines 19 from manifest key "data" diff --git a/src/pylint_odoo/checkers/odoo_addons.py b/src/pylint_odoo/checkers/odoo_addons.py index e3cfdfb4..35687c4e 100644 --- a/src/pylint_odoo/checkers/odoo_addons.py +++ b/src/pylint_odoo/checkers/odoo_addons.py @@ -184,6 +184,11 @@ "no-raise-unlink", "Use @api.ondelete to add any constraints instead", ), + "E8145": ( + "Manifest version (%s) is lower than migration scripts (%s)", + "manifest-behind-migrations", + "Update your manifest version, otherwise the migration script won't run", + ), "F8101": ('File "%s": "%s" not found.', "resource-not-exist", CHECK_DESCRIPTION), "R8101": ( "`odoo.exceptions.Warning` is a deprecated alias to `odoo.exceptions.UserError` " @@ -556,8 +561,25 @@ class OdooAddons(OdooBaseChecker, BaseChecker): def __init__(self, linter: PyLinter): super().__init__(linter) + self._module_versions = {} + self._module_migrations = defaultdict(set) self._deprecated_odoo_methods = set() + @staticmethod + def version_greater_equal_than(original, against): + """Compare two versions and state which one is bigger than the other. + :param str original: Original version + :param str against: Value to compare against + :return: True if a >= b, otherwise False + """ + for original_val, against_val in zip(original.split("."), against.split(".")): + if int(against_val) > int(original_val): + return False + if int(original_val) > int(against_val): + return True + + return True + def close(self): """Final process get all cached values and add messages""" self.linter.config.deprecated_odoo_model_methods = set() @@ -579,6 +601,18 @@ def close(self): "consider-merging-classes-inherited", node=first_node, args=(odoo_class_inherit, ", ".join(path_nodes)) ) + if self.linter.is_message_enabled("manifest-behind-migrations"): + for module, migrations in self._module_migrations.items(): + module_version, manifest_node = self._module_versions[module] + for migration in migrations: + try: + if not self.version_greater_equal_than(module_version, migration): + self.add_message( + "manifest-behind-migrations", node=manifest_node, args=(module_version, migration) + ) + except ValueError: + continue + def visit_module(self, node): """Initizalize the cache to save the original library name of all imported node @@ -587,6 +621,13 @@ def visit_module(self, node): All these methods are these "visit_*" methods are called from pylint API """ self._from_imports = {} + basename = os.path.basename(node.file) + if basename not in {"pre-migration.py", "post-migration.py"}: + return + + migration_version = os.path.basename(os.path.dirname(node.file)) + module_name = os.path.basename(os.path.abspath(os.path.join(node.file, "..", "..", ".."))) + self._module_migrations[module_name].add(migration_version) def leave_module(self, node): """Clear variables""" @@ -1006,6 +1047,7 @@ def visit_call(self, node): "missing-readme", "resource-not-exist", "website-manifest-key-not-valid-uri", + "manifest-behind-migrations", ) def visit_dict(self, node): if not os.path.basename(self.linter.current_file) in misc.MANIFEST_FILES or not isinstance( @@ -1068,6 +1110,9 @@ def visit_dict(self, node): # Check version format version_format = manifest_dict.get("version", "") + self._module_versions[os.path.basename(os.path.dirname(self.linter.current_file))] = (version_format, node) + + # Check version format formatrgx = self.formatversion(version_format) if version_format and not formatrgx: self.add_message( diff --git a/testing/resources/test_repo/broken_module2/migrations/2.0/post-migration.py b/testing/resources/test_repo/broken_module2/migrations/2.0/post-migration.py new file mode 100644 index 00000000..e69de29b diff --git a/testing/resources/test_repo/broken_module3/migrations/8.0.1.0.2/pre-migration.py b/testing/resources/test_repo/broken_module3/migrations/8.0.1.0.2/pre-migration.py new file mode 100644 index 00000000..1fb3d21b --- /dev/null +++ b/testing/resources/test_repo/broken_module3/migrations/8.0.1.0.2/pre-migration.py @@ -0,0 +1 @@ +# Should raise manifest-behind-migrations but since manifest version is not parseable, it won't diff --git a/testing/resources/test_repo/eleven_module/migrations/11.0.1.0.1/pre-migration.py b/testing/resources/test_repo/eleven_module/migrations/11.0.1.0.1/pre-migration.py new file mode 100644 index 00000000..e69de29b diff --git a/testing/resources/test_repo/test_module/migrations/11.0.1.0.0/pre-migration.py b/testing/resources/test_repo/test_module/migrations/11.0.1.0.0/pre-migration.py new file mode 100644 index 00000000..e69de29b diff --git a/testing/resources/test_repo/twelve_module/migrations/12.0.0.0.0/pre-migration.py b/testing/resources/test_repo/twelve_module/migrations/12.0.0.0.0/pre-migration.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/test_main.py b/tests/test_main.py index 77a5c149..1cd7251d 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -67,6 +67,7 @@ "website-manifest-key-not-valid-uri": 1, "no-raise-unlink": 2, "deprecated-odoo-model-method": 2, + "manifest-behind-migrations": 3, }