Browse Source
[REF] module_auto_update: Step 3, backwards compatibility
[REF] module_auto_update: Step 3, backwards compatibility
The previous implementation of this addon proved being extremely buggy: - It supplied out of the box a enabled cron to update Odoo that didn't restart the server, which possibly meant that upgrades broke things. - It overloaded standard Odoo upgrade methods that made i.e. installing an addon sometimes forced to upgrade all other addons in the database. - The checksum system wasn't smart enough, and some files that didn't need a module upgrade triggered the upgrade. - It was based on a dirhash library that was untested. - Some updates were not detected properly. - Storing a column into `ir.module.module` sometimes forbids uninstalling the addon. Thanks to Stéphane Bidoul (ACSONE), now we have new methods to perform the same work in a safer and more stable way. All I'm doing here is: - Cron is disabled by default. - Installed checksums are no longer saved at first install. - Old installations should keep most functionality intact thanks to the migration script. - Drop some duplicated tests. - Allow module uninstallation by pre-removing the fields from ir.mode.model. - When uninstalling the addon, the deprecated features will get removed for next installs always. Besides that, fixes for the new implementation too: - When uninstalling the addon, we remove the stored checksum data, so further installations work as if the addon was installed from scratch.pull/1217/head
Jairo Llopis
7 years ago
committed by
Benjamin Willig
10 changed files with 139 additions and 86 deletions
-
21module_auto_update/README.rst
-
2module_auto_update/__init__.py
-
4module_auto_update/__manifest__.py
-
2module_auto_update/data/cron_data_deprecated.xml
-
12module_auto_update/hooks.py
-
23module_auto_update/migrations/10.0.2.0.0/pre-migrate.py
-
13module_auto_update/models/module_deprecated.py
-
52module_auto_update/tests/test_module_deprecated.py
-
3module_auto_update/tests/test_module_upgrade_deprecated.py
-
93module_auto_update/wizards/module_upgrade_deprecated.py
@ -0,0 +1,23 @@ |
|||||
|
# -*- 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") |
@ -1,49 +1,84 @@ |
|||||
# Copyright 2017 LasLabs Inc. |
# Copyright 2017 LasLabs Inc. |
||||
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). |
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). |
||||
|
|
||||
|
import logging |
||||
|
|
||||
from odoo import api, models |
from odoo import api, models |
||||
|
|
||||
|
from ..models.module_deprecated import PARAM_DEPRECATED |
||||
|
|
||||
|
_logger = logging.getLogger(__name__) |
||||
|
|
||||
|
|
||||
class ModuleUpgrade(models.TransientModel): |
class ModuleUpgrade(models.TransientModel): |
||||
_inherit = 'base.module.upgrade' |
_inherit = 'base.module.upgrade' |
||||
|
|
||||
@api.model |
@api.model |
||||
@api.returns('ir.module.module') |
|
||||
|
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): |
def get_module_list(self): |
||||
"""Set modules to upgrade searching by their dir checksum.""" |
"""Set modules to upgrade searching by their dir checksum.""" |
||||
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() |
|
||||
|
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() |
return super(ModuleUpgrade, self).get_module_list() |
||||
|
|
||||
@api.multi |
@api.multi |
||||
def upgrade_module(self): |
def upgrade_module(self): |
||||
"""Make a fully automated addon upgrade.""" |
"""Make a fully automated addon upgrade.""" |
||||
# Compute updates by checksum when called in @api.model fashion |
|
||||
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"])} |
|
||||
|
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 |
# Perform upgrades, possibly in a limited graph that excludes me |
||||
result = super(ModuleUpgrade, self).upgrade_module() |
result = super(ModuleUpgrade, self).upgrade_module() |
||||
# 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 |
|
||||
|
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 |
return result |
Write
Preview
Loading…
Cancel
Save
Reference in new issue