Initial commit: Odoo 18.0-20251222 extra-addons
This commit is contained in:
195
module_auto_update/models/module.py
Executable file
195
module_auto_update/models/module.py
Executable file
@@ -0,0 +1,195 @@
|
||||
# Copyright 2017 LasLabs Inc.
|
||||
# Copyright 2018 ACSONE SA/NV.
|
||||
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl).
|
||||
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
|
||||
from odoo import _, api, exceptions, models, tools
|
||||
from odoo.modules.module import get_module_path
|
||||
|
||||
from ..addon_hash import addon_hash
|
||||
|
||||
PARAM_INSTALLED_CHECKSUMS = "module_auto_update.installed_checksums"
|
||||
PARAM_EXCLUDE_PATTERNS = "module_auto_update.exclude_patterns"
|
||||
DEFAULT_EXCLUDE_PATTERNS = "*.pyc,*.pyo,i18n/*.pot,i18n_extra/*.pot,static/*,tests/*"
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class FailedUpgradeError(exceptions.UserError):
|
||||
pass
|
||||
|
||||
|
||||
class IncompleteUpgradeError(exceptions.UserError):
|
||||
pass
|
||||
|
||||
|
||||
def ensure_module_state(env, modules, state):
|
||||
# read module states, bypassing any Odoo cache
|
||||
if not modules:
|
||||
return
|
||||
env.cr.execute(
|
||||
"SELECT name FROM ir_module_module " "WHERE id IN %s AND state != %s",
|
||||
(tuple(modules.ids), state),
|
||||
)
|
||||
names = [r[0] for r in env.cr.fetchall()]
|
||||
if names:
|
||||
raise FailedUpgradeError(
|
||||
f"The following modules should be in state {state!r} "
|
||||
f"at this stage: {','.join(names)}. Bailing out for safety."
|
||||
)
|
||||
|
||||
|
||||
class Module(models.Model):
|
||||
_inherit = "ir.module.module"
|
||||
|
||||
def _get_checksum_dir(self):
|
||||
self.ensure_one()
|
||||
|
||||
exclude_patterns = self.env["ir.config_parameter"].get_param(
|
||||
PARAM_EXCLUDE_PATTERNS,
|
||||
DEFAULT_EXCLUDE_PATTERNS,
|
||||
)
|
||||
exclude_patterns = [p.strip() for p in exclude_patterns.split(",")]
|
||||
keep_langs = self.env["res.lang"].search([]).mapped("code")
|
||||
|
||||
module_path = get_module_path(self.name)
|
||||
if module_path and os.path.isdir(module_path):
|
||||
checksum_dir = addon_hash(
|
||||
module_path,
|
||||
exclude_patterns,
|
||||
keep_langs,
|
||||
)
|
||||
else:
|
||||
checksum_dir = False
|
||||
|
||||
return checksum_dir
|
||||
|
||||
@api.model
|
||||
def _get_saved_checksums(self):
|
||||
Icp = self.env["ir.config_parameter"]
|
||||
return json.loads(Icp.get_param(PARAM_INSTALLED_CHECKSUMS, "{}"))
|
||||
|
||||
@api.model
|
||||
def _save_checksums(self, checksums):
|
||||
Icp = self.env["ir.config_parameter"]
|
||||
Icp.set_param(PARAM_INSTALLED_CHECKSUMS, json.dumps(checksums))
|
||||
Icp.flush_model()
|
||||
|
||||
@api.model
|
||||
def _save_installed_checksums(self):
|
||||
checksums = {}
|
||||
installed_modules = self.search([("state", "=", "installed")])
|
||||
for module in installed_modules:
|
||||
checksums[module.name] = module._get_checksum_dir()
|
||||
self._save_checksums(checksums)
|
||||
|
||||
@api.model
|
||||
def _get_modules_partially_installed(self):
|
||||
return self.search([("state", "in", ("to install", "to remove", "to upgrade"))])
|
||||
|
||||
@api.model
|
||||
def _get_modules_with_changed_checksum(self):
|
||||
saved_checksums = self._get_saved_checksums()
|
||||
installed_modules = self.search([("state", "=", "installed")])
|
||||
return installed_modules.filtered(
|
||||
lambda r: r._get_checksum_dir() != saved_checksums.get(r.name),
|
||||
)
|
||||
|
||||
@api.model
|
||||
def upgrade_changed_checksum(self, overwrite_existing_translations=False):
|
||||
"""Run an upgrade of the database, upgrading only changed modules.
|
||||
|
||||
Installed modules for which the checksum has changed since the
|
||||
last successful run of this method are marked "to upgrade",
|
||||
then the normal Odoo scheduled upgrade process
|
||||
is launched.
|
||||
|
||||
If there is no module with a changed checksum, and no module in state
|
||||
other than installed, uninstalled, uninstallable, this method does
|
||||
nothing, otherwise the normal Odoo upgrade process is launched.
|
||||
|
||||
After a successful upgrade, the checksums of installed modules are
|
||||
saved.
|
||||
|
||||
In case of error during the upgrade, an exception is raised.
|
||||
If any module remains to upgrade or to uninstall after the upgrade
|
||||
process, an exception is raised as well.
|
||||
|
||||
Note: this method commits the current transaction at each important
|
||||
step, it is therefore not intended to be run as part of a
|
||||
larger transaction.
|
||||
"""
|
||||
_logger.info(
|
||||
"Checksum upgrade starting (i18n-overwrite=%s)...",
|
||||
overwrite_existing_translations,
|
||||
)
|
||||
|
||||
tools.config["overwrite_existing_translations"] = (
|
||||
overwrite_existing_translations
|
||||
)
|
||||
|
||||
_logger.info("Updating modules list...")
|
||||
self.update_list()
|
||||
changed_modules = self._get_modules_with_changed_checksum()
|
||||
if not changed_modules and not self._get_modules_partially_installed():
|
||||
_logger.info(
|
||||
"No checksum change detected in installed modules "
|
||||
"and all modules installed, nothing to do."
|
||||
)
|
||||
|
||||
return {
|
||||
"type": "ir.actions.client",
|
||||
"tag": "display_notification",
|
||||
"params": {
|
||||
"message": _(
|
||||
"No checksum change detected in installed modules "
|
||||
"and all modules installed, nothing to do."
|
||||
),
|
||||
"type": "success",
|
||||
"sticky": False,
|
||||
},
|
||||
}
|
||||
|
||||
_logger.info(
|
||||
"Marking the following modules to upgrade, "
|
||||
"for their checksums changed: %s...",
|
||||
",".join(changed_modules.mapped("name")),
|
||||
)
|
||||
changed_modules.button_upgrade()
|
||||
self.env.cr.commit() # pylint: disable=invalid-commit
|
||||
# in rare situations, button_upgrade may fail without
|
||||
# exception, this would lead to corruption because
|
||||
# no upgrade would be performed and save_installed_checksums
|
||||
# would update cheksums for modules that have not been upgraded
|
||||
ensure_module_state(self.env, changed_modules, "to upgrade")
|
||||
|
||||
_logger.info("Upgrading...")
|
||||
self.env["base.module.upgrade"].upgrade_module()
|
||||
self.env.cr.commit() # pylint: disable=invalid-commit
|
||||
|
||||
_logger.info("Upgrade successful, updating checksums...")
|
||||
self._save_installed_checksums()
|
||||
self.env.cr.commit() # pylint: disable=invalid-commit
|
||||
|
||||
partial_modules = self._get_modules_partially_installed()
|
||||
if partial_modules:
|
||||
raise IncompleteUpgradeError(
|
||||
"Checksum upgrade successful but "
|
||||
"incomplete for the following modules: {}".format(
|
||||
",".join(partial_modules.mapped("name"))
|
||||
)
|
||||
)
|
||||
|
||||
_logger.info("Checksum upgrade complete.")
|
||||
return {
|
||||
"type": "ir.actions.client",
|
||||
"tag": "display_notification",
|
||||
"params": {
|
||||
"message": _("Checksum upgrade complete."),
|
||||
"type": "success",
|
||||
"sticky": False,
|
||||
},
|
||||
}
|
||||
Reference in New Issue
Block a user