From b3ecebd58421dfa2f53b0ceeaf96512007014409 Mon Sep 17 00:00:00 2001 From: Brenton Hughes Date: Thu, 20 Jul 2017 00:28:37 -0700 Subject: [PATCH 1/7] [ADD] module_auto_update: Create module (#882) * [IMP] module_auto_update: Create new module * Add checksum_dir and checksum_installed fields to ir.module.module * Add checksum_dir to compute current checksum of module directory in addons path * Add checksum_installed to store checksum of module directory when module was last installed or upgraded * Use checksumdir Python library to compute module directory sha1 hashes, ignoring pyc and pyo extensions * Extend update_list method to compare modules' checksum_dir and checksum_installed, then change state of modules with differing checksums to 'to upgrade' * Replace Apps/Updates menu item with menu item of same name, which updates apps list and displays tree view of ir.module.module records with state 'to upgrade' * Extend create and write methods to store computed checksum_dir as checksum_installed during module installation and upgrade, and set checksum_installed to False on uninstall * Use context to stop checksum_installed from being updated during upgrade/uninstall cancellation * Add cron job to periodically check for module upgrades by comparing checksums, then perform any available upgrades * Extend upgrade_module method (called by cron and 'Apply Scheduled Upgrades' menu item) to call update_list * Add post_init_hook to store checksum_installed of existing modules * Add test coverage * [FIX] module_auto_update: Fix test broken by changes * Use dummy module to test update_list method instead of module_auto_update --- module_auto_update/README.rst | 76 +++++++ module_auto_update/__init__.py | 7 + module_auto_update/__manifest__.py | 30 +++ module_auto_update/data/cron_data.xml | 15 ++ module_auto_update/hooks.py | 14 ++ module_auto_update/models/__init__.py | 5 + module_auto_update/models/module.py | 83 +++++++ module_auto_update/tests/__init__.py | 6 + module_auto_update/tests/test_module.py | 212 ++++++++++++++++++ .../tests/test_module_upgrade.py | 42 ++++ module_auto_update/views/module_views.xml | 49 ++++ module_auto_update/wizards/__init__.py | 5 + module_auto_update/wizards/module_upgrade.py | 21 ++ 13 files changed, 565 insertions(+) create mode 100644 module_auto_update/README.rst create mode 100644 module_auto_update/__init__.py create mode 100644 module_auto_update/__manifest__.py create mode 100644 module_auto_update/data/cron_data.xml create mode 100644 module_auto_update/hooks.py create mode 100644 module_auto_update/models/__init__.py create mode 100644 module_auto_update/models/module.py create mode 100644 module_auto_update/tests/__init__.py create mode 100644 module_auto_update/tests/test_module.py create mode 100644 module_auto_update/tests/test_module_upgrade.py create mode 100644 module_auto_update/views/module_views.xml create mode 100644 module_auto_update/wizards/__init__.py create mode 100644 module_auto_update/wizards/module_upgrade.py diff --git a/module_auto_update/README.rst b/module_auto_update/README.rst new file mode 100644 index 000000000..3535f4fb2 --- /dev/null +++ b/module_auto_update/README.rst @@ -0,0 +1,76 @@ +.. image:: https://img.shields.io/badge/licence-LGPL--3-blue.svg + :target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html + :alt: License: LGPL-3 + +================== +Module Auto Update +================== + +This module will automatically check for and apply module upgrades on a schedule. + +Upgrade checking is accomplished by comparing the SHA1 checksums of currently-installed modules to the checksums of corresponding modules in the addons directories. + +Installation +============ + +Prior to installing this module, you need to: + +#. Install checksumdir with `pip install checksumdir` +#. Ensure all installed modules are up-to-date. When installed, this module will assume the versions found in the addons directories are currently installed. + +Configuration +============= + +The default time for checking and applying upgrades is 3:00 AM (UTC). To change this schedule, modify the "Perform Module Upgrades" scheduled action. + +This module will ignore .pyc and .pyo file extensions by default. To modify this, create a module_auto_update.checksum_excluded_extensions system parameter with the desired extensions listed as comma-separated values. + +Usage +===== + +Modules scheduled for upgrade can be viewed by clicking the "Updates" menu item in the Apps sidebar. + +To perform upgrades manually, click the "Apply Scheduled Upgrades" menu item in the Apps sidebar. + +.. 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/10.0 + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues +`_. In case of trouble, please +check there if your issue has already been reported. If you spotted it first, +help us smash it by providing detailed and welcomed feedback. + +Credits +======= + +Images +------ + +* Odoo Community Association: `Icon `_. + +Contributors +------------ + +* Brent Hughes +* Juan José Scarafía + +Do not contact contributors directly about support or help with technical issues. + +Maintainer +---------- + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +This module is maintained by the OCA. + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +To contribute to this module, please visit https://odoo-community.org. diff --git a/module_auto_update/__init__.py b/module_auto_update/__init__.py new file mode 100644 index 000000000..36f555442 --- /dev/null +++ b/module_auto_update/__init__.py @@ -0,0 +1,7 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 LasLabs Inc. +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +from . import models +from . import wizards +from .hooks import post_init_hook diff --git a/module_auto_update/__manifest__.py b/module_auto_update/__manifest__.py new file mode 100644 index 000000000..db65d269a --- /dev/null +++ b/module_auto_update/__manifest__.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 LasLabs Inc. +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +{ + 'name': 'Module Auto Update', + 'summary': 'Automatically update Odoo modules', + 'version': '10.0.1.0.0', + 'category': 'Extra Tools', + 'website': 'https://odoo-community.org/', + 'author': 'LasLabs, ' + 'Juan José Scarafía, ' + 'Odoo Community Association (OCA)', + 'license': 'LGPL-3', + 'application': False, + 'installable': True, + 'post_init_hook': 'post_init_hook', + 'external_dependencies': { + 'python': [ + 'checksumdir', + ], + }, + 'depends': [ + 'base', + ], + 'data': [ + 'views/module_views.xml', + 'data/cron_data.xml', + ], +} diff --git a/module_auto_update/data/cron_data.xml b/module_auto_update/data/cron_data.xml new file mode 100644 index 000000000..1745fe0c9 --- /dev/null +++ b/module_auto_update/data/cron_data.xml @@ -0,0 +1,15 @@ + + + + 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 new file mode 100644 index 000000000..f062966c3 --- /dev/null +++ b/module_auto_update/hooks.py @@ -0,0 +1,14 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 LasLabs Inc. +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +from odoo import SUPERUSER_ID, api + + +def post_init_hook(cr, registry): + env = api.Environment(cr, SUPERUSER_ID, {}) + installed_modules = env['ir.module.module'].search([ + ('state', '=', 'installed'), + ]) + for r in installed_modules: + r.checksum_installed = r.checksum_dir diff --git a/module_auto_update/models/__init__.py b/module_auto_update/models/__init__.py new file mode 100644 index 000000000..b27944126 --- /dev/null +++ b/module_auto_update/models/__init__.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 LasLabs Inc. +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +from . import module diff --git a/module_auto_update/models/module.py b/module_auto_update/models/module.py new file mode 100644 index 000000000..4d9ccec59 --- /dev/null +++ b/module_auto_update/models/module.py @@ -0,0 +1,83 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 LasLabs Inc. +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +import logging + +from odoo import api, fields, models +from odoo.modules.module import get_module_path + +_logger = logging.getLogger(__name__) +try: + from checksumdir import dirhash +except ImportError: + _logger.debug('Cannot `import checksumdir`.') + + +class Module(models.Model): + _inherit = 'ir.module.module' + + checksum_dir = fields.Char( + compute='_compute_checksum_dir', + ) + checksum_installed = fields.Char() + + @api.depends('name') + def _compute_checksum_dir(self): + exclude = self.env["ir.config_parameter"].get_param( + "module_auto_update.checksum_excluded_extensions", + "pyc,pyo", + ).split(",") + + for r in self: + r.checksum_dir = dirhash( + get_module_path(r.name), + 'sha1', + excluded_extensions=exclude, + ) + + def _store_checksum_installed(self, vals): + if self.env.context.get('retain_checksum_installed'): + return + if 'checksum_installed' not in vals: + if vals.get('state') == 'installed': + for r in self: + r.checksum_installed = r.checksum_dir + elif vals.get('state') == 'uninstalled': + self.write({'checksum_installed': False}) + + @api.multi + def button_uninstall_cancel(self): + return super( + Module, + self.with_context(retain_checksum_installed=True), + ).button_uninstall_cancel() + + @api.multi + def button_upgrade_cancel(self): + return super( + Module, + self.with_context(retain_checksum_installed=True), + ).button_upgrade_cancel() + + @api.model + def create(self, vals): + res = super(Module, self).create(vals) + res._store_checksum_installed(vals) + return res + + @api.model + def update_list(self): + res = super(Module, self).update_list() + installed_modules = self.search([('state', '=', 'installed')]) + upgradeable_modules = installed_modules.filtered( + lambda r: r.checksum_dir != r.checksum_installed, + ) + upgradeable_modules.write({'state': "to upgrade"}) + 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 new file mode 100644 index 000000000..237970451 --- /dev/null +++ b/module_auto_update/tests/__init__.py @@ -0,0 +1,6 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 LasLabs Inc. +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +from . import test_module +from . import test_module_upgrade diff --git a/module_auto_update/tests/test_module.py b/module_auto_update/tests/test_module.py new file mode 100644 index 000000000..5d499fc27 --- /dev/null +++ b/module_auto_update/tests/test_module.py @@ -0,0 +1,212 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 LasLabs Inc. +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +import logging +import tempfile + +import mock + +from odoo.modules import get_module_path +from odoo.tests.common import TransactionCase + +from .. import post_init_hook + +_logger = logging.getLogger(__name__) +try: + from checksumdir import dirhash +except ImportError: + _logger.debug('Cannot `import checksumdir`.') + +model = 'odoo.addons.module_auto_update.models.module' + + +class TestModule(TransactionCase): + + def setUp(self): + super(TestModule, self).setUp() + module_name = 'module_auto_update' + self.own_module = self.env['ir.module.module'].search([ + ('name', '=', module_name), + ]) + self.own_dir_path = get_module_path(module_name) + self.own_checksum = dirhash( + self.own_dir_path, + 'sha1', + excluded_extensions=['pyc', 'pyo'], + ) + + @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_compute_checksum_dir(self): + """It should compute the directory's SHA-1 hash""" + self.assertEqual( + self.own_module.checksum_dir, self.own_checksum, + 'Module directory checksum not computed properly', + ) + + def test_compute_checksum_dir_ignore_excluded(self): + """It should exclude .pyc/.pyo extensions from checksum + calculations""" + with tempfile.NamedTemporaryFile( + suffix='.pyc', dir=self.own_dir_path): + self.assertEqual( + self.own_module.checksum_dir, self.own_checksum, + 'SHA1 checksum does not ignore excluded extensions', + ) + + def test_compute_checksum_dir_recomputes_when_file_added(self): + """It should return a different value when a non-.pyc/.pyo file is + added to the module directory""" + with tempfile.NamedTemporaryFile( + suffix='.py', dir=self.own_dir_path): + self.assertNotEqual( + self.own_module.checksum_dir, self.own_checksum, + 'SHA1 checksum not recomputed', + ) + + def test_store_checksum_installed_state_installed(self): + """It should set the module's checksum_installed equal to + checksum_dir when vals contain state 'installed'""" + self.own_module.checksum_installed = 'test' + self.own_module._store_checksum_installed({'state': 'installed'}) + self.assertEqual( + self.own_module.checksum_installed, self.own_module.checksum_dir, + 'Setting state to installed does not store checksum_dir ' + 'as checksum_installed', + ) + + def test_store_checksum_installed_state_uninstalled(self): + """It should clear the module's checksum_installed when vals + contain state 'uninstalled'""" + self.own_module.checksum_installed = 'test' + self.own_module._store_checksum_installed({'state': 'uninstalled'}) + self.assertEqual( + self.own_module.checksum_installed, False, + 'Setting state to uninstalled does not clear checksum_installed', + ) + + 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() + 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) + self.env['ir.module.module']._revert_method( + '_store_checksum_installed', + ) + + @mock.patch('%s.get_module_path' % model) + def test_update_list(self, get_module_path_mock): + """It should change the state of modules with different + checksum_dir and checksum_installed to 'to upgrade'""" + get_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['ir.module.module'].update_list() + self.assertEqual( + test_module.state, 'to upgrade', + 'List update does not mark upgradeable modules "to upgrade"', + ) + + def test_update_list_only_changes_installed(self): + """It should not change the state of a module with a former state + other than 'installed' to 'to upgrade'""" + vals = { + 'name': 'module_auto_update_test_module', + 'state': 'uninstalled', + } + test_module = self.create_test_module(vals) + self.env['ir.module.module'].update_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', + ) + + def test_post_init_hook(self): + """It should set checksum_installed equal to checksum_dir for all + installed modules""" + installed_modules = self.env['ir.module.module'].search([ + ('state', '=', 'installed'), + ]) + post_init_hook(self.env.cr, None) + self.assertListEqual( + installed_modules.mapped('checksum_dir'), + installed_modules.mapped('checksum_installed'), + 'Installed modules did not have checksum_installed stored', + ) diff --git a/module_auto_update/tests/test_module_upgrade.py b/module_auto_update/tests/test_module_upgrade.py new file mode 100644 index 000000000..edc24fd8e --- /dev/null +++ b/module_auto_update/tests/test_module_upgrade.py @@ -0,0 +1,42 @@ +# -*- coding: utf-8 -*- +# 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 + + +class TestModuleUpgrade(TransactionCase): + + def setUp(self): + super(TestModuleUpgrade, self).setUp() + module_name = 'module_auto_update' + 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): + """It should call update_list method on ir.module.module""" + update_list_mock = mock.MagicMock() + self.env['ir.module.module']._patch_method( + 'update_list', + update_list_mock, + ) + self.env['base.module.upgrade'].upgrade_module() + update_list_mock.assert_called_once_with() + self.env['ir.module.module']._revert_method('update_list') diff --git a/module_auto_update/views/module_views.xml b/module_auto_update/views/module_views.xml new file mode 100644 index 000000000..78a0be51e --- /dev/null +++ b/module_auto_update/views/module_views.xml @@ -0,0 +1,49 @@ + + + + + updates.module.search + ir.module.module + + + + + + + + + + + Open Updates and Update Apps List Server Action + + + if model.update_list(): + action = { + 'name': 'Updates', + 'type': 'ir.actions.act_window', + 'res_model': 'ir.module.module', + 'view_type': 'form', + 'view_mode': 'tree,form', + 'target': 'main', + 'context': '{"search_default_scheduled_upgrades": 1}', + } + + + + + + + + + + + + + + diff --git a/module_auto_update/wizards/__init__.py b/module_auto_update/wizards/__init__.py new file mode 100644 index 000000000..58cb00103 --- /dev/null +++ b/module_auto_update/wizards/__init__.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 LasLabs Inc. +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +from . import module_upgrade diff --git a/module_auto_update/wizards/module_upgrade.py b/module_auto_update/wizards/module_upgrade.py new file mode 100644 index 000000000..e9b69e07c --- /dev/null +++ b/module_auto_update/wizards/module_upgrade.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 LasLabs Inc. +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +from odoo import api, models + + +class ModuleUpgrade(models.TransientModel): + _inherit = 'base.module.upgrade' + + @api.multi + def upgrade_module_cancel(self): + return super( + ModuleUpgrade, + self.with_context(retain_checksum_installed=True), + ).upgrade_module_cancel() + + @api.multi + def upgrade_module(self): + self.env['ir.module.module'].update_list() + super(ModuleUpgrade, self).upgrade_module() From bfddb98f6fe670436ce58137b21dd0aeb8b80a5b Mon Sep 17 00:00:00 2001 From: OCA Transbot Date: Sat, 22 Jul 2017 10:29:49 +0200 Subject: [PATCH 2/7] OCA Transbot updated translations from Transifex --- module_auto_update/i18n/ca.po | 59 ++++++++++++++++++++++++++++++++ module_auto_update/i18n/de.po | 59 ++++++++++++++++++++++++++++++++ module_auto_update/i18n/es.po | 59 ++++++++++++++++++++++++++++++++ module_auto_update/i18n/es_MX.po | 59 ++++++++++++++++++++++++++++++++ module_auto_update/i18n/hr.po | 59 ++++++++++++++++++++++++++++++++ module_auto_update/i18n/it.po | 59 ++++++++++++++++++++++++++++++++ module_auto_update/i18n/nl_NL.po | 59 ++++++++++++++++++++++++++++++++ module_auto_update/i18n/pt_BR.po | 59 ++++++++++++++++++++++++++++++++ module_auto_update/i18n/sl.po | 59 ++++++++++++++++++++++++++++++++ module_auto_update/i18n/tr.po | 59 ++++++++++++++++++++++++++++++++ 10 files changed, 590 insertions(+) create mode 100644 module_auto_update/i18n/ca.po create mode 100644 module_auto_update/i18n/de.po create mode 100644 module_auto_update/i18n/es.po create mode 100644 module_auto_update/i18n/es_MX.po create mode 100644 module_auto_update/i18n/hr.po create mode 100644 module_auto_update/i18n/it.po create mode 100644 module_auto_update/i18n/nl_NL.po create mode 100644 module_auto_update/i18n/pt_BR.po create mode 100644 module_auto_update/i18n/sl.po create mode 100644 module_auto_update/i18n/tr.po diff --git a/module_auto_update/i18n/ca.po b/module_auto_update/i18n/ca.po new file mode 100644 index 000000000..90b967e03 --- /dev/null +++ b/module_auto_update/i18n/ca.po @@ -0,0 +1,59 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * module_auto_update +# +# Translators: +# OCA Transbot , 2017 +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 10.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-07-21 02:43+0000\n" +"PO-Revision-Date: 2017-07-21 02:43+0000\n" +"Last-Translator: OCA Transbot , 2017\n" +"Language-Team: Catalan (https://www.transifex.com/oca/teams/23907/ca/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Language: ca\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#. module: module_auto_update +#: model:ir.model.fields,field_description:module_auto_update.field_ir_module_module_checksum_dir +msgid "Checksum dir" +msgstr "" + +#. module: module_auto_update +#: model:ir.model.fields,field_description:module_auto_update.field_ir_module_module_checksum_installed +msgid "Checksum installed" +msgstr "" + +#. module: module_auto_update +#: model:ir.model,name:module_auto_update.model_ir_module_module +msgid "Module" +msgstr "Mòdul" + +#. module: module_auto_update +#: model:ir.model,name:module_auto_update.model_base_module_upgrade +msgid "Module Upgrade" +msgstr "" + +#. module: module_auto_update +#: model:ir.ui.menu,name:module_auto_update.menu_default_modules +msgid "Modules" +msgstr "" + +#. module: module_auto_update +#: model:ir.actions.server,name:module_auto_update.module_action_open_updates +msgid "Open Updates and Update Apps List Server Action" +msgstr "" + +#. module: module_auto_update +#: model:ir.ui.view,arch_db:module_auto_update.module_view_search +msgid "Scheduled Upgrades" +msgstr "" + +#. module: module_auto_update +#: model:ir.ui.menu,name:module_auto_update.module_menu_updates +msgid "Updates" +msgstr "" diff --git a/module_auto_update/i18n/de.po b/module_auto_update/i18n/de.po new file mode 100644 index 000000000..0938aac2a --- /dev/null +++ b/module_auto_update/i18n/de.po @@ -0,0 +1,59 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * module_auto_update +# +# Translators: +# Niki Waibel , 2017 +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 10.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-07-21 02:43+0000\n" +"PO-Revision-Date: 2017-07-21 02:43+0000\n" +"Last-Translator: Niki Waibel , 2017\n" +"Language-Team: German (https://www.transifex.com/oca/teams/23907/de/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Language: de\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#. module: module_auto_update +#: model:ir.model.fields,field_description:module_auto_update.field_ir_module_module_checksum_dir +msgid "Checksum dir" +msgstr "" + +#. module: module_auto_update +#: model:ir.model.fields,field_description:module_auto_update.field_ir_module_module_checksum_installed +msgid "Checksum installed" +msgstr "" + +#. module: module_auto_update +#: model:ir.model,name:module_auto_update.model_ir_module_module +msgid "Module" +msgstr "Modul" + +#. module: module_auto_update +#: model:ir.model,name:module_auto_update.model_base_module_upgrade +msgid "Module Upgrade" +msgstr "Modul aktualisieren" + +#. module: module_auto_update +#: model:ir.ui.menu,name:module_auto_update.menu_default_modules +msgid "Modules" +msgstr "" + +#. module: module_auto_update +#: model:ir.actions.server,name:module_auto_update.module_action_open_updates +msgid "Open Updates and Update Apps List Server Action" +msgstr "" + +#. module: module_auto_update +#: model:ir.ui.view,arch_db:module_auto_update.module_view_search +msgid "Scheduled Upgrades" +msgstr "" + +#. module: module_auto_update +#: model:ir.ui.menu,name:module_auto_update.module_menu_updates +msgid "Updates" +msgstr "" diff --git a/module_auto_update/i18n/es.po b/module_auto_update/i18n/es.po new file mode 100644 index 000000000..a318b0947 --- /dev/null +++ b/module_auto_update/i18n/es.po @@ -0,0 +1,59 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * module_auto_update +# +# Translators: +# Pedro M. Baeza , 2017 +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 10.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-07-21 02:43+0000\n" +"PO-Revision-Date: 2017-07-21 02:43+0000\n" +"Last-Translator: Pedro M. Baeza , 2017\n" +"Language-Team: Spanish (https://www.transifex.com/oca/teams/23907/es/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Language: es\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#. module: module_auto_update +#: model:ir.model.fields,field_description:module_auto_update.field_ir_module_module_checksum_dir +msgid "Checksum dir" +msgstr "" + +#. module: module_auto_update +#: model:ir.model.fields,field_description:module_auto_update.field_ir_module_module_checksum_installed +msgid "Checksum installed" +msgstr "" + +#. module: module_auto_update +#: model:ir.model,name:module_auto_update.model_ir_module_module +msgid "Module" +msgstr "Módulo" + +#. module: module_auto_update +#: model:ir.model,name:module_auto_update.model_base_module_upgrade +msgid "Module Upgrade" +msgstr "Actualización de módulo" + +#. module: module_auto_update +#: model:ir.ui.menu,name:module_auto_update.menu_default_modules +msgid "Modules" +msgstr "" + +#. module: module_auto_update +#: model:ir.actions.server,name:module_auto_update.module_action_open_updates +msgid "Open Updates and Update Apps List Server Action" +msgstr "" + +#. module: module_auto_update +#: model:ir.ui.view,arch_db:module_auto_update.module_view_search +msgid "Scheduled Upgrades" +msgstr "" + +#. module: module_auto_update +#: model:ir.ui.menu,name:module_auto_update.module_menu_updates +msgid "Updates" +msgstr "" diff --git a/module_auto_update/i18n/es_MX.po b/module_auto_update/i18n/es_MX.po new file mode 100644 index 000000000..6a52834e7 --- /dev/null +++ b/module_auto_update/i18n/es_MX.po @@ -0,0 +1,59 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * module_auto_update +# +# Translators: +# OCA Transbot , 2017 +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 10.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-07-21 02:43+0000\n" +"PO-Revision-Date: 2017-07-21 02:43+0000\n" +"Last-Translator: OCA Transbot , 2017\n" +"Language-Team: Spanish (Mexico) (https://www.transifex.com/oca/teams/23907/es_MX/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Language: es_MX\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#. module: module_auto_update +#: model:ir.model.fields,field_description:module_auto_update.field_ir_module_module_checksum_dir +msgid "Checksum dir" +msgstr "" + +#. module: module_auto_update +#: model:ir.model.fields,field_description:module_auto_update.field_ir_module_module_checksum_installed +msgid "Checksum installed" +msgstr "" + +#. module: module_auto_update +#: model:ir.model,name:module_auto_update.model_ir_module_module +msgid "Module" +msgstr "Módulo" + +#. module: module_auto_update +#: model:ir.model,name:module_auto_update.model_base_module_upgrade +msgid "Module Upgrade" +msgstr "" + +#. module: module_auto_update +#: model:ir.ui.menu,name:module_auto_update.menu_default_modules +msgid "Modules" +msgstr "" + +#. module: module_auto_update +#: model:ir.actions.server,name:module_auto_update.module_action_open_updates +msgid "Open Updates and Update Apps List Server Action" +msgstr "" + +#. module: module_auto_update +#: model:ir.ui.view,arch_db:module_auto_update.module_view_search +msgid "Scheduled Upgrades" +msgstr "" + +#. module: module_auto_update +#: model:ir.ui.menu,name:module_auto_update.module_menu_updates +msgid "Updates" +msgstr "" diff --git a/module_auto_update/i18n/hr.po b/module_auto_update/i18n/hr.po new file mode 100644 index 000000000..7cae9d5f4 --- /dev/null +++ b/module_auto_update/i18n/hr.po @@ -0,0 +1,59 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * module_auto_update +# +# Translators: +# Bole , 2017 +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 10.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-07-21 02:43+0000\n" +"PO-Revision-Date: 2017-07-21 02:43+0000\n" +"Last-Translator: Bole , 2017\n" +"Language-Team: Croatian (https://www.transifex.com/oca/teams/23907/hr/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Language: hr\n" +"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" + +#. module: module_auto_update +#: model:ir.model.fields,field_description:module_auto_update.field_ir_module_module_checksum_dir +msgid "Checksum dir" +msgstr "" + +#. module: module_auto_update +#: model:ir.model.fields,field_description:module_auto_update.field_ir_module_module_checksum_installed +msgid "Checksum installed" +msgstr "" + +#. module: module_auto_update +#: model:ir.model,name:module_auto_update.model_ir_module_module +msgid "Module" +msgstr "Modul" + +#. module: module_auto_update +#: model:ir.model,name:module_auto_update.model_base_module_upgrade +msgid "Module Upgrade" +msgstr "" + +#. module: module_auto_update +#: model:ir.ui.menu,name:module_auto_update.menu_default_modules +msgid "Modules" +msgstr "" + +#. module: module_auto_update +#: model:ir.actions.server,name:module_auto_update.module_action_open_updates +msgid "Open Updates and Update Apps List Server Action" +msgstr "" + +#. module: module_auto_update +#: model:ir.ui.view,arch_db:module_auto_update.module_view_search +msgid "Scheduled Upgrades" +msgstr "" + +#. module: module_auto_update +#: model:ir.ui.menu,name:module_auto_update.module_menu_updates +msgid "Updates" +msgstr "" diff --git a/module_auto_update/i18n/it.po b/module_auto_update/i18n/it.po new file mode 100644 index 000000000..91f03c624 --- /dev/null +++ b/module_auto_update/i18n/it.po @@ -0,0 +1,59 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * module_auto_update +# +# Translators: +# OCA Transbot , 2017 +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 10.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-07-21 02:43+0000\n" +"PO-Revision-Date: 2017-07-21 02:43+0000\n" +"Last-Translator: OCA Transbot , 2017\n" +"Language-Team: Italian (https://www.transifex.com/oca/teams/23907/it/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Language: it\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#. module: module_auto_update +#: model:ir.model.fields,field_description:module_auto_update.field_ir_module_module_checksum_dir +msgid "Checksum dir" +msgstr "" + +#. module: module_auto_update +#: model:ir.model.fields,field_description:module_auto_update.field_ir_module_module_checksum_installed +msgid "Checksum installed" +msgstr "" + +#. module: module_auto_update +#: model:ir.model,name:module_auto_update.model_ir_module_module +msgid "Module" +msgstr "Modulo" + +#. module: module_auto_update +#: model:ir.model,name:module_auto_update.model_base_module_upgrade +msgid "Module Upgrade" +msgstr "" + +#. module: module_auto_update +#: model:ir.ui.menu,name:module_auto_update.menu_default_modules +msgid "Modules" +msgstr "" + +#. module: module_auto_update +#: model:ir.actions.server,name:module_auto_update.module_action_open_updates +msgid "Open Updates and Update Apps List Server Action" +msgstr "" + +#. module: module_auto_update +#: model:ir.ui.view,arch_db:module_auto_update.module_view_search +msgid "Scheduled Upgrades" +msgstr "" + +#. module: module_auto_update +#: model:ir.ui.menu,name:module_auto_update.module_menu_updates +msgid "Updates" +msgstr "" diff --git a/module_auto_update/i18n/nl_NL.po b/module_auto_update/i18n/nl_NL.po new file mode 100644 index 000000000..340b85847 --- /dev/null +++ b/module_auto_update/i18n/nl_NL.po @@ -0,0 +1,59 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * module_auto_update +# +# Translators: +# Peter Hageman , 2017 +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 10.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-07-21 02:43+0000\n" +"PO-Revision-Date: 2017-07-21 02:43+0000\n" +"Last-Translator: Peter Hageman , 2017\n" +"Language-Team: Dutch (Netherlands) (https://www.transifex.com/oca/teams/23907/nl_NL/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Language: nl_NL\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#. module: module_auto_update +#: model:ir.model.fields,field_description:module_auto_update.field_ir_module_module_checksum_dir +msgid "Checksum dir" +msgstr "" + +#. module: module_auto_update +#: model:ir.model.fields,field_description:module_auto_update.field_ir_module_module_checksum_installed +msgid "Checksum installed" +msgstr "" + +#. module: module_auto_update +#: model:ir.model,name:module_auto_update.model_ir_module_module +msgid "Module" +msgstr "Module" + +#. module: module_auto_update +#: model:ir.model,name:module_auto_update.model_base_module_upgrade +msgid "Module Upgrade" +msgstr "" + +#. module: module_auto_update +#: model:ir.ui.menu,name:module_auto_update.menu_default_modules +msgid "Modules" +msgstr "" + +#. module: module_auto_update +#: model:ir.actions.server,name:module_auto_update.module_action_open_updates +msgid "Open Updates and Update Apps List Server Action" +msgstr "" + +#. module: module_auto_update +#: model:ir.ui.view,arch_db:module_auto_update.module_view_search +msgid "Scheduled Upgrades" +msgstr "" + +#. module: module_auto_update +#: model:ir.ui.menu,name:module_auto_update.module_menu_updates +msgid "Updates" +msgstr "" diff --git a/module_auto_update/i18n/pt_BR.po b/module_auto_update/i18n/pt_BR.po new file mode 100644 index 000000000..aef834e1b --- /dev/null +++ b/module_auto_update/i18n/pt_BR.po @@ -0,0 +1,59 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * module_auto_update +# +# Translators: +# OCA Transbot , 2017 +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 10.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-07-21 02:43+0000\n" +"PO-Revision-Date: 2017-07-21 02:43+0000\n" +"Last-Translator: OCA Transbot , 2017\n" +"Language-Team: Portuguese (Brazil) (https://www.transifex.com/oca/teams/23907/pt_BR/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Language: pt_BR\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" + +#. module: module_auto_update +#: model:ir.model.fields,field_description:module_auto_update.field_ir_module_module_checksum_dir +msgid "Checksum dir" +msgstr "" + +#. module: module_auto_update +#: model:ir.model.fields,field_description:module_auto_update.field_ir_module_module_checksum_installed +msgid "Checksum installed" +msgstr "" + +#. module: module_auto_update +#: model:ir.model,name:module_auto_update.model_ir_module_module +msgid "Module" +msgstr "Módulo" + +#. module: module_auto_update +#: model:ir.model,name:module_auto_update.model_base_module_upgrade +msgid "Module Upgrade" +msgstr "" + +#. module: module_auto_update +#: model:ir.ui.menu,name:module_auto_update.menu_default_modules +msgid "Modules" +msgstr "" + +#. module: module_auto_update +#: model:ir.actions.server,name:module_auto_update.module_action_open_updates +msgid "Open Updates and Update Apps List Server Action" +msgstr "" + +#. module: module_auto_update +#: model:ir.ui.view,arch_db:module_auto_update.module_view_search +msgid "Scheduled Upgrades" +msgstr "" + +#. module: module_auto_update +#: model:ir.ui.menu,name:module_auto_update.module_menu_updates +msgid "Updates" +msgstr "" diff --git a/module_auto_update/i18n/sl.po b/module_auto_update/i18n/sl.po new file mode 100644 index 000000000..b14fb1443 --- /dev/null +++ b/module_auto_update/i18n/sl.po @@ -0,0 +1,59 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * module_auto_update +# +# Translators: +# OCA Transbot , 2017 +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 10.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-07-21 02:43+0000\n" +"PO-Revision-Date: 2017-07-21 02:43+0000\n" +"Last-Translator: OCA Transbot , 2017\n" +"Language-Team: Slovenian (https://www.transifex.com/oca/teams/23907/sl/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Language: sl\n" +"Plural-Forms: nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3);\n" + +#. module: module_auto_update +#: model:ir.model.fields,field_description:module_auto_update.field_ir_module_module_checksum_dir +msgid "Checksum dir" +msgstr "" + +#. module: module_auto_update +#: model:ir.model.fields,field_description:module_auto_update.field_ir_module_module_checksum_installed +msgid "Checksum installed" +msgstr "" + +#. module: module_auto_update +#: model:ir.model,name:module_auto_update.model_ir_module_module +msgid "Module" +msgstr "Modul" + +#. module: module_auto_update +#: model:ir.model,name:module_auto_update.model_base_module_upgrade +msgid "Module Upgrade" +msgstr "" + +#. module: module_auto_update +#: model:ir.ui.menu,name:module_auto_update.menu_default_modules +msgid "Modules" +msgstr "" + +#. module: module_auto_update +#: model:ir.actions.server,name:module_auto_update.module_action_open_updates +msgid "Open Updates and Update Apps List Server Action" +msgstr "" + +#. module: module_auto_update +#: model:ir.ui.view,arch_db:module_auto_update.module_view_search +msgid "Scheduled Upgrades" +msgstr "" + +#. module: module_auto_update +#: model:ir.ui.menu,name:module_auto_update.module_menu_updates +msgid "Updates" +msgstr "" diff --git a/module_auto_update/i18n/tr.po b/module_auto_update/i18n/tr.po new file mode 100644 index 000000000..2b6b538a1 --- /dev/null +++ b/module_auto_update/i18n/tr.po @@ -0,0 +1,59 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * module_auto_update +# +# Translators: +# OCA Transbot , 2017 +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 10.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-07-21 02:43+0000\n" +"PO-Revision-Date: 2017-07-21 02:43+0000\n" +"Last-Translator: OCA Transbot , 2017\n" +"Language-Team: Turkish (https://www.transifex.com/oca/teams/23907/tr/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Language: tr\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" + +#. module: module_auto_update +#: model:ir.model.fields,field_description:module_auto_update.field_ir_module_module_checksum_dir +msgid "Checksum dir" +msgstr "" + +#. module: module_auto_update +#: model:ir.model.fields,field_description:module_auto_update.field_ir_module_module_checksum_installed +msgid "Checksum installed" +msgstr "" + +#. module: module_auto_update +#: model:ir.model,name:module_auto_update.model_ir_module_module +msgid "Module" +msgstr "Modül" + +#. module: module_auto_update +#: model:ir.model,name:module_auto_update.model_base_module_upgrade +msgid "Module Upgrade" +msgstr "" + +#. module: module_auto_update +#: model:ir.ui.menu,name:module_auto_update.menu_default_modules +msgid "Modules" +msgstr "" + +#. module: module_auto_update +#: model:ir.actions.server,name:module_auto_update.module_action_open_updates +msgid "Open Updates and Update Apps List Server Action" +msgstr "" + +#. module: module_auto_update +#: model:ir.ui.view,arch_db:module_auto_update.module_view_search +msgid "Scheduled Upgrades" +msgstr "" + +#. module: module_auto_update +#: model:ir.ui.menu,name:module_auto_update.module_menu_updates +msgid "Updates" +msgstr "" From 10a6a1aa420551a49f71d1a6b8ca93c8bc59fb59 Mon Sep 17 00:00:00 2001 From: Jairo Llopis Date: Wed, 2 Aug 2017 10:35:52 +0200 Subject: [PATCH 3/7] [REF][module_auto_update] Forward port v9 improvements * Recompute dir hashes only when needed - By removing the recomputation from `update_list` we get faster CLI module upgrades and it only performs the autoupdate when using the autoupdate wizard or cron. * Pass tests if addon is in readonly directory * Set dependencies to upgrade [FIX][module_auto_update] Pass tests if addon is in readonly directory [FIX][module_auto_update] Set dependencies to upgrade --- module_auto_update/models/module.py | 25 ++++++++----------- module_auto_update/tests/test_module.py | 18 +++++++++---- .../tests/test_module_upgrade.py | 20 ++++++++------- module_auto_update/wizards/module_upgrade.py | 16 ++++++++++-- 4 files changed, 48 insertions(+), 31 deletions(-) diff --git a/module_auto_update/models/module.py b/module_auto_update/models/module.py index 4d9ccec59..f191dcb75 100644 --- a/module_auto_update/models/module.py +++ b/module_auto_update/models/module.py @@ -30,11 +30,16 @@ class Module(models.Model): ).split(",") for r in self: - r.checksum_dir = dirhash( - get_module_path(r.name), - 'sha1', - excluded_extensions=exclude, - ) + try: + r.checksum_dir = dirhash( + get_module_path(r.name), + 'sha1', + excluded_extensions=exclude, + ) + except TypeError: + _logger.debug( + "Cannot compute dir hash for %s, module not found", + r.display_name) def _store_checksum_installed(self, vals): if self.env.context.get('retain_checksum_installed'): @@ -66,16 +71,6 @@ class Module(models.Model): res._store_checksum_installed(vals) return res - @api.model - def update_list(self): - res = super(Module, self).update_list() - installed_modules = self.search([('state', '=', 'installed')]) - upgradeable_modules = installed_modules.filtered( - lambda r: r.checksum_dir != r.checksum_installed, - ) - upgradeable_modules.write({'state': "to upgrade"}) - return res - @api.multi def write(self, vals): res = super(Module, self).write(vals) diff --git a/module_auto_update/tests/test_module.py b/module_auto_update/tests/test_module.py index 5d499fc27..2cdbba44b 100644 --- a/module_auto_update/tests/test_module.py +++ b/module_auto_update/tests/test_module.py @@ -3,6 +3,7 @@ # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). import logging +import os import tempfile import mock @@ -35,6 +36,7 @@ class TestModule(TransactionCase): 'sha1', excluded_extensions=['pyc', 'pyo'], ) + 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): @@ -52,6 +54,8 @@ class TestModule(TransactionCase): def test_compute_checksum_dir_ignore_excluded(self): """It should exclude .pyc/.pyo extensions from checksum calculations""" + if not self.own_writeable: + self.skipTest("Own directory not writeable") with tempfile.NamedTemporaryFile( suffix='.pyc', dir=self.own_dir_path): self.assertEqual( @@ -62,6 +66,8 @@ class TestModule(TransactionCase): def test_compute_checksum_dir_recomputes_when_file_added(self): """It should return a different value when a non-.pyc/.pyo file is added to the module directory""" + if not self.own_writeable: + self.skipTest("Own directory not writeable") with tempfile.NamedTemporaryFile( suffix='.py', dir=self.own_dir_path): self.assertNotEqual( @@ -154,31 +160,33 @@ class TestModule(TransactionCase): ) @mock.patch('%s.get_module_path' % model) - def test_update_list(self, get_module_path_mock): + 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'""" - get_module_path_mock.return_value = self.own_dir_path + 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['ir.module.module'].update_list() + self.env['base.module.upgrade'].get_module_list() self.assertEqual( test_module.state, 'to upgrade', 'List update does not mark upgradeable modules "to upgrade"', ) - def test_update_list_only_changes_installed(self): + @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['ir.module.module'].update_list() + self.env['base.module.upgrade'].get_module_list() self.assertNotEqual( test_module.state, 'to upgrade', 'List update changed state of an uninstalled module', diff --git a/module_auto_update/tests/test_module_upgrade.py b/module_auto_update/tests/test_module_upgrade.py index edc24fd8e..0f908d5e0 100644 --- a/module_auto_update/tests/test_module_upgrade.py +++ b/module_auto_update/tests/test_module_upgrade.py @@ -31,12 +31,14 @@ class TestModuleUpgrade(TransactionCase): @mock.patch.object(Registry, 'new') def test_upgrade_module(self, new_mock): - """It should call update_list method on ir.module.module""" - update_list_mock = mock.MagicMock() - self.env['ir.module.module']._patch_method( - 'update_list', - update_list_mock, - ) - self.env['base.module.upgrade'].upgrade_module() - update_list_mock.assert_called_once_with() - self.env['ir.module.module']._revert_method('update_list') + """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/module_upgrade.py b/module_auto_update/wizards/module_upgrade.py index e9b69e07c..0ccfeba9a 100644 --- a/module_auto_update/wizards/module_upgrade.py +++ b/module_auto_update/wizards/module_upgrade.py @@ -8,6 +8,16 @@ from odoo import api, models class ModuleUpgrade(models.TransientModel): _inherit = 'base.module.upgrade' + @api.model + def get_module_list(self): + 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_cancel(self): return super( @@ -17,5 +27,7 @@ class ModuleUpgrade(models.TransientModel): @api.multi def upgrade_module(self): - self.env['ir.module.module'].update_list() - super(ModuleUpgrade, self).upgrade_module() + # Compute updates by checksum when called in @api.model fashion + if not self: + self.get_module_list() + return super(ModuleUpgrade, self).upgrade_module() From 84fb073ffe7dbf0dd51738ce6d06330fa4495cf0 Mon Sep 17 00:00:00 2001 From: Dave Lasley Date: Fri, 11 Aug 2017 09:10:12 -0700 Subject: [PATCH 4/7] [FIX] module_auto_update: Don't compute upgrade on uninstall * `button_uninstall` calls `get_module_list`, which subsequently forces module upgrades. Add a context validation to prevent that * Add test for button_immediate_uninstall * Mock commit for immediate uninstall * Fix immediate uninstall test * Switch nesting * Bump module version --- module_auto_update/__manifest__.py | 2 +- module_auto_update/models/module.py | 7 ++++ module_auto_update/tests/test_module.py | 39 ++++++++++++++++++++ module_auto_update/wizards/module_upgrade.py | 13 ++++--- 4 files changed, 54 insertions(+), 7 deletions(-) diff --git a/module_auto_update/__manifest__.py b/module_auto_update/__manifest__.py index db65d269a..57a5846db 100644 --- a/module_auto_update/__manifest__.py +++ b/module_auto_update/__manifest__.py @@ -5,7 +5,7 @@ { 'name': 'Module Auto Update', 'summary': 'Automatically update Odoo modules', - 'version': '10.0.1.0.0', + 'version': '10.0.1.0.1', 'category': 'Extra Tools', 'website': 'https://odoo-community.org/', 'author': 'LasLabs, ' diff --git a/module_auto_update/models/module.py b/module_auto_update/models/module.py index f191dcb75..273d72588 100644 --- a/module_auto_update/models/module.py +++ b/module_auto_update/models/module.py @@ -51,6 +51,13 @@ class Module(models.Model): elif vals.get('state') == 'uninstalled': self.write({'checksum_installed': False}) + @api.multi + def button_uninstall(self): + return super( + Module, + self.with_context(module_uninstall=True), + ).button_uninstall() + @api.multi def button_uninstall_cancel(self): return super( diff --git a/module_auto_update/tests/test_module.py b/module_auto_update/tests/test_module.py index 2cdbba44b..bd25ed07a 100644 --- a/module_auto_update/tests/test_module.py +++ b/module_auto_update/tests/test_module.py @@ -22,6 +22,10 @@ except ImportError: model = 'odoo.addons.module_auto_update.models.module' +class EndTestException(Exception): + pass + + class TestModule(TransactionCase): def setUp(self): @@ -122,6 +126,41 @@ class TestModule(TransactionCase): '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'}) diff --git a/module_auto_update/wizards/module_upgrade.py b/module_auto_update/wizards/module_upgrade.py index 0ccfeba9a..188030dd8 100644 --- a/module_auto_update/wizards/module_upgrade.py +++ b/module_auto_update/wizards/module_upgrade.py @@ -10,12 +10,13 @@ class ModuleUpgrade(models.TransientModel): @api.model def get_module_list(self): - 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 not self.env.context.get('module_uninstall'): + 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 From 9ec2de1fad12a8ccb7c6dfdc6714feb1ce6739b3 Mon Sep 17 00:00:00 2001 From: Jairo Llopis Date: Mon, 28 Aug 2017 12:33:43 +0200 Subject: [PATCH 5/7] [FIX][module_auto_update] Record base addon checksum (#948) --- module_auto_update/models/module.py | 39 ++++++-------------- module_auto_update/tests/test_module.py | 17 ++++----- module_auto_update/wizards/module_upgrade.py | 14 ++++++- 3 files changed, 32 insertions(+), 38 deletions(-) diff --git a/module_auto_update/models/module.py b/module_auto_update/models/module.py index 273d72588..398809e61 100644 --- a/module_auto_update/models/module.py +++ b/module_auto_update/models/module.py @@ -41,36 +41,21 @@ class Module(models.Model): "Cannot compute dir hash for %s, module not found", r.display_name) + @api.multi def _store_checksum_installed(self, vals): - if self.env.context.get('retain_checksum_installed'): - return + """Store the right installed checksum, if addon is installed.""" if 'checksum_installed' not in vals: - if vals.get('state') == 'installed': - for r in self: - r.checksum_installed = r.checksum_dir - elif vals.get('state') == 'uninstalled': + try: + version = vals["latest_version"] + except KeyError: + return # Not [un]installing/updating any addon + if version is False: + # Uninstalling self.write({'checksum_installed': False}) - - @api.multi - def button_uninstall(self): - return super( - Module, - self.with_context(module_uninstall=True), - ).button_uninstall() - - @api.multi - def button_uninstall_cancel(self): - return super( - Module, - self.with_context(retain_checksum_installed=True), - ).button_uninstall_cancel() - - @api.multi - def button_upgrade_cancel(self): - return super( - Module, - self.with_context(retain_checksum_installed=True), - ).button_upgrade_cancel() + else: + # Installing or updating + for one in self: + one.checksum_installed = one.checksum_dir @api.model def create(self, vals): diff --git a/module_auto_update/tests/test_module.py b/module_auto_update/tests/test_module.py index bd25ed07a..f8fcf7278 100644 --- a/module_auto_update/tests/test_module.py +++ b/module_auto_update/tests/test_module.py @@ -10,6 +10,7 @@ import mock from odoo.modules import get_module_path from odoo.tests.common import TransactionCase +from odoo.tools import mute_logger from .. import post_init_hook @@ -81,24 +82,19 @@ class TestModule(TransactionCase): def test_store_checksum_installed_state_installed(self): """It should set the module's checksum_installed equal to - checksum_dir when vals contain state 'installed'""" + checksum_dir when vals contain a ``latest_version`` str.""" self.own_module.checksum_installed = 'test' - self.own_module._store_checksum_installed({'state': 'installed'}) + self.own_module._store_checksum_installed({'latest_version': '1.0'}) self.assertEqual( self.own_module.checksum_installed, self.own_module.checksum_dir, - 'Setting state to installed does not store checksum_dir ' - 'as checksum_installed', ) def test_store_checksum_installed_state_uninstalled(self): """It should clear the module's checksum_installed when vals - contain state 'uninstalled'""" + contain ``"latest_version": False``""" self.own_module.checksum_installed = 'test' - self.own_module._store_checksum_installed({'state': 'uninstalled'}) - self.assertEqual( - self.own_module.checksum_installed, False, - 'Setting state to uninstalled does not clear checksum_installed', - ) + 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 @@ -198,6 +194,7 @@ class TestModule(TransactionCase): '_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 diff --git a/module_auto_update/wizards/module_upgrade.py b/module_auto_update/wizards/module_upgrade.py index 188030dd8..eaf278651 100644 --- a/module_auto_update/wizards/module_upgrade.py +++ b/module_auto_update/wizards/module_upgrade.py @@ -31,4 +31,16 @@ class ModuleUpgrade(models.TransientModel): # Compute updates by checksum when called in @api.model fashion if not self: self.get_module_list() - return super(ModuleUpgrade, self).upgrade_module() + # Get base adddon status before updating + base = self.env["ir.module.module"].search([("name", "=", "base")]) + pre_state = base.state + result = super(ModuleUpgrade, self).upgrade_module() + # Update base addon checksum if its state changed + base.invalidate_cache() + if base.state != pre_state: + # This triggers the write hook that should have been triggered + # when the module was [un]installed/updated in the base-only + # module graph inside above call to super(), and updates its + # dir checksum as needed + base.latest_version = base.latest_version + return result From f88bcaffec6ebd72ae2895c24547add29c60180b Mon Sep 17 00:00:00 2001 From: Jairo Llopis Date: Tue, 29 Aug 2017 10:55:36 +0200 Subject: [PATCH 6/7] [FIX][module_auto_update] Always store changes in lower graphs The same problem that was fixed for the `base` addon in #948 happened with random addons that do not depend on `module_auto_update` (a.k.a. any addon) that Odoo decided to load before that one in the graph. Now we always check for all addons if their state has changed, and make sure to trigger the udpate mechanism that stores the right value in `installed_checksum_dir` field. If you installed and uninstalled the addon right away, you'd get a ProgrammingError saying that some columns exist no more. Checks are done now using `search_read`, which lets us limit the fields being fetched, and the environment is cleared to make sure nothing fails. Also we now guess if this own addon has been uninstalled and skip further logic if so, given it would hit broken triggers otherwise as it did before. --- module_auto_update/wizards/module_upgrade.py | 53 +++++++++++--------- 1 file changed, 28 insertions(+), 25 deletions(-) diff --git a/module_auto_update/wizards/module_upgrade.py b/module_auto_update/wizards/module_upgrade.py index eaf278651..3ab297d18 100644 --- a/module_auto_update/wizards/module_upgrade.py +++ b/module_auto_update/wizards/module_upgrade.py @@ -10,37 +10,40 @@ class ModuleUpgrade(models.TransientModel): @api.model def get_module_list(self): - if not self.env.context.get('module_uninstall'): - 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() + """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() return super(ModuleUpgrade, self).get_module_list() - @api.multi - def upgrade_module_cancel(self): - return super( - ModuleUpgrade, - self.with_context(retain_checksum_installed=True), - ).upgrade_module_cancel() - @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() - # Get base adddon status before updating - base = self.env["ir.module.module"].search([("name", "=", "base")]) - pre_state = base.state + 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() - # Update base addon checksum if its state changed - base.invalidate_cache() - if base.state != pre_state: - # This triggers the write hook that should have been triggered - # when the module was [un]installed/updated in the base-only - # module graph inside above call to super(), and updates its - # dir checksum as needed - base.latest_version = base.latest_version + # 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 From 5bef5cc20957be199b2fd94668861f0374683b30 Mon Sep 17 00:00:00 2001 From: Jairo Llopis Date: Fri, 5 Jan 2018 13:55:52 +0000 Subject: [PATCH 7/7] [MIG] module_auto_update: Migrate to v11 --- module_auto_update/README.rst | 7 ++++--- module_auto_update/__init__.py | 2 -- module_auto_update/__manifest__.py | 4 ++-- module_auto_update/data/cron_data.xml | 11 ++++++++--- module_auto_update/hooks.py | 1 - module_auto_update/models/__init__.py | 2 -- module_auto_update/models/module.py | 1 - module_auto_update/tests/__init__.py | 2 -- module_auto_update/tests/test_module.py | 1 - module_auto_update/tests/test_module_upgrade.py | 1 - module_auto_update/wizards/__init__.py | 2 -- module_auto_update/wizards/module_upgrade.py | 2 +- requirements.txt | 1 + 13 files changed, 16 insertions(+), 21 deletions(-) create mode 100644 requirements.txt diff --git a/module_auto_update/README.rst b/module_auto_update/README.rst index 3535f4fb2..6349fdd7b 100644 --- a/module_auto_update/README.rst +++ b/module_auto_update/README.rst @@ -15,7 +15,7 @@ Installation Prior to installing this module, you need to: -#. Install checksumdir with `pip install checksumdir` +#. Install checksumdir with ``pip install checksumdir`` #. Ensure all installed modules are up-to-date. When installed, this module will assume the versions found in the addons directories are currently installed. Configuration @@ -23,7 +23,7 @@ Configuration The default time for checking and applying upgrades is 3:00 AM (UTC). To change this schedule, modify the "Perform Module Upgrades" scheduled action. -This module will ignore .pyc and .pyo file extensions by default. To modify this, create a module_auto_update.checksum_excluded_extensions system parameter with the desired extensions listed as comma-separated values. +This module will ignore ``.pyc`` and ``.pyo`` file extensions by default. To modify this, create a ``module_auto_update.checksum_excluded_extensions`` system parameter with the desired extensions listed as comma-separated values. Usage ===== @@ -34,7 +34,7 @@ To perform upgrades manually, click the "Apply Scheduled Upgrades" menu item in .. 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/10.0 + :target: https://runbot.odoo-community.org/runbot/149/11.0 Bug Tracker =========== @@ -57,6 +57,7 @@ Contributors * Brent Hughes * Juan José Scarafía +* Jairo Llopis Do not contact contributors directly about support or help with technical issues. diff --git a/module_auto_update/__init__.py b/module_auto_update/__init__.py index 36f555442..c80c51237 100644 --- a/module_auto_update/__init__.py +++ b/module_auto_update/__init__.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- -# Copyright 2017 LasLabs Inc. # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). from . import models diff --git a/module_auto_update/__manifest__.py b/module_auto_update/__manifest__.py index 57a5846db..50a6578ae 100644 --- a/module_auto_update/__manifest__.py +++ b/module_auto_update/__manifest__.py @@ -1,15 +1,15 @@ -# -*- coding: utf-8 -*- # Copyright 2017 LasLabs Inc. # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). { 'name': 'Module Auto Update', 'summary': 'Automatically update Odoo modules', - 'version': '10.0.1.0.1', + 'version': '11.0.1.0.0', 'category': 'Extra Tools', 'website': 'https://odoo-community.org/', 'author': 'LasLabs, ' 'Juan José Scarafía, ' + 'Tecnativa, ' 'Odoo Community Association (OCA)', 'license': 'LGPL-3', 'application': False, diff --git a/module_auto_update/data/cron_data.xml b/module_auto_update/data/cron_data.xml index 1745fe0c9..9b5b1cc6b 100644 --- a/module_auto_update/data/cron_data.xml +++ b/module_auto_update/data/cron_data.xml @@ -1,4 +1,8 @@ + + Perform Module Upgrades @@ -8,8 +12,9 @@ days -1 - base.module.upgrade - upgrade_module - + + code + model.upgrade_module() + diff --git a/module_auto_update/hooks.py b/module_auto_update/hooks.py index f062966c3..56d60c6ef 100644 --- a/module_auto_update/hooks.py +++ b/module_auto_update/hooks.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright 2017 LasLabs Inc. # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). diff --git a/module_auto_update/models/__init__.py b/module_auto_update/models/__init__.py index b27944126..e5ee3ea66 100644 --- a/module_auto_update/models/__init__.py +++ b/module_auto_update/models/__init__.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- -# Copyright 2017 LasLabs Inc. # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). from . import module diff --git a/module_auto_update/models/module.py b/module_auto_update/models/module.py index 398809e61..271bc5700 100644 --- a/module_auto_update/models/module.py +++ b/module_auto_update/models/module.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright 2017 LasLabs Inc. # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). diff --git a/module_auto_update/tests/__init__.py b/module_auto_update/tests/__init__.py index 237970451..06952e34e 100644 --- a/module_auto_update/tests/__init__.py +++ b/module_auto_update/tests/__init__.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- -# Copyright 2017 LasLabs Inc. # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). from . import test_module diff --git a/module_auto_update/tests/test_module.py b/module_auto_update/tests/test_module.py index f8fcf7278..08fda9841 100644 --- a/module_auto_update/tests/test_module.py +++ b/module_auto_update/tests/test_module.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright 2017 LasLabs Inc. # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). diff --git a/module_auto_update/tests/test_module_upgrade.py b/module_auto_update/tests/test_module_upgrade.py index 0f908d5e0..880c80d00 100644 --- a/module_auto_update/tests/test_module_upgrade.py +++ b/module_auto_update/tests/test_module_upgrade.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright 2017 LasLabs Inc. # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). diff --git a/module_auto_update/wizards/__init__.py b/module_auto_update/wizards/__init__.py index 58cb00103..0448de3cf 100644 --- a/module_auto_update/wizards/__init__.py +++ b/module_auto_update/wizards/__init__.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- -# Copyright 2017 LasLabs Inc. # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). from . import module_upgrade diff --git a/module_auto_update/wizards/module_upgrade.py b/module_auto_update/wizards/module_upgrade.py index 3ab297d18..8634c38e5 100644 --- a/module_auto_update/wizards/module_upgrade.py +++ b/module_auto_update/wizards/module_upgrade.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright 2017 LasLabs Inc. # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). @@ -9,6 +8,7 @@ class ModuleUpgrade(models.TransientModel): _inherit = 'base.module.upgrade' @api.model + @api.returns('ir.module.module') def get_module_list(self): """Set modules to upgrade searching by their dir checksum.""" Module = self.env["ir.module.module"] diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 000000000..1ff56e771 --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +checksumdir