Skip to content
Permalink
Browse files

Merge pull request #10 from trojikman/12.0-saas_demo-compatibility

[WIP] saas_demo
  • Loading branch information...
yelizariev committed Aug 13, 2019
2 parents f322998 + ef1a689 commit ff203e86e9a7ab8ed793832bfa7343adcb14027a
@@ -1,3 +1,3 @@
# License LGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
from . import controllers
from . import models
@@ -1,5 +1,5 @@
# Copyright 2018 Ivan Yelizariev <https://it-projects.info/team/yelizariev>
# License LGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
{
"name": """Quick Demo Databases""",
"summary": """One-click demo-instances with modules from your git repositories""",
@@ -12,7 +12,7 @@
"author": "IT-Projects LLC, Ivan Yelizariev",
"support": "apps@it-projects.info",
"website": "https://it-projects.info/team/yelizariev",
"license": "LGPL-3",
"license": "AGPL-3",
# "price": 9.00,
# "currency": "EUR",

@@ -24,6 +24,7 @@
"security/ir.model.access.csv",
],
"demo": [
"demo/demo.xml",
],
"qweb": [
],
@@ -34,5 +35,5 @@
"uninstall_hook": None,

"auto_install": False,
"installable": False,
"installable": True,
}
@@ -1,2 +1,2 @@
# License LGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
from . import main
@@ -1,3 +1,3 @@
# Copyright 2018 Ivan Yelizariev <https://it-projects.info/team/yelizariev>
# License LGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
# -*- coding: utf-8 -*-
@@ -0,0 +1,16 @@
<!-- Copyright 2019 Denis Mudarisov <https://it-projects.info/team/trojikman>
License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).-->
<odoo>
<record id="saas_demo" model="saas.demo">
<field name="name">SaaS Demo Test</field>
</record>

<record id="saas.local_operator" model="saas.operator">
<field name="demo_id" ref="saas_demo"/>
</record>
<record id="saas_demo_repo" model="saas.demo.repo">
<field name="demo_id" ref="saas_demo"/>
<field name="url">https://gitlab.com/itpp/dev/saas-demo-test.git</field>
<field name="branch">12.0</field>
</record>
</odoo>
@@ -17,15 +17,13 @@ Add following attributes to manifest of your modules

::

"demo_title": "Super-Duper Reminders",
"demo_addons": ["reminder_phonecall", "reminder_task_deadline", "reminder_hr_recruitment"],
"demo_addons_hidden": ["website"],
"demo_url": "reminders-and-agenda",

* ``demo_title`` -- human-readable description of demonstrated modules
* ``demo_addons`` -- list of additional modules to demostrate
* ``demo_addons_hidden`` -- additional modules to install
* ``demo_url`` -- is used as part of url to get demo instance
"saas_demo_title": "Super-Duper Reminders",
"saas_demo_addons": ["reminder_phonecall", "reminder_task_deadline", "reminder_hr_recruitment"],
"saas_demo_addons_hidden": ["website"],

* ``saas_demo_title`` -- human-readable description of demonstrated modules
* ``saas_demo_addons`` -- list of additional modules to demostrate
* ``saas_demo_addons_hidden`` -- additional modules to install

apps.odoo.com
-------------
@@ -1,4 +1,4 @@
# License LGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
from . import saas_operator
from . import saas_demo
from . import saas_template
@@ -1,14 +1,14 @@
# Copyright 2018 Ivan Yelizariev <https://it-projects.info/team/yelizariev>
# Copyright 2019 Denis Mudarisov <https://it-projects.info/team/trojikman>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
import logging
import os
import os.path
import json

from odoo import models, fields, api

from odoo.addons.queue_job.job import job
from ..os import repos_dir, update_repo, get_manifests
from ..os import repos_dir, analysis_dir, update_repo, get_manifests

_logger = logging.getLogger(__name__)

@@ -27,41 +27,63 @@ def fetch_and_generate_templates(self):
# TODO: this method is called via git webhooks
# FIXME: split into methods to avoid deep nesting
repos_path = repos_dir()
analysis_path = analysis_dir()
demos_for_immediate_update = self.env[self._name]
for demo in self:
updated = demo.repo_ids._local_update_repo(update_commit=True)
if not updated:
continue
for repo in demo.repo_ids:
path = os.path.join(repos_path, repo.url_escaped)
path = os.path.join(analysis_path, repo.branch, repo.url_escaped)
self.operator_ids.update_ad_paths(path)
for module, manifest in get_manifests(path).items():
if not manifest.get('demo_url'):
if not manifest.get('saas_demo_title'):
# not a demo
continue
if not manifest.get('installable', True):
# not installable
continue
template = self.env['saas.template'].search([
('demo_id', '=', demo.id),
('demo_module', '=', module),
('demo_main_addon_id.name', '=', module),
])
if not template:
module_rec = self.env['saas.module'].search([('name', '=', module)])
if not module_rec:
module_rec = self.env['saas.module'].create({
'name': module
})
template = self.env['saas.template'].create({
'demo_id': demo.id,
'demo_module': module,
'demo_main_addon_id': module_rec.id,
})
demos_for_immediate_update |= demo
modules_to_show = [module] + manifest.get('saas_demo_addons')
modules_to_install = modules_to_show + manifest.get('saas_demo_addons_hidden')
template.write({
'name': manifest.get('demo_title'),
'template_modules_domain': json.dumps([
('name', 'in', manifest.get('demo_addons') + manifest.get('demo_addons_hidden'))
]),
'demo_addons': ','.join(manifest.get('demo_addons')),
'demo_url': manifest.get('demo_url'),
'name': manifest.get('saas_demo_title'),
'template_module_ids': self.get_module_vals(modules_to_install),
'demo_addon_ids': self.get_module_vals(modules_to_show),
})
self.operator_ids.remove_ad_paths(path)
build_path = os.path.join(repos_path, repo.branch, repo.url_escaped)
self.operator_ids.update_ad_paths(build_path)

if demos_for_immediate_update:
self.repos_updating_start(demos_for_immediate_update)

@api.model
def get_module_vals(self, modules):
module_ids = self.env['saas.module'].search([('name', 'in', modules)])
existing_modules = module_ids.mapped('name')
for module in modules:
if module not in existing_modules:
new_module = self.env['saas.module'].create({'name': module})
module_ids |= new_module
existing_modules.append(module)
vals = [(4, module.id, 0) for module in module_ids]
return vals

@api.model
@job
def repos_updating_start(self, demos=None):
@@ -100,19 +122,18 @@ def repos_updating_next(self):
# filter out operators which demo already has processing operator
def filter_free_operators(op):
states = op.demo_id.operator_ids.mapped('update_repos_state')
return all((s != 'processing' for s in states))
return all((s != 'updating' for s in states))

operators = pending_operators.filtered(filter_free_operators)
if not operators:
# it's not a time to start
return

operators.write({
'update_repos_state': 'processing',
'update_repos_state': 'updating',
})
# close transaction to make update_repos_state update visible
self.env.cr.commit()

operators.update_repos()

# repos_updating_next() will be called via cron
@@ -124,10 +145,10 @@ class Repo(models.Model):
_description = 'Repository for Demo'
_rec_name = 'url'

demo_id = fields.Many2one('saas.operator')
url = fields.Char('Repo URL')
demo_id = fields.Many2one('saas.demo')
url = fields.Char('Repo URL', required=True)
url_escaped = fields.Char('Repo URL (escaped)', compute='_compute_url_escaped')
branch = fields.Char('Branch')
branch = fields.Char('Branch', required=True)
demo_repo = fields.Boolean('Scan for demo', default=True)
commit = fields.Char('Commit SHA', help='Last processed point')

@@ -139,12 +160,15 @@ def _compute_url_escaped(self):
r.url_escaped = url

def _local_update_repo(self, update_commit=True):
local_root = repos_dir()
analysis_root = analysis_dir()
updated = False
for repo in self:
path = os.path.join(local_root, repo.url_escaped)
commit = update_repo(path, repo.url, repo.branch)
analysis_path = os.path.join(analysis_root, repo.branch, repo.url_escaped)
commit = update_repo(analysis_path, repo.url, repo.branch)
if commit != repo.commit:
local_root = repos_dir()
build_path = os.path.join(local_root, repo.branch, repo.url_escaped)
update_repo(build_path, repo.url, repo.branch)
updated = True
if update_commit:
repo.commit = commit
@@ -3,6 +3,7 @@
import logging

from odoo import models, fields, api, service
from odoo.modules.module import ad_paths
from ..os import repos_dir, update_addons_path, root_odoo_path, git

_logger = logging.getLogger(__name__)
@@ -51,7 +52,6 @@ def update_repos(self):
# update odoo source only when we have updates in other repositories.
# Otherwise don't update it and don't rebuild templates
updated_operators.update_odoo()

# update addons-path
updated_operators.update_addons_path()

@@ -62,7 +62,12 @@ def update_repos(self):
def update_odoo(self):
"""Fetch and checkout Repository"""
if self.is_local():
git(root_odoo_path(), ['pull', 'origin'])
test = self.env['ir.config_parameter'].get_param('test_saas_demo')
if test:
# no need to pull odoo folder in test mode
return
else:
git(root_odoo_path(), ['pull', 'origin'])

@api.multi
def update_addons_path(self):
@@ -73,11 +78,23 @@ def update_addons_path(self):
@api.multi
def restart_odoo(self):
if self.is_local():
service.service.restart()
service.server.restart()

@api.multi
def _update_repos(self):
self.ensure_one()
if self.type != 'local':
return
return self.demo_id.repo_ids._local_update_repo()

@api.multi
def update_ad_paths(self, path):
if self.is_local():
if path not in ad_paths:
ad_paths.append(path)

@api.multi
def remove_ad_paths(self, path):
if self.is_local():
if path in ad_paths:
ad_paths.remove(path)
@@ -1,4 +1,5 @@
# Copyright 2018 Ivan Yelizariev <https://it-projects.info/team/yelizariev>
# Copyright 2019 Denis Mudarisov <https://it-projects.info/team/trojikman>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
from odoo import models, fields

@@ -7,10 +8,11 @@ class Template(models.Model):
_inherit = 'saas.template'

demo_id = fields.Many2one('saas.demo')
demo_module = fields.Char()
demo_url = fields.Char()
demo_addons = fields.Char(help='Comma-separated list. Will be used as default filter in Apps section of the builds')
demo_main_addon_id = fields.Many2one('saas.module')
# demo_url = fields.Char()
demo_addon_ids = fields.Many2many('saas.module', help='Will be used as default filter in Apps section of the builds')
demo_branch = fields.Char()

_sql_constraints = [
('name_uniq', 'unique (demo_id, demo_module)', 'Template for that demo already exists.'),
('demo_uniq', 'unique (demo_id, demo_main_addon_id, demo_branch)', 'Template for that demo already exists.'),
]
@@ -1,4 +1,5 @@
# Copyright 2018 Ivan Yelizariev <https://it-projects.info/team/yelizariev>
# Copyright 2019 Denis Mudarisov <https://it-projects.info/team/trojikman>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
# Some code is based on https://github.com/odoo/odoo-extra/blob/master/runbot/runbot.py
import subprocess
@@ -54,7 +55,7 @@ def mkdir(d):
def git(path, cmd):
cmd = ['git', '-C', path] + cmd
_logger.debug("git: %s", ' '.join(cmd))
return subprocess.check_output(cmd).strip()
return subprocess.check_output(cmd).strip().decode('utf-8')


def update_repo(path, repo_url, branch):
@@ -66,6 +67,11 @@ def update_repo(path, repo_url, branch):
return commit


def analysis_dir():
d = os.path.join(tools.config['data_dir'], 'analysis')
return mkdir(d)


# ODOO
def repos_dir():
d = os.path.join(tools.config['data_dir'], 'repos')
@@ -102,12 +108,13 @@ def update_addons_path(folder_of_folders, force=True):
]
addons_path += extra
addons_path = list(set(addons_path))
addons_path = addons_path.join(',')
addons_path = ','.join(addons_path)
_logger.info('addons_path for %s:\n%s', folder_of_folders, addons_path)
update_config('options', 'addons_path', addons_path)


def update_config(section, key, value):
config_parser.read(tools.config.rcfile)
config_parser.set(section, key, value)
with open(tools.config.rcfile, 'w') as configfile:
config_parser.write(configfile)
@@ -125,9 +132,9 @@ def is_really_module(name):
for it in os.listdir(path)
if is_really_module(it)
]
res = []
res = {}
for mname in modules:
mod_path = os.path.join(path, mname)
info = load_information_from_description_file(mname, mod_path)
res[mname] = info
return info
return res
@@ -0,0 +1,2 @@
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
from . import test_saas_demo
@@ -0,0 +1,25 @@
# Copyright 2019 Denis Mudarisov <https://it-projects.info/team/trojikman>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).


from odoo.tests.common import TransactionCase, tagged


@tagged('post_install', 'at_install')
class TestSaasDemo(TransactionCase):
def setUp(self):
super(TestSaasDemo, self).setUp()
self.env = self.env(context=dict(
self.env.context,
test_queue_job_no_delay=True,
))
self.env['ir.config_parameter'].set_param('test_saas_demo', 'True')
self.saas_demo = self.env.ref('saas_demo.saas_demo')

def test_saas_demo(self):
# for some reason, when you restart the tests, the template is not deleted
self.env['saas.template'].search([('name', '=', 'Demo Title')]).unlink()
repo = self.env['saas.demo.repo'].search([('demo_id', '=', self.saas_demo.id)])
if repo.commit:
repo.commit = None
self.saas_demo.fetch_and_generate_templates()

0 comments on commit ff203e8

Please sign in to comment.
You can’t perform that action at this time.