From 2c25f45ac98cbe5ce195431b27bdf259031cbf0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Bidoul=20=28ACSONE=29?= Date: Fri, 5 Oct 2018 18:12:50 +0200 Subject: [PATCH] [MIG] module_auto_update from 11 to 12 Use tagged to mark post install tests as previous mechanism seems to be broken. --- module_auto_update/__init__.py | 1 - module_auto_update/__manifest__.py | 5 +- .../data/cron_data_deprecated.xml | 20 -- module_auto_update/hooks.py | 6 - .../migrations/10.0.2.0.0/pre-migrate.py | 24 -- module_auto_update/models/__init__.py | 1 - .../models/module_deprecated.py | 69 ------ module_auto_update/readme/ROADMAP.rst | 17 -- module_auto_update/tests/__init__.py | 2 - module_auto_update/tests/test_module.py | 7 +- .../tests/test_module_deprecated.py | 209 ------------------ .../tests/test_module_upgrade_deprecated.py | 46 ---- module_auto_update/wizards/__init__.py | 3 - .../wizards/module_upgrade_deprecated.py | 84 ------- setup/.setuptools-odoo-make-default-ignore | 2 + setup/README | 2 + .../odoo/addons/module_auto_update | 1 + setup/module_auto_update/setup.py | 6 + 18 files changed, 15 insertions(+), 490 deletions(-) delete mode 100644 module_auto_update/data/cron_data_deprecated.xml delete mode 100644 module_auto_update/migrations/10.0.2.0.0/pre-migrate.py delete mode 100644 module_auto_update/models/module_deprecated.py delete mode 100644 module_auto_update/readme/ROADMAP.rst delete mode 100644 module_auto_update/tests/test_module_deprecated.py delete mode 100644 module_auto_update/tests/test_module_upgrade_deprecated.py delete mode 100644 module_auto_update/wizards/__init__.py delete mode 100644 module_auto_update/wizards/module_upgrade_deprecated.py create mode 100644 setup/.setuptools-odoo-make-default-ignore create mode 100644 setup/README create mode 120000 setup/module_auto_update/odoo/addons/module_auto_update create mode 100644 setup/module_auto_update/setup.py diff --git a/module_auto_update/__init__.py b/module_auto_update/__init__.py index a0c82fd06..d2ad125af 100644 --- a/module_auto_update/__init__.py +++ b/module_auto_update/__init__.py @@ -1,5 +1,4 @@ # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). from . import models -from . import wizards from .hooks import uninstall_hook diff --git a/module_auto_update/__manifest__.py b/module_auto_update/__manifest__.py index 0b2ff5e2c..b0b0c5033 100644 --- a/module_auto_update/__manifest__.py +++ b/module_auto_update/__manifest__.py @@ -4,7 +4,7 @@ { 'name': 'Module Auto Update', 'summary': 'Automatically update Odoo modules', - 'version': '11.0.2.0.4', + 'version': '12.0.2.0.4', 'category': 'Extra Tools', 'website': 'https://github.com/OCA/server-tools', 'author': 'LasLabs, ' @@ -19,9 +19,6 @@ 'depends': [ 'base', ], - 'data': [ - 'data/cron_data_deprecated.xml', - ], 'development_status': 'Production/Stable', 'maintainers': ['sbidoul'], } diff --git a/module_auto_update/data/cron_data_deprecated.xml b/module_auto_update/data/cron_data_deprecated.xml deleted file mode 100644 index d903dbda6..000000000 --- a/module_auto_update/data/cron_data_deprecated.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - Perform Module Upgrades - - - 1 - days - -1 - - - code - model.upgrade_module() - - - diff --git a/module_auto_update/hooks.py b/module_auto_update/hooks.py index cd161a246..50b2eabf6 100644 --- a/module_auto_update/hooks.py +++ b/module_auto_update/hooks.py @@ -4,14 +4,8 @@ from odoo import SUPERUSER_ID, api from .models.module import PARAM_INSTALLED_CHECKSUMS -from .models.module_deprecated import PARAM_DEPRECATED def uninstall_hook(cr, registry): env = api.Environment(cr, SUPERUSER_ID, {}) env["ir.config_parameter"].set_param(PARAM_INSTALLED_CHECKSUMS, False) - # TODO Remove from here when removing deprecated features - env["ir.config_parameter"].set_param(PARAM_DEPRECATED, False) - prefix = "module_auto_update.field_ir_module_module_checksum_%s" - fields = env.ref(prefix % "dir") | env.ref(prefix % "installed") - fields.with_context(_force_unlink=True).unlink() diff --git a/module_auto_update/migrations/10.0.2.0.0/pre-migrate.py b/module_auto_update/migrations/10.0.2.0.0/pre-migrate.py deleted file mode 100644 index da98ab3ca..000000000 --- a/module_auto_update/migrations/10.0.2.0.0/pre-migrate.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright 2018 Tecnativa - Jairo Llopis -# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl). -import logging -from psycopg2 import IntegrityError -from odoo.addons.module_auto_update.models.module_deprecated import \ - PARAM_DEPRECATED - -_logger = logging.getLogger(__name__) - - -def migrate(cr, version): - """Autoenable deprecated behavior.""" - try: - with cr.savepoint(): - cr.execute( - """INSERT INTO ir_config_parameter (key, value) - VALUES (%s, '1')""", - (PARAM_DEPRECATED,) - ) - _logger.warn("Deprecated features have been autoenabled, see " - "addon's README to know how to upgrade to the new " - "supported autoupdate mechanism.") - except IntegrityError: - _logger.info("Deprecated features setting exists, not autoenabling") diff --git a/module_auto_update/models/__init__.py b/module_auto_update/models/__init__.py index 53c05a539..e5ee3ea66 100644 --- a/module_auto_update/models/__init__.py +++ b/module_auto_update/models/__init__.py @@ -1,4 +1,3 @@ # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). from . import module -from . import module_deprecated diff --git a/module_auto_update/models/module_deprecated.py b/module_auto_update/models/module_deprecated.py deleted file mode 100644 index 7383a8ac5..000000000 --- a/module_auto_update/models/module_deprecated.py +++ /dev/null @@ -1,69 +0,0 @@ -# Copyright 2017 LasLabs Inc. -# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). -# pylint: disable=consider-merging-classes-inherited - -from odoo import api, fields, models - -PARAM_DEPRECATED = "module_auto_update.enable_deprecated" - - -class Module(models.Model): - _inherit = 'ir.module.module' - - checksum_dir = fields.Char( - deprecated=True, - compute='_compute_checksum_dir', - ) - checksum_installed = fields.Char( - deprecated=True, - compute='_compute_checksum_installed', - inverse='_inverse_checksum_installed', - store=False, - ) - - @api.depends('name') - def _compute_checksum_dir(self): - for rec in self: - rec.checksum_dir = rec._get_checksum_dir() - - def _compute_checksum_installed(self): - saved_checksums = self._get_saved_checksums() - for rec in self: - rec.checksum_installed = saved_checksums.get(rec.name, False) - - def _inverse_checksum_installed(self): - checksums = self._get_saved_checksums() - for rec in self: - checksums[rec.name] = rec.checksum_installed - self._save_checksums(checksums) - - @api.multi - def _store_checksum_installed(self, vals): - """Store the right installed checksum, if addon is installed.""" - if not self.env["base.module.upgrade"]._autoupdate_deprecated(): - # Skip if deprecated features are disabled - return - if 'checksum_installed' not in vals: - try: - version = vals["latest_version"] - except KeyError: - return # Not [un]installing/updating any addon - if version is False: - # Uninstalling - self.write({'checksum_installed': False}) - else: - # Installing or updating - for one in self: - one.checksum_installed = one.checksum_dir - - @api.model - def create(self, vals): - res = super(Module, self).create(vals) - res._store_checksum_installed(vals) - return res - - @api.multi - def write(self, vals): - res = super(Module, self).write(vals) - self._store_checksum_installed(vals) - return res diff --git a/module_auto_update/readme/ROADMAP.rst b/module_auto_update/readme/ROADMAP.rst deleted file mode 100644 index 19dad6fdc..000000000 --- a/module_auto_update/readme/ROADMAP.rst +++ /dev/null @@ -1,17 +0,0 @@ -* Since version ``2.0.0``, some features have been deprecated. - When you upgrade from previous versions, these features will be kept for - backwards compatibility, but beware! They are buggy! - - If you install this addon from scratch, these features are disabled by - default. - - To force enabling or disabling the deprecated features, set a configuration - parameter called ``module_auto_update.enable_deprecated`` to either ``1`` - or ``0``. It is recommended that you disable them. - - Keep in mind that from this version, all upgrades are assumed to run in a - separate odoo instance, dedicated exclusively to upgrade Odoo. - -* When migrating the addon to new versions, the deprecated features should be - removed. To make it simple all deprecated features are found in files - suffixed with ``_deprecated``. diff --git a/module_auto_update/tests/__init__.py b/module_auto_update/tests/__init__.py index 54f7cc144..82778565d 100644 --- a/module_auto_update/tests/__init__.py +++ b/module_auto_update/tests/__init__.py @@ -2,5 +2,3 @@ from . import test_addon_hash from . import test_module -from . import test_module_deprecated -from . import test_module_upgrade_deprecated diff --git a/module_auto_update/tests/test_module.py b/module_auto_update/tests/test_module.py index e424f7701..f2d9486bf 100644 --- a/module_auto_update/tests/test_module.py +++ b/module_auto_update/tests/test_module.py @@ -7,9 +7,9 @@ import tempfile import mock +import odoo from odoo.modules import get_module_path -from odoo.tests import common -from odoo.tests.common import TransactionCase +from odoo.tests import TransactionCase from ..addon_hash import addon_hash from ..models.module import IncompleteUpgradeError, DEFAULT_EXCLUDE_PATTERNS @@ -79,8 +79,7 @@ class TestModule(TransactionCase): self.assertFalse(Imm._get_modules_with_changed_checksum()) -@common.at_install(False) -@common.post_install(True) +@odoo.tests.tagged('post_install', '-at_install') class TestModuleAfterInstall(TransactionCase): def setUp(self): diff --git a/module_auto_update/tests/test_module_deprecated.py b/module_auto_update/tests/test_module_deprecated.py deleted file mode 100644 index ca0434fc8..000000000 --- a/module_auto_update/tests/test_module_deprecated.py +++ /dev/null @@ -1,209 +0,0 @@ -# Copyright 2017 LasLabs Inc. -# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). - -import os - -import mock - -from odoo.modules import get_module_path -from odoo.tests.common import TransactionCase -from odoo.tools import mute_logger - -from .. addon_hash import addon_hash -from ..models.module_deprecated import PARAM_DEPRECATED - - -model = 'odoo.addons.module_auto_update.models.module' - - -class EndTestException(Exception): - pass - - -class TestModule(TransactionCase): - - def setUp(self): - super(TestModule, self).setUp() - module_name = 'module_auto_update' - self.env["ir.config_parameter"].set_param(PARAM_DEPRECATED, "1") - self.own_module = self.env['ir.module.module'].search([ - ('name', '=', module_name), - ]) - self.own_dir_path = get_module_path(module_name) - keep_langs = self.env['res.lang'].search([]).mapped('code') - self.own_checksum = addon_hash( - self.own_dir_path, - exclude_patterns=['*.pyc', '*.pyo', '*.pot', 'static/*'], - keep_langs=keep_langs, - ) - self.own_writeable = os.access(self.own_dir_path, os.W_OK) - - @mock.patch('%s.get_module_path' % model) - def create_test_module(self, vals, get_module_path_mock): - get_module_path_mock.return_value = self.own_dir_path - test_module = self.env['ir.module.module'].create(vals) - return test_module - - def test_store_checksum_installed_state_installed(self): - """It should set the module's checksum_installed equal to - checksum_dir when vals contain a ``latest_version`` str.""" - self.own_module.checksum_installed = 'test' - self.own_module._store_checksum_installed({'latest_version': '1.0'}) - self.assertEqual( - self.own_module.checksum_installed, self.own_module.checksum_dir, - ) - - def test_store_checksum_installed_state_uninstalled(self): - """It should clear the module's checksum_installed when vals - contain ``"latest_version": False``""" - self.own_module.checksum_installed = 'test' - self.own_module._store_checksum_installed({'latest_version': False}) - self.assertIs(self.own_module.checksum_installed, False) - - def test_store_checksum_installed_vals_contain_checksum_installed(self): - """It should not set checksum_installed to False or checksum_dir when - a checksum_installed is included in vals""" - self.own_module.checksum_installed = 'test' - self.own_module._store_checksum_installed({ - 'state': 'installed', - 'checksum_installed': 'test', - }) - self.assertEqual( - self.own_module.checksum_installed, 'test', - 'Providing checksum_installed in vals did not prevent overwrite', - ) - - def test_store_checksum_installed_with_retain_context(self): - """It should not set checksum_installed to False or checksum_dir when - self has context retain_checksum_installed=True""" - self.own_module.checksum_installed = 'test' - self.own_module.with_context( - retain_checksum_installed=True, - )._store_checksum_installed({'state': 'installed'}) - self.assertEqual( - self.own_module.checksum_installed, 'test', - 'Providing retain_checksum_installed context did not prevent ' - 'overwrite', - ) - - @mock.patch('%s.get_module_path' % model) - def test_button_uninstall_no_recompute(self, module_path_mock): - """It should not attempt update on `button_uninstall`.""" - module_path_mock.return_value = self.own_dir_path - vals = { - 'name': 'module_auto_update_test_module', - 'state': 'installed', - } - test_module = self.create_test_module(vals) - test_module.checksum_installed = 'test' - uninstall_module = self.env['ir.module.module'].search([ - ('name', '=', 'web'), - ]) - uninstall_module.button_uninstall() - self.assertNotEqual( - test_module.state, 'to upgrade', - 'Auto update logic was triggered during uninstall.', - ) - - def test_button_immediate_uninstall_no_recompute(self): - """It should not attempt update on `button_immediate_uninstall`.""" - - uninstall_module = self.env['ir.module.module'].search([ - ('name', '=', 'web'), - ]) - - try: - mk = mock.MagicMock() - uninstall_module._patch_method('button_uninstall', mk) - mk.side_effect = EndTestException - with self.assertRaises(EndTestException): - uninstall_module.button_immediate_uninstall() - finally: - uninstall_module._revert_method('button_uninstall') - - def test_button_uninstall_cancel(self): - """It should preserve checksum_installed when cancelling uninstall""" - self.own_module.write({'state': 'to remove'}) - self.own_module.checksum_installed = 'test' - self.own_module.button_uninstall_cancel() - self.assertEqual( - self.own_module.checksum_installed, 'test', - 'Uninstall cancellation does not preserve checksum_installed', - ) - - def test_button_upgrade_cancel(self): - """It should preserve checksum_installed when cancelling upgrades""" - self.own_module.write({'state': 'to upgrade'}) - self.own_module.checksum_installed = 'test' - self.own_module.button_upgrade_cancel() - self.assertEqual( - self.own_module.checksum_installed, 'test', - 'Upgrade cancellation does not preserve checksum_installed', - ) - - def test_create(self): - """It should call _store_checksum_installed method""" - _store_checksum_installed_mock = mock.MagicMock() - try: - self.env['ir.module.module']._patch_method( - '_store_checksum_installed', - _store_checksum_installed_mock, - ) - vals = { - 'name': 'module_auto_update_test_module', - 'state': 'installed', - } - self.create_test_module(vals) - _store_checksum_installed_mock.assert_called_once_with(vals) - finally: - self.env['ir.module.module']._revert_method( - '_store_checksum_installed', - ) - - @mute_logger("openerp.modules.module") - @mock.patch('%s.get_module_path' % model) - def test_get_module_list(self, module_path_mock): - """It should change the state of modules with different - checksum_dir and checksum_installed to 'to upgrade'""" - module_path_mock.return_value = self.own_dir_path - vals = { - 'name': 'module_auto_update_test_module', - 'state': 'installed', - } - test_module = self.create_test_module(vals) - test_module.checksum_installed = 'test' - self.env['base.module.upgrade'].get_module_list() - self.assertEqual( - test_module.state, 'to upgrade', - 'List update does not mark upgradeable modules "to upgrade"', - ) - - @mock.patch('%s.get_module_path' % model) - def test_get_module_list_only_changes_installed(self, module_path_mock): - """It should not change the state of a module with a former state - other than 'installed' to 'to upgrade'""" - module_path_mock.return_value = self.own_dir_path - vals = { - 'name': 'module_auto_update_test_module', - 'state': 'uninstalled', - } - test_module = self.create_test_module(vals) - self.env['base.module.upgrade'].get_module_list() - self.assertNotEqual( - test_module.state, 'to upgrade', - 'List update changed state of an uninstalled module', - ) - - def test_write(self): - """It should call _store_checksum_installed method""" - _store_checksum_installed_mock = mock.MagicMock() - self.env['ir.module.module']._patch_method( - '_store_checksum_installed', - _store_checksum_installed_mock, - ) - vals = {'state': 'installed'} - self.own_module.write(vals) - _store_checksum_installed_mock.assert_called_once_with(vals) - self.env['ir.module.module']._revert_method( - '_store_checksum_installed', - ) diff --git a/module_auto_update/tests/test_module_upgrade_deprecated.py b/module_auto_update/tests/test_module_upgrade_deprecated.py deleted file mode 100644 index 13e729594..000000000 --- a/module_auto_update/tests/test_module_upgrade_deprecated.py +++ /dev/null @@ -1,46 +0,0 @@ -# Copyright 2017 LasLabs Inc. -# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). - -import mock - -from odoo.modules import get_module_path -from odoo.modules.registry import Registry -from odoo.tests.common import TransactionCase - -from ..models.module_deprecated import PARAM_DEPRECATED - - -class TestModuleUpgrade(TransactionCase): - - def setUp(self): - super(TestModuleUpgrade, self).setUp() - module_name = 'module_auto_update' - self.env["ir.config_parameter"].set_param(PARAM_DEPRECATED, "1") - self.own_module = self.env['ir.module.module'].search([ - ('name', '=', module_name), - ]) - self.own_dir_path = get_module_path(module_name) - - def test_upgrade_module_cancel(self): - """It should preserve checksum_installed when cancelling upgrades""" - self.own_module.write({'state': 'to upgrade'}) - self.own_module.checksum_installed = 'test' - self.env['base.module.upgrade'].upgrade_module_cancel() - self.assertEqual( - self.own_module.checksum_installed, 'test', - 'Upgrade cancellation does not preserve checksum_installed', - ) - - @mock.patch.object(Registry, 'new') - def test_upgrade_module(self, new_mock): - """Calls get_module_list when upgrading in api.model mode""" - get_module_list_mock = mock.MagicMock() - try: - self.env['base.module.upgrade']._patch_method( - 'get_module_list', - get_module_list_mock, - ) - self.env['base.module.upgrade'].upgrade_module() - get_module_list_mock.assert_called_once_with() - finally: - self.env['base.module.upgrade']._revert_method('get_module_list') diff --git a/module_auto_update/wizards/__init__.py b/module_auto_update/wizards/__init__.py deleted file mode 100644 index bcaca7966..000000000 --- a/module_auto_update/wizards/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). - -from . import module_upgrade_deprecated diff --git a/module_auto_update/wizards/module_upgrade_deprecated.py b/module_auto_update/wizards/module_upgrade_deprecated.py deleted file mode 100644 index b44ec684d..000000000 --- a/module_auto_update/wizards/module_upgrade_deprecated.py +++ /dev/null @@ -1,84 +0,0 @@ -# Copyright 2017 LasLabs Inc. -# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). - -import logging - -from odoo import api, models - -from ..models.module_deprecated import PARAM_DEPRECATED - -_logger = logging.getLogger(__name__) - - -class ModuleUpgrade(models.TransientModel): - _inherit = 'base.module.upgrade' - - @api.model - def _autoupdate_deprecated(self): - """Know if we should enable deprecated features.""" - deprecated = ( - self.env["ir.config_parameter"].get_param(PARAM_DEPRECATED)) - if deprecated is False: - # Enable deprecated features if this is the 1st automated update - # after the version that deprecated them (X.Y.2.0.0) - own_module = self.env["ir.module.module"].search([ - ("name", "=", "module_auto_update"), - ]) - try: - if own_module.latest_version.split(".")[2] == "1": - deprecated = "1" - except AttributeError: - pass # 1st install, there's no latest_version - return deprecated == "1" - - @api.model - def get_module_list(self): - """Set modules to upgrade searching by their dir checksum.""" - if self._autoupdate_deprecated(): - Module = self.env["ir.module.module"] - installed_modules = Module.search([('state', '=', 'installed')]) - upgradeable_modules = installed_modules.filtered( - lambda r: r.checksum_dir != r.checksum_installed, - ) - upgradeable_modules.button_upgrade() - return super(ModuleUpgrade, self).get_module_list() - - @api.multi - def upgrade_module(self): - """Make a fully automated addon upgrade.""" - if self._autoupdate_deprecated(): - _logger.warning( - "You are possibly using an unsupported upgrade system; " - "set '%s' system parameter to '0' and start calling " - "`env['ir.module.module'].upgrade_changed_checksum()` from " - "now on to get rid of this message. See module's README's " - "Known Issues section for further information on the matter." - ) - # Compute updates by checksum when called in @api.model fashion - self.env.cr.autocommit(True) # Avoid transaction lock - if not self: - self.get_module_list() - Module = self.env["ir.module.module"] - # Get every addon state before updating - pre_states = {addon["name"]: addon["state"] for addon - in Module.search_read([], ["name", "state"])} - # Perform upgrades, possibly in a limited graph that excludes me - result = super(ModuleUpgrade, self).upgrade_module() - if self._autoupdate_deprecated(): - self.env.cr.autocommit(False) - # Reload environments, anything may have changed - self.env.clear() - # Update addons checksum if state changed and I wasn't uninstalled - own = Module.search_read( - [("name", "=", "module_auto_update")], - ["state"], - limit=1) - if own and own[0]["state"] != "uninstalled": - for addon in Module.search([]): - if addon.state != pre_states.get(addon.name): - # Trigger the write hook that should have been - # triggered when the module was [un]installed/updated - # in the limited module graph inside above call to - # super(), and updates its dir checksum as needed - addon.latest_version = addon.latest_version - return result diff --git a/setup/.setuptools-odoo-make-default-ignore b/setup/.setuptools-odoo-make-default-ignore new file mode 100644 index 000000000..207e61533 --- /dev/null +++ b/setup/.setuptools-odoo-make-default-ignore @@ -0,0 +1,2 @@ +# addons listed in this file are ignored by +# setuptools-odoo-make-default (one addon per line) diff --git a/setup/README b/setup/README new file mode 100644 index 000000000..a63d633e8 --- /dev/null +++ b/setup/README @@ -0,0 +1,2 @@ +To learn more about this directory, please visit +https://pypi.python.org/pypi/setuptools-odoo diff --git a/setup/module_auto_update/odoo/addons/module_auto_update b/setup/module_auto_update/odoo/addons/module_auto_update new file mode 120000 index 000000000..752ff6fc9 --- /dev/null +++ b/setup/module_auto_update/odoo/addons/module_auto_update @@ -0,0 +1 @@ +../../../../module_auto_update \ No newline at end of file diff --git a/setup/module_auto_update/setup.py b/setup/module_auto_update/setup.py new file mode 100644 index 000000000..28c57bb64 --- /dev/null +++ b/setup/module_auto_update/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +)