Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 76 additions & 0 deletions module_auto_update/README.rst
Original file line number Diff line number Diff line change
@@ -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
<https://github.com/OCA/server-tools/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 <https://github.com/OCA/maintainer-tools/blob/master/template/module/static/description/icon.svg>`_.

Contributors
------------

* Brent Hughes <brent.hughes@laslabs.com>
* Juan José Scarafía <jjs@adhoc.com.ar>

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.
7 changes: 7 additions & 0 deletions module_auto_update/__init__.py
Original file line number Diff line number Diff line change
@@ -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
30 changes: 30 additions & 0 deletions module_auto_update/__manifest__.py
Original file line number Diff line number Diff line change
@@ -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',
],
}
15 changes: 15 additions & 0 deletions module_auto_update/data/cron_data.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo noupdate="1">
<record model="ir.cron" id="module_check_upgrades_cron">
<field name="name">Perform Module Upgrades</field>
<field name="active" eval="True"/>
<field name="user_id" ref="base.user_root"/>
<field name="interval_number">1</field>
<field name="interval_type">days</field>
<field name="numbercall">-1</field>
<field name="nextcall" eval="(DateTime.now() + timedelta(days= +1)).strftime('%Y-%m-%d 3:00:00')"/>
<field name="model">base.module.upgrade</field>
<field name="function">upgrade_module</field>
<field name="args" eval="'()'"/>
</record>
</odoo>
14 changes: 14 additions & 0 deletions module_auto_update/hooks.py
Original file line number Diff line number Diff line change
@@ -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, {})

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't tested. Also I don't think it's implemented correctly, because coverage should have a hit regardless of test status for hooks.

installed_modules = env['ir.module.module'].search([
('state', '=', 'installed'),
])
for r in installed_modules:
r.checksum_installed = r.checksum_dir
5 changes: 5 additions & 0 deletions module_auto_update/models/__init__.py
Original file line number Diff line number Diff line change
@@ -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
83 changes: 83 additions & 0 deletions module_auto_update/models/module.py
Original file line number Diff line number Diff line change
@@ -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()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel this one should be read only, mostly now that you are exposing it in the UI.
You should also add string and help attributes to both fields for the same reason, to let the user know what's this.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense. Does exposing these fields in the UI have value, or should I take it out again? I keep wavering on this.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The value would be for the human. If a human cares, expose it - otherwise don't. In this case, I'd say don't.


@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
6 changes: 6 additions & 0 deletions module_auto_update/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -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
Loading