diff --git a/module_auto_update/README.rst b/module_auto_update/README.rst index 96ea98e52..f051f7c06 100644 --- a/module_auto_update/README.rst +++ b/module_auto_update/README.rst @@ -46,28 +46,7 @@ in an Odoo shell session:: .. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas :alt: Try me on Runbot - :target: https://runbot.odoo-community.org/runbot/149/9.0 - -Known issues / Roadmap -====================== - -* 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``. + :target: https://runbot.odoo-community.org/runbot/149/8.0 Bug Tracker =========== diff --git a/module_auto_update/__init__.py b/module_auto_update/__init__.py index 6b6305ed2..22255df3d 100644 --- a/module_auto_update/__init__.py +++ b/module_auto_update/__init__.py @@ -3,5 +3,4 @@ # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). from . import models -from . import wizards -from .hooks import uninstall_hook +from .hooks import install_hook, uninstall_hook diff --git a/module_auto_update/__openerp__.py b/module_auto_update/__openerp__.py index 3b5d2bbf9..fdb25fc34 100644 --- a/module_auto_update/__openerp__.py +++ b/module_auto_update/__openerp__.py @@ -5,7 +5,7 @@ { 'name': 'Module Auto Update', 'summary': 'Automatically update Odoo modules', - 'version': '9.0.2.0.0', + 'version': '8.0.2.0.0', 'category': 'Extra Tools', 'website': 'https://github.com/OCA/server-tools', 'author': 'LasLabs, ' @@ -16,11 +16,9 @@ 'license': 'LGPL-3', 'application': False, 'installable': True, + 'install_hook': 'install_hook', 'uninstall_hook': 'uninstall_hook', 'depends': [ 'base', ], - 'data': [ - 'data/cron_data_deprecated.xml', - ], } 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 26d55fa88..000000000 --- a/module_auto_update/data/cron_data_deprecated.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - Perform Module Upgrades - - - 1 - days - -1 - - base.module.upgrade - upgrade_module - - - diff --git a/module_auto_update/hooks.py b/module_auto_update/hooks.py index 4f0330826..f1db6d87f 100644 --- a/module_auto_update/hooks.py +++ b/module_auto_update/hooks.py @@ -8,11 +8,13 @@ from .models.module import PARAM_INSTALLED_CHECKSUMS from .models.module_deprecated import PARAM_DEPRECATED +def install_hook(cr, registry): + env = api.Environment(cr, SUPERUSER_ID, {}) + # make sure migration to 9 does not enable deprecated features + env["ir.config_parameter"].set_param(PARAM_DEPRECATED, '0') + + 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/9.0.2.0.0/pre-migrate.py b/module_auto_update/migrations/9.0.2.0.0/pre-migrate.py deleted file mode 100644 index 4fe36ede7..000000000 --- a/module_auto_update/migrations/9.0.2.0.0/pre-migrate.py +++ /dev/null @@ -1,23 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright 2018 Tecnativa - Jairo Llopis -# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl). -import logging -from psycopg2 import IntegrityError -from openerp.addons.module_auto_update.models.module_deprecated import \ - PARAM_DEPRECATED - -_logger = logging.getLogger(__name__) - - -def migrate(cr, version): - """Autoenable deprecated behavior.""" - try: - 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 7a40d1994..b27944126 100644 --- a/module_auto_update/models/__init__.py +++ b/module_auto_update/models/__init__.py @@ -3,4 +3,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.py b/module_auto_update/models/module.py index 334ec9d16..830985cec 100644 --- a/module_auto_update/models/module.py +++ b/module_auto_update/models/module.py @@ -22,7 +22,7 @@ DEFAULT_EXCLUDE_PATTERNS = \ _logger = logging.getLogger(__name__) -class IncompleteUpgradeError(exceptions.UserError): +class IncompleteUpgradeError(exceptions.Warning): pass diff --git a/module_auto_update/models/module_deprecated.py b/module_auto_update/models/module_deprecated.py index 25a8d5b9d..d7bfb1d2f 100644 --- a/module_auto_update/models/module_deprecated.py +++ b/module_auto_update/models/module_deprecated.py @@ -1,69 +1,4 @@ # -*- coding: utf-8 -*- -# Copyright 2017 LasLabs Inc. # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). -from openerp 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/tests/__init__.py b/module_auto_update/tests/__init__.py index 4be96d1cc..3a7c09bd0 100644 --- a/module_auto_update/tests/__init__.py +++ b/module_auto_update/tests/__init__.py @@ -4,5 +4,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_deprecated.py b/module_auto_update/tests/test_module_deprecated.py deleted file mode 100644 index d861c379f..000000000 --- a/module_auto_update/tests/test_module_deprecated.py +++ /dev/null @@ -1,172 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright 2017 LasLabs Inc. -# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). - -import os - -import mock - -from openerp.modules import get_module_path -from openerp.tests.common import TransactionCase -from openerp.tools import mute_logger - -from openerp.addons.module_auto_update.addon_hash import addon_hash - -from ..models.module_deprecated import PARAM_DEPRECATED - - -model = 'openerp.addons.module_auto_update.models.module' - - -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', - ) - - 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 41069a2ad..000000000 --- a/module_auto_update/tests/test_module_upgrade_deprecated.py +++ /dev/null @@ -1,47 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright 2017 LasLabs Inc. -# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). - -import mock - -from openerp.modules import get_module_path -from openerp.modules.registry import RegistryManager -from openerp.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(RegistryManager, '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 ab4eca2e4..000000000 --- a/module_auto_update/wizards/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright 2017 LasLabs Inc. -# 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 599395079..000000000 --- a/module_auto_update/wizards/module_upgrade_deprecated.py +++ /dev/null @@ -1,85 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright 2017 LasLabs Inc. -# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). - -import logging - -from openerp 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