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/1382/head
Jairo Llopis
7 years ago
committed by
Stéphane Bidoul (ACSONE)
No known key found for this signature in database
GPG Key ID: BCAB2555446B5B92
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. |
|||
# 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 |
|||
@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): |
|||
"""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() |
|||
|
|||
@api.multi |
|||
def upgrade_module(self): |
|||
"""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 |
|||
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 |
Write
Preview
Loading…
Cancel
Save
Reference in new issue