mirror of
https://github.com/odoo/runbot.git
synced 2025-03-27 13:25:47 +07:00
[MERGE] runbot: migration to odoo 13.0
This commit is contained in:
commit
300f39003f
@ -6,7 +6,7 @@
|
|||||||
'author': "Odoo SA",
|
'author': "Odoo SA",
|
||||||
'website': "http://runbot.odoo.com",
|
'website': "http://runbot.odoo.com",
|
||||||
'category': 'Website',
|
'category': 'Website',
|
||||||
'version': '4.6',
|
'version': '4.8',
|
||||||
'depends': ['website', 'base'],
|
'depends': ['website', 'base'],
|
||||||
'data': [
|
'data': [
|
||||||
'security/runbot_security.xml',
|
'security/runbot_security.xml',
|
||||||
@ -33,5 +33,6 @@
|
|||||||
'data/build_parse.xml',
|
'data/build_parse.xml',
|
||||||
'data/runbot_error_regex_data.xml',
|
'data/runbot_error_regex_data.xml',
|
||||||
'data/error_link.xml',
|
'data/error_link.xml',
|
||||||
|
'data/website_data.xml',
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
@ -54,7 +54,7 @@ def time2str(t):
|
|||||||
|
|
||||||
def dt2time(datetime):
|
def dt2time(datetime):
|
||||||
"""Convert datetime to time"""
|
"""Convert datetime to time"""
|
||||||
return time.mktime(time.strptime(datetime, DEFAULT_SERVER_DATETIME_FORMAT))
|
return time.mktime(datetime.timetuple())
|
||||||
|
|
||||||
|
|
||||||
def now():
|
def now():
|
||||||
|
@ -35,7 +35,7 @@ class RunbotBadge(Controller):
|
|||||||
|
|
||||||
build = builds[0]
|
build = builds[0]
|
||||||
etag = request.httprequest.headers.get('If-None-Match')
|
etag = request.httprequest.headers.get('If-None-Match')
|
||||||
retag = hashlib.md5(build[last_update].encode()).hexdigest()
|
retag = hashlib.md5(str(build[last_update]).encode()).hexdigest()
|
||||||
|
|
||||||
if etag == retag:
|
if etag == retag:
|
||||||
return werkzeug.wrappers.Response(status=304)
|
return werkzeug.wrappers.Response(status=304)
|
||||||
|
@ -39,6 +39,7 @@ class Runbot(Controller):
|
|||||||
'host_stats': [],
|
'host_stats': [],
|
||||||
'pending_total': pending[0],
|
'pending_total': pending[0],
|
||||||
'pending_level': pending[1],
|
'pending_level': pending[1],
|
||||||
|
'hosts_data': request.env['runbot.host'].search([]),
|
||||||
'search': search,
|
'search': search,
|
||||||
'refresh': refresh,
|
'refresh': refresh,
|
||||||
}
|
}
|
||||||
@ -101,15 +102,11 @@ class Runbot(Controller):
|
|||||||
def branch_info(branch):
|
def branch_info(branch):
|
||||||
return {
|
return {
|
||||||
'branch': branch,
|
'branch': branch,
|
||||||
'fqdn': fqdn(),
|
|
||||||
'builds': [build_dict[build_id] for build_id in build_by_branch_ids.get(branch.id) or []]
|
'builds': [build_dict[build_id] for build_id in build_by_branch_ids.get(branch.id) or []]
|
||||||
}
|
}
|
||||||
|
|
||||||
context.update({
|
context.update({
|
||||||
'branches': [branch_info(b) for b in branches],
|
'branches': [branch_info(b) for b in branches],
|
||||||
'testing': build_obj.search_count([('repo_id', '=', repo.id), ('local_state', '=', 'testing')]),
|
|
||||||
'running': build_obj.search_count([('repo_id', '=', repo.id), ('local_state', '=', 'running')]),
|
|
||||||
'pending': build_obj.search_count([('repo_id', '=', repo.id), ('local_state', '=', 'pending')]),
|
|
||||||
'qu': QueryURL('/runbot/repo/' + slug(repo), search=search, refresh=refresh),
|
'qu': QueryURL('/runbot/repo/' + slug(repo), search=search, refresh=refresh),
|
||||||
'fqdn': fqdn(),
|
'fqdn': fqdn(),
|
||||||
})
|
})
|
||||||
@ -117,14 +114,6 @@ class Runbot(Controller):
|
|||||||
# consider host gone if no build in last 100
|
# consider host gone if no build in last 100
|
||||||
build_threshold = max(build_ids or [0]) - 100
|
build_threshold = max(build_ids or [0]) - 100
|
||||||
|
|
||||||
for result in build_obj.read_group([('id', '>', build_threshold)], ['host'], ['host']):
|
|
||||||
if result['host']:
|
|
||||||
context['host_stats'].append({
|
|
||||||
'host': result['host'],
|
|
||||||
'testing': build_obj.search_count([('local_state', '=', 'testing'), ('host', '=', result['host'])]),
|
|
||||||
'running': build_obj.search_count([('local_state', '=', 'running'), ('host', '=', result['host'])]),
|
|
||||||
})
|
|
||||||
|
|
||||||
context.update({'message': request.env['ir.config_parameter'].sudo().get_param('runbot.runbot_message')})
|
context.update({'message': request.env['ir.config_parameter'].sudo().get_param('runbot.runbot_message')})
|
||||||
return request.render('runbot.repo', context)
|
return request.render('runbot.repo', context)
|
||||||
|
|
||||||
@ -302,21 +291,27 @@ class Runbot(Controller):
|
|||||||
return request.render("runbot.glances", qctx)
|
return request.render("runbot.glances", qctx)
|
||||||
|
|
||||||
@route('/runbot/monitoring', type='http', auth='user', website=True)
|
@route('/runbot/monitoring', type='http', auth='user', website=True)
|
||||||
def monitoring(self, refresh=None):
|
@route('/runbot/monitoring/<int:config_id>', type='http', auth='user', website=True)
|
||||||
|
@route('/runbot/monitoring/<int:config_id>/<int:view_id>', type='http', auth='user', website=True)
|
||||||
|
def monitoring(self, config_id=None, view_id=None, refresh=None):
|
||||||
glances_ctx = self._glances_ctx()
|
glances_ctx = self._glances_ctx()
|
||||||
pending = self._pending()
|
pending = self._pending()
|
||||||
hosts_data = request.env['runbot.host'].search([])
|
hosts_data = request.env['runbot.host'].search([])
|
||||||
|
|
||||||
monitored_config_id = int(request.env['ir.config_parameter'].sudo().get_param('runbot.monitored_config_id', 1))
|
last_monitored = None
|
||||||
request.env.cr.execute("""SELECT DISTINCT ON (branch_id) branch_id, id FROM runbot_build
|
if config_id or config_id is None:
|
||||||
WHERE config_id = %s
|
monitored_config_id = config_id or int(request.env['ir.config_parameter'].sudo().get_param('runbot.monitored_config_id', 1))
|
||||||
AND global_state in ('running', 'done')
|
request.env.cr.execute("""SELECT DISTINCT ON (branch_id) branch_id, id FROM runbot_build
|
||||||
AND branch_id in (SELECT id FROM runbot_branch where sticky='t')
|
WHERE config_id = %s
|
||||||
AND local_state != 'duplicate'
|
AND global_state in ('running', 'done')
|
||||||
ORDER BY branch_id ASC, id DESC""", [int(monitored_config_id)])
|
AND branch_id in (SELECT id FROM runbot_branch where sticky='t')
|
||||||
last_monitored = request.env['runbot.build'].browse([r[1] for r in request.env.cr.fetchall()])
|
AND local_state != 'duplicate'
|
||||||
|
ORDER BY branch_id ASC, id DESC""", [int(monitored_config_id)])
|
||||||
|
last_monitored = request.env['runbot.build'].browse([r[1] for r in request.env.cr.fetchall()])
|
||||||
|
|
||||||
|
config = request.env['runbot.build.config'].browse(monitored_config_id)
|
||||||
qctx = {
|
qctx = {
|
||||||
|
'config': config,
|
||||||
'refresh': refresh,
|
'refresh': refresh,
|
||||||
'pending_total': pending[0],
|
'pending_total': pending[0],
|
||||||
'pending_level': pending[1],
|
'pending_level': pending[1],
|
||||||
@ -326,7 +321,7 @@ class Runbot(Controller):
|
|||||||
'auto_tags': request.env['runbot.build.error'].disabling_tags(),
|
'auto_tags': request.env['runbot.build.error'].disabling_tags(),
|
||||||
'build_errors': request.env['runbot.build.error'].search([('random', '=', True)])
|
'build_errors': request.env['runbot.build.error'].search([('random', '=', True)])
|
||||||
}
|
}
|
||||||
return request.render("runbot.monitoring", qctx)
|
return request.render(request.env['ir.ui.view'].browse('view_id') if view_id else config.monitoring_view_id.id or "runbot.monitoring", qctx)
|
||||||
|
|
||||||
@route(['/runbot/branch/<int:branch_id>', '/runbot/branch/<int:branch_id>/page/<int:page>'], website=True, auth='public', type='http')
|
@route(['/runbot/branch/<int:branch_id>', '/runbot/branch/<int:branch_id>/page/<int:page>'], website=True, auth='public', type='http')
|
||||||
def branch_builds(self, branch_id=None, search='', page=1, limit=50, refresh='', **kwargs):
|
def branch_builds(self, branch_id=None, search='', page=1, limit=50, refresh='', **kwargs):
|
||||||
@ -337,9 +332,9 @@ class Runbot(Controller):
|
|||||||
url='/runbot/branch/%s' % branch_id,
|
url='/runbot/branch/%s' % branch_id,
|
||||||
total=builds_count,
|
total=builds_count,
|
||||||
page=page,
|
page=page,
|
||||||
step=50
|
step=50,
|
||||||
)
|
)
|
||||||
builds = request.env['runbot.build'].search(domain, limit=limit, offset=pager.get('offset',0))
|
builds = request.env['runbot.build'].search(domain, limit=limit, offset=pager.get('offset',0))
|
||||||
|
|
||||||
context = {'pager': pager, 'builds': builds}
|
context = {'pager': pager, 'builds': builds, 'repo': request.env['runbot.branch'].browse(branch_id).repo_id}
|
||||||
return request.render("runbot.branch", context)
|
return request.render("runbot.branch", context)
|
||||||
|
@ -31,7 +31,7 @@ class RunbotHook(http.Controller):
|
|||||||
|
|
||||||
# force update of dependencies to in case a hook is lost
|
# force update of dependencies to in case a hook is lost
|
||||||
if not payload or event == 'push' or (event == 'pull_request' and payload.get('action') in ('synchronize', 'opened', 'reopened')):
|
if not payload or event == 'push' or (event == 'pull_request' and payload.get('action') in ('synchronize', 'opened', 'reopened')):
|
||||||
(repo | repo.dependency_ids).write({'hook_time': time.time()})
|
(repo | repo.dependency_ids).set_hook_time(time.time())
|
||||||
else:
|
else:
|
||||||
_logger.debug('Ignoring unsupported hook %s %s', event, payload.get('action', ''))
|
_logger.debug('Ignoring unsupported hook %s %s', event, payload.get('action', ''))
|
||||||
return ""
|
return ""
|
||||||
|
5
runbot/data/website_data.xml
Normal file
5
runbot/data/website_data.xml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<odoo>
|
||||||
|
<record id="website.homepage_page" model="website.page">
|
||||||
|
<field name="url">/home</field>
|
||||||
|
</record>
|
||||||
|
</odoo>
|
@ -12,6 +12,7 @@ _re_patch = re.compile(r'.*patch-\d+$')
|
|||||||
class runbot_branch(models.Model):
|
class runbot_branch(models.Model):
|
||||||
|
|
||||||
_name = "runbot.branch"
|
_name = "runbot.branch"
|
||||||
|
_description = "Branch"
|
||||||
_order = 'name'
|
_order = 'name'
|
||||||
_sql_constraints = [('branch_repo_uniq', 'unique (name,repo_id)', 'The branch must be unique per repository !')]
|
_sql_constraints = [('branch_repo_uniq', 'unique (name,repo_id)', 'The branch must be unique per repository !')]
|
||||||
|
|
||||||
@ -35,7 +36,7 @@ class runbot_branch(models.Model):
|
|||||||
no_auto_build = fields.Boolean("Don't automatically build commit on this branch", default=False)
|
no_auto_build = fields.Boolean("Don't automatically build commit on this branch", default=False)
|
||||||
rebuild_requested = fields.Boolean("Request a rebuild", help="Rebuild the latest commit even when no_auto_build is set.", default=False)
|
rebuild_requested = fields.Boolean("Request a rebuild", help="Rebuild the latest commit even when no_auto_build is set.", default=False)
|
||||||
|
|
||||||
branch_config_id = fields.Many2one('runbot.build.config', 'Run Config')
|
branch_config_id = fields.Many2one('runbot.build.config', 'Branch Config')
|
||||||
config_id = fields.Many2one('runbot.build.config', 'Run Config', compute='_compute_config_id', inverse='_inverse_config_id')
|
config_id = fields.Many2one('runbot.build.config', 'Run Config', compute='_compute_config_id', inverse='_inverse_config_id')
|
||||||
|
|
||||||
@api.depends('sticky', 'defined_sticky', 'target_branch_name', 'name')
|
@api.depends('sticky', 'defined_sticky', 'target_branch_name', 'name')
|
||||||
@ -47,16 +48,24 @@ class runbot_branch(models.Model):
|
|||||||
elif branch.defined_sticky:
|
elif branch.defined_sticky:
|
||||||
branch.closest_sticky = branch.defined_sticky # be carefull with loop
|
branch.closest_sticky = branch.defined_sticky # be carefull with loop
|
||||||
elif branch.target_branch_name:
|
elif branch.target_branch_name:
|
||||||
corresping_branch = self.search([('branch_name', '=', branch.target_branch_name), ('repo_id', '=', branch.repo_id.id)])
|
corresponding_branch = self.search([('branch_name', '=', branch.target_branch_name), ('repo_id', '=', branch.repo_id.id)])
|
||||||
branch.closest_sticky = corresping_branch.closest_sticky
|
branch.closest_sticky = corresponding_branch.closest_sticky
|
||||||
else:
|
else:
|
||||||
repo_ids = (branch.repo_id | branch.repo_id.duplicate_id).ids
|
repo_ids = (branch.repo_id | branch.repo_id.duplicate_id).ids
|
||||||
self.env.cr.execute("select id from runbot_branch where sticky = 't' and repo_id = any(%s) and %s like name||'%%'", (repo_ids, branch.name or ''))
|
self.env.cr.execute("select id from runbot_branch where sticky = 't' and repo_id = any(%s) and %s like name||'%%'", (repo_ids, branch.name or ''))
|
||||||
branch.closest_sticky = self.browse(self.env.cr.fetchone())
|
branch.closest_sticky = self.browse(self.env.cr.fetchone())
|
||||||
|
|
||||||
@api.depends('closest_sticky.previous_version')
|
@api.depends('closest_sticky') #, 'closest_sticky.previous_version')
|
||||||
def _compute_previous_version(self):
|
def _compute_previous_version(self):
|
||||||
for branch in self:
|
for branch in self.sorted(key='sticky', reverse=True):
|
||||||
|
# orm does not support non_searchable.non_stored dependency.
|
||||||
|
# thus, the closest_sticky.previous_version dependency will log an error
|
||||||
|
# when previous_version is written.
|
||||||
|
# this dependency is usefull to make the compute recursive, avoiding to have
|
||||||
|
# both record and record.closest_sticky in self, in that order, making the record.previous_version
|
||||||
|
# empty in all cases.
|
||||||
|
# Sorting self on sticky will mitigate the problem. but it is still posible to
|
||||||
|
# have computation errors if defined_sticky is not sticky. (which is not a normal use case)
|
||||||
if branch.closest_sticky == branch:
|
if branch.closest_sticky == branch:
|
||||||
repo_ids = (branch.repo_id | branch.repo_id.duplicate_id).ids
|
repo_ids = (branch.repo_id | branch.repo_id.duplicate_id).ids
|
||||||
domain = [('branch_name', 'like', '%.0'), ('sticky', '=', True), ('branch_name', '!=', 'master'), ('repo_id', 'in', repo_ids)]
|
domain = [('branch_name', 'like', '%.0'), ('sticky', '=', True), ('branch_name', '!=', 'master'), ('repo_id', 'in', repo_ids)]
|
||||||
@ -66,11 +75,12 @@ class runbot_branch(models.Model):
|
|||||||
else:
|
else:
|
||||||
branch.previous_version = branch.closest_sticky.previous_version
|
branch.previous_version = branch.closest_sticky.previous_version
|
||||||
|
|
||||||
@api.depends('previous_version', 'closest_sticky.intermediate_stickies')
|
@api.depends('previous_version', 'closest_sticky')
|
||||||
def _compute_intermediate_stickies(self):
|
def _compute_intermediate_stickies(self):
|
||||||
for branch in self:
|
for branch in self.sorted(key='sticky', reverse=True):
|
||||||
if branch.closest_sticky == branch:
|
if branch.closest_sticky == branch:
|
||||||
if not branch.previous_version:
|
if not branch.previous_version:
|
||||||
|
branch.intermediate_stickies = [(5, 0, 0)]
|
||||||
continue
|
continue
|
||||||
repo_ids = (branch.repo_id | branch.repo_id.duplicate_id).ids
|
repo_ids = (branch.repo_id | branch.repo_id.duplicate_id).ids
|
||||||
domain = [('id', '>', branch.previous_version.id), ('sticky', '=', True), ('branch_name', '!=', 'master'), ('repo_id', 'in', repo_ids)]
|
domain = [('id', '>', branch.previous_version.id), ('sticky', '=', True), ('branch_name', '!=', 'master'), ('repo_id', 'in', repo_ids)]
|
||||||
@ -133,7 +143,7 @@ class runbot_branch(models.Model):
|
|||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@api.model
|
@api.model_create_single
|
||||||
def create(self, vals):
|
def create(self, vals):
|
||||||
if not vals.get('config_id') and ('use-coverage' in (vals.get('name') or '')):
|
if not vals.get('config_id') and ('use-coverage' in (vals.get('name') or '')):
|
||||||
coverage_config = self.env.ref('runbot.runbot_build_config_test_coverage', raise_if_not_found=False)
|
coverage_config = self.env.ref('runbot.runbot_build_config_test_coverage', raise_if_not_found=False)
|
||||||
@ -287,6 +297,6 @@ class runbot_branch(models.Model):
|
|||||||
for branch in self:
|
for branch in self:
|
||||||
if not branch.rebuild_requested:
|
if not branch.rebuild_requested:
|
||||||
branch.rebuild_requested = True
|
branch.rebuild_requested = True
|
||||||
branch.repo_id.write({'hook_time': time.time()})
|
branch.repo_id.set_hook_time(time.time())
|
||||||
else:
|
else:
|
||||||
branch.rebuild_requested = False
|
branch.rebuild_requested = False
|
||||||
|
@ -32,6 +32,8 @@ def make_selection(array):
|
|||||||
|
|
||||||
class runbot_build(models.Model):
|
class runbot_build(models.Model):
|
||||||
_name = "runbot.build"
|
_name = "runbot.build"
|
||||||
|
_description = "Build"
|
||||||
|
|
||||||
_order = 'id desc'
|
_order = 'id desc'
|
||||||
_rec_name = 'id'
|
_rec_name = 'id'
|
||||||
|
|
||||||
@ -55,16 +57,16 @@ class runbot_build(models.Model):
|
|||||||
# state machine
|
# state machine
|
||||||
|
|
||||||
global_state = fields.Selection(make_selection(state_order), string='Status', compute='_compute_global_state', store=True)
|
global_state = fields.Selection(make_selection(state_order), string='Status', compute='_compute_global_state', store=True)
|
||||||
local_state = fields.Selection(make_selection(state_order), string='Build Status', default='pending', required=True, oldname='state', index=True)
|
local_state = fields.Selection(make_selection(state_order), string='Build Status', default='pending', required=True, index=True)
|
||||||
global_result = fields.Selection(make_selection(result_order), string='Result', compute='_compute_global_result', store=True)
|
global_result = fields.Selection(make_selection(result_order), string='Result', compute='_compute_global_result', store=True)
|
||||||
local_result = fields.Selection(make_selection(result_order), string='Build Result', oldname='result')
|
local_result = fields.Selection(make_selection(result_order), string='Build Result')
|
||||||
triggered_result = fields.Selection(make_selection(result_order), string='Triggered Result') # triggered by db only
|
triggered_result = fields.Selection(make_selection(result_order), string='Triggered Result') # triggered by db only
|
||||||
|
|
||||||
requested_action = fields.Selection([('wake_up', 'To wake up'), ('deathrow', 'To kill')], string='Action requested', index=True)
|
requested_action = fields.Selection([('wake_up', 'To wake up'), ('deathrow', 'To kill')], string='Action requested', index=True)
|
||||||
|
|
||||||
nb_pending = fields.Integer("Number of pending in queue", default=0)
|
nb_pending = fields.Integer("Number of pending in queue", default=0)
|
||||||
nb_testing = fields.Integer("Number of test slot use", default=0)
|
nb_testing = fields.Integer("Number of test slot use", default=0)
|
||||||
nb_running = fields.Integer("Number of test slot use", default=0)
|
nb_running = fields.Integer("Number of run slot use", default=0)
|
||||||
|
|
||||||
# should we add a stored field for children results?
|
# should we add a stored field for children results?
|
||||||
active_step = fields.Many2one('runbot.build.config.step', 'Active step')
|
active_step = fields.Many2one('runbot.build.config.step', 'Active step')
|
||||||
@ -74,7 +76,7 @@ class runbot_build(models.Model):
|
|||||||
build_start = fields.Datetime('Build start')
|
build_start = fields.Datetime('Build start')
|
||||||
build_end = fields.Datetime('Build end')
|
build_end = fields.Datetime('Build end')
|
||||||
job_time = fields.Integer(compute='_compute_job_time', string='Job time')
|
job_time = fields.Integer(compute='_compute_job_time', string='Job time')
|
||||||
build_time = fields.Integer(compute='_compute_build_time', string='Job time')
|
build_time = fields.Integer(compute='_compute_build_time', string='Build time')
|
||||||
build_age = fields.Integer(compute='_compute_build_age', string='Build age')
|
build_age = fields.Integer(compute='_compute_build_age', string='Build age')
|
||||||
duplicate_id = fields.Many2one('runbot.build', 'Corresponding Build', index=True)
|
duplicate_id = fields.Many2one('runbot.build', 'Corresponding Build', index=True)
|
||||||
revdep_build_ids = fields.Many2many('runbot.build', 'runbot_rev_dep_builds',
|
revdep_build_ids = fields.Many2many('runbot.build', 'runbot_rev_dep_builds',
|
||||||
@ -116,18 +118,21 @@ class runbot_build(models.Model):
|
|||||||
for build in self:
|
for build in self:
|
||||||
build.log_list = ','.join({step.name for step in build.config_id.step_ids() if step._has_log()})
|
build.log_list = ','.join({step.name for step in build.config_id.step_ids() if step._has_log()})
|
||||||
|
|
||||||
@api.depends('nb_testing', 'nb_pending', 'local_state', 'duplicate_id.global_state')
|
@api.depends('children_ids.global_state', 'local_state', 'duplicate_id.global_state')
|
||||||
def _compute_global_state(self):
|
def _compute_global_state(self):
|
||||||
# could we use nb_pending / nb_testing ? not in a compute, but in a update state method
|
|
||||||
for record in self:
|
for record in self:
|
||||||
if record.duplicate_id:
|
if record.duplicate_id:
|
||||||
record.global_state = record.duplicate_id.global_state
|
record.global_state = record.duplicate_id.global_state
|
||||||
else:
|
else:
|
||||||
waiting_score = record._get_state_score('waiting')
|
waiting_score = record._get_state_score('waiting')
|
||||||
if record._get_state_score(record.local_state) < waiting_score or record.nb_pending + record.nb_testing == 0:
|
if record._get_state_score(record.local_state) > waiting_score and record.children_ids: # if finish, check children
|
||||||
record.global_state = record.local_state
|
children_state = record._get_youngest_state([child.global_state for child in record.children_ids])
|
||||||
|
if record._get_state_score(children_state) > waiting_score:
|
||||||
|
record.global_state = record.local_state
|
||||||
|
else:
|
||||||
|
record.global_state = 'waiting'
|
||||||
else:
|
else:
|
||||||
record.global_state = 'waiting'
|
record.global_state = record.local_state
|
||||||
|
|
||||||
def _get_youngest_state(self, states):
|
def _get_youngest_state(self, states):
|
||||||
index = min([self._get_state_score(state) for state in states])
|
index = min([self._get_state_score(state) for state in states])
|
||||||
@ -166,24 +171,7 @@ class runbot_build(models.Model):
|
|||||||
def _get_result_score(self, result):
|
def _get_result_score(self, result):
|
||||||
return result_order.index(result)
|
return result_order.index(result)
|
||||||
|
|
||||||
def _update_nb_children(self, new_state, old_state=None):
|
@api.depends('active_step', 'duplicate_id.active_step')
|
||||||
# could be interresting to update state in batches.
|
|
||||||
tracked_count_list = ['pending', 'testing', 'running']
|
|
||||||
if (new_state not in tracked_count_list and old_state not in tracked_count_list) or new_state == old_state:
|
|
||||||
return
|
|
||||||
|
|
||||||
for record in self:
|
|
||||||
values = {}
|
|
||||||
if old_state in tracked_count_list:
|
|
||||||
values['nb_%s' % old_state] = record['nb_%s' % old_state] - 1
|
|
||||||
if new_state in tracked_count_list:
|
|
||||||
values['nb_%s' % new_state] = record['nb_%s' % new_state] + 1
|
|
||||||
|
|
||||||
record.write(values)
|
|
||||||
if record.parent_id:
|
|
||||||
record.parent_id._update_nb_children(new_state, old_state)
|
|
||||||
|
|
||||||
@api.depends('real_build.active_step')
|
|
||||||
def _compute_job(self):
|
def _compute_job(self):
|
||||||
for build in self:
|
for build in self:
|
||||||
build.job = build.real_build.active_step.name
|
build.job = build.real_build.active_step.name
|
||||||
@ -196,13 +184,13 @@ class runbot_build(models.Model):
|
|||||||
def copy(self, values=None):
|
def copy(self, values=None):
|
||||||
raise UserError("Cannot duplicate build!")
|
raise UserError("Cannot duplicate build!")
|
||||||
|
|
||||||
|
@api.model_create_single
|
||||||
def create(self, vals):
|
def create(self, vals):
|
||||||
branch = self.env['runbot.branch'].search([('id', '=', vals.get('branch_id', False))]) # branche 10174?
|
branch = self.env['runbot.branch'].search([('id', '=', vals.get('branch_id', False))]) # branche 10174?
|
||||||
if branch.no_build:
|
if branch.no_build:
|
||||||
return self.env['runbot.build']
|
return self.env['runbot.build']
|
||||||
vals['config_id'] = vals['config_id'] if 'config_id' in vals else branch.config_id.id
|
vals['config_id'] = vals['config_id'] if 'config_id' in vals else branch.config_id.id
|
||||||
build_id = super(runbot_build, self).create(vals)
|
build_id = super(runbot_build, self).create(vals)
|
||||||
build_id._update_nb_children(build_id.local_state)
|
|
||||||
extra_info = {'sequence': build_id.id if not build_id.sequence else build_id.sequence}
|
extra_info = {'sequence': build_id.id if not build_id.sequence else build_id.sequence}
|
||||||
context = self.env.context
|
context = self.env.context
|
||||||
|
|
||||||
@ -309,8 +297,6 @@ class runbot_build(models.Model):
|
|||||||
build_by_old_values = defaultdict(lambda: self.env['runbot.build'])
|
build_by_old_values = defaultdict(lambda: self.env['runbot.build'])
|
||||||
for record in self:
|
for record in self:
|
||||||
build_by_old_values[record.local_state] += record
|
build_by_old_values[record.local_state] += record
|
||||||
for local_state, builds in build_by_old_values.items():
|
|
||||||
builds._update_nb_children(values.get('local_state'), local_state)
|
|
||||||
assert 'state' not in values
|
assert 'state' not in values
|
||||||
local_result = values.get('local_result')
|
local_result = values.get('local_result')
|
||||||
for build in self:
|
for build in self:
|
||||||
@ -318,6 +304,8 @@ class runbot_build(models.Model):
|
|||||||
res = super(runbot_build, self).write(values)
|
res = super(runbot_build, self).write(values)
|
||||||
for build in self:
|
for build in self:
|
||||||
assert bool(not build.duplicate_id) ^ (build.local_state == 'duplicate') # don't change duplicate state without removing duplicate id.
|
assert bool(not build.duplicate_id) ^ (build.local_state == 'duplicate') # don't change duplicate state without removing duplicate id.
|
||||||
|
if 'log_counter' in values: # not 100% usefull but more correct ( see test_ir_logging)
|
||||||
|
self.flush()
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def update_build_end(self):
|
def update_build_end(self):
|
||||||
@ -360,6 +348,8 @@ class runbot_build(models.Model):
|
|||||||
build.job_time = int(dt2time(build.job_end) - dt2time(build.job_start))
|
build.job_time = int(dt2time(build.job_end) - dt2time(build.job_start))
|
||||||
elif build.job_start:
|
elif build.job_start:
|
||||||
build.job_time = int(time.time() - dt2time(build.job_start))
|
build.job_time = int(time.time() - dt2time(build.job_start))
|
||||||
|
else:
|
||||||
|
build.job_time = 0
|
||||||
|
|
||||||
@api.depends('build_start', 'build_end', 'duplicate_id.build_time')
|
@api.depends('build_start', 'build_end', 'duplicate_id.build_time')
|
||||||
def _compute_build_time(self):
|
def _compute_build_time(self):
|
||||||
@ -370,6 +360,8 @@ class runbot_build(models.Model):
|
|||||||
build.build_time = int(dt2time(build.build_end) - dt2time(build.build_start))
|
build.build_time = int(dt2time(build.build_end) - dt2time(build.build_start))
|
||||||
elif build.build_start:
|
elif build.build_start:
|
||||||
build.build_time = int(time.time() - dt2time(build.build_start))
|
build.build_time = int(time.time() - dt2time(build.build_start))
|
||||||
|
else:
|
||||||
|
build.build_time = 0
|
||||||
|
|
||||||
@api.depends('job_start', 'duplicate_id.build_age')
|
@api.depends('job_start', 'duplicate_id.build_age')
|
||||||
def _compute_build_age(self):
|
def _compute_build_age(self):
|
||||||
@ -379,6 +371,8 @@ class runbot_build(models.Model):
|
|||||||
build.build_age = build.duplicate_id.build_age
|
build.build_age = build.duplicate_id.build_age
|
||||||
elif build.job_start:
|
elif build.job_start:
|
||||||
build.build_age = int(time.time() - dt2time(build.build_start))
|
build.build_age = int(time.time() - dt2time(build.build_start))
|
||||||
|
else:
|
||||||
|
build.build_age = 0
|
||||||
|
|
||||||
def _get_params(self):
|
def _get_params(self):
|
||||||
try:
|
try:
|
||||||
|
@ -21,6 +21,7 @@ PYTHON_DEFAULT = "# type python code here\n\n\n\n\n\n"
|
|||||||
|
|
||||||
class Config(models.Model):
|
class Config(models.Model):
|
||||||
_name = "runbot.build.config"
|
_name = "runbot.build.config"
|
||||||
|
_description = "Build config"
|
||||||
_inherit = "mail.thread"
|
_inherit = "mail.thread"
|
||||||
|
|
||||||
name = fields.Char('Config name', required=True, unique=True, track_visibility='onchange', help="Unique name for config please use trigram as postfix for custom configs")
|
name = fields.Char('Config name', required=True, unique=True, track_visibility='onchange', help="Unique name for config please use trigram as postfix for custom configs")
|
||||||
@ -29,9 +30,10 @@ class Config(models.Model):
|
|||||||
update_github_state = fields.Boolean('Notify build state to github', default=False, track_visibility='onchange')
|
update_github_state = fields.Boolean('Notify build state to github', default=False, track_visibility='onchange')
|
||||||
protected = fields.Boolean('Protected', default=False, track_visibility='onchange')
|
protected = fields.Boolean('Protected', default=False, track_visibility='onchange')
|
||||||
group = fields.Many2one('runbot.build.config', 'Configuration group', help="Group of config's and config steps")
|
group = fields.Many2one('runbot.build.config', 'Configuration group', help="Group of config's and config steps")
|
||||||
group_name = fields.Char(related='group.name')
|
group_name = fields.Char('Group name', related='group.name')
|
||||||
|
monitoring_view_id = fields.Many2one('ir.ui.view', 'Monitoring view')
|
||||||
|
|
||||||
@api.model
|
@api.model_create_single
|
||||||
def create(self, values):
|
def create(self, values):
|
||||||
res = super(Config, self).create(values)
|
res = super(Config, self).create(values)
|
||||||
res._check_step_ids_order()
|
res._check_step_ids_order()
|
||||||
@ -84,6 +86,7 @@ class Config(models.Model):
|
|||||||
|
|
||||||
class ConfigStep(models.Model):
|
class ConfigStep(models.Model):
|
||||||
_name = 'runbot.build.config.step'
|
_name = 'runbot.build.config.step'
|
||||||
|
_description = "Config step"
|
||||||
_inherit = 'mail.thread'
|
_inherit = 'mail.thread'
|
||||||
|
|
||||||
# general info
|
# general info
|
||||||
@ -161,7 +164,7 @@ class ConfigStep(models.Model):
|
|||||||
copy._write({'protected': False})
|
copy._write({'protected': False})
|
||||||
return copy
|
return copy
|
||||||
|
|
||||||
@api.model
|
@api.model_create_single
|
||||||
def create(self, values):
|
def create(self, values):
|
||||||
self._check(values)
|
self._check(values)
|
||||||
return super(ConfigStep, self).create(values)
|
return super(ConfigStep, self).create(values)
|
||||||
@ -560,6 +563,7 @@ class ConfigStep(models.Model):
|
|||||||
|
|
||||||
class ConfigStepOrder(models.Model):
|
class ConfigStepOrder(models.Model):
|
||||||
_name = 'runbot.build.config.step.order'
|
_name = 'runbot.build.config.step.order'
|
||||||
|
_description = "Config step order"
|
||||||
_order = 'sequence, id'
|
_order = 'sequence, id'
|
||||||
# a kind of many2many rel with sequence
|
# a kind of many2many rel with sequence
|
||||||
|
|
||||||
@ -571,7 +575,7 @@ class ConfigStepOrder(models.Model):
|
|||||||
def _onchange_step_id(self):
|
def _onchange_step_id(self):
|
||||||
self.sequence = self.step_id.default_sequence
|
self.sequence = self.step_id.default_sequence
|
||||||
|
|
||||||
@api.model
|
@api.model_create_single
|
||||||
def create(self, values):
|
def create(self, values):
|
||||||
if 'sequence' not in values and values.get('step_id'):
|
if 'sequence' not in values and values.get('step_id'):
|
||||||
values['sequence'] = self.env['runbot.build.config.step'].browse(values.get('step_id')).default_sequence
|
values['sequence'] = self.env['runbot.build.config.step'].browse(values.get('step_id')).default_sequence
|
||||||
|
@ -3,6 +3,7 @@ from odoo import models, fields
|
|||||||
|
|
||||||
class RunbotBuildDependency(models.Model):
|
class RunbotBuildDependency(models.Model):
|
||||||
_name = "runbot.build.dependency"
|
_name = "runbot.build.dependency"
|
||||||
|
_description = "Build dependency"
|
||||||
|
|
||||||
build_id = fields.Many2one('runbot.build', 'Build', required=True, ondelete='cascade', index=True)
|
build_id = fields.Many2one('runbot.build', 'Build', required=True, ondelete='cascade', index=True)
|
||||||
dependecy_repo_id = fields.Many2one('runbot.repo', 'Dependency repo', required=True, ondelete='cascade')
|
dependecy_repo_id = fields.Many2one('runbot.repo', 'Dependency repo', required=True, ondelete='cascade')
|
||||||
|
@ -13,6 +13,8 @@ _logger = logging.getLogger(__name__)
|
|||||||
class RunbotBuildError(models.Model):
|
class RunbotBuildError(models.Model):
|
||||||
|
|
||||||
_name = "runbot.build.error"
|
_name = "runbot.build.error"
|
||||||
|
_description = "Build error"
|
||||||
|
|
||||||
_inherit = "mail.thread"
|
_inherit = "mail.thread"
|
||||||
_rec_name = "id"
|
_rec_name = "id"
|
||||||
|
|
||||||
@ -34,7 +36,7 @@ class RunbotBuildError(models.Model):
|
|||||||
parent_id = fields.Many2one('runbot.build.error', 'Linked to')
|
parent_id = fields.Many2one('runbot.build.error', 'Linked to')
|
||||||
child_ids = fields.One2many('runbot.build.error', 'parent_id', string='Child Errors', context={'active_test': False})
|
child_ids = fields.One2many('runbot.build.error', 'parent_id', string='Child Errors', context={'active_test': False})
|
||||||
children_build_ids = fields.Many2many('runbot.build', compute='_compute_children_build_ids', string='Children builds')
|
children_build_ids = fields.Many2many('runbot.build', compute='_compute_children_build_ids', string='Children builds')
|
||||||
error_history_ids = fields.One2many('runbot.build.error', compute='_compute_error_history_ids', string='Old errors')
|
error_history_ids = fields.Many2many('runbot.build.error', compute='_compute_error_history_ids', string='Old errors', context={'active_test': False})
|
||||||
first_seen_build_id = fields.Many2one('runbot.build', compute='_compute_first_seen_build_id', string='First Seen build')
|
first_seen_build_id = fields.Many2one('runbot.build', compute='_compute_first_seen_build_id', string='First Seen build')
|
||||||
first_seen_date = fields.Datetime(string='First Seen Date', related='first_seen_build_id.create_date')
|
first_seen_date = fields.Datetime(string='First Seen Date', related='first_seen_build_id.create_date')
|
||||||
last_seen_build_id = fields.Many2one('runbot.build', compute='_compute_last_seen_build_id', string='Last Seen build')
|
last_seen_build_id = fields.Many2one('runbot.build', compute='_compute_last_seen_build_id', string='Last Seen build')
|
||||||
@ -47,7 +49,7 @@ class RunbotBuildError(models.Model):
|
|||||||
if build_error.test_tags and '-' in build_error.test_tags:
|
if build_error.test_tags and '-' in build_error.test_tags:
|
||||||
raise ValidationError('Build error test_tags should not be negated')
|
raise ValidationError('Build error test_tags should not be negated')
|
||||||
|
|
||||||
@api.model
|
@api.model_create_single
|
||||||
def create(self, vals):
|
def create(self, vals):
|
||||||
cleaners = self.env['runbot.error.regex'].search([('re_type', '=', 'cleaning')])
|
cleaners = self.env['runbot.error.regex'].search([('re_type', '=', 'cleaning')])
|
||||||
content = vals.get('content')
|
content = vals.get('content')
|
||||||
@ -57,7 +59,6 @@ class RunbotBuildError(models.Model):
|
|||||||
})
|
})
|
||||||
return super().create(vals)
|
return super().create(vals)
|
||||||
|
|
||||||
@api.multi
|
|
||||||
def write(self, vals):
|
def write(self, vals):
|
||||||
if 'active' in vals:
|
if 'active' in vals:
|
||||||
for build_error in self:
|
for build_error in self:
|
||||||
@ -100,7 +101,7 @@ class RunbotBuildError(models.Model):
|
|||||||
for build_error in self:
|
for build_error in self:
|
||||||
build_error.first_seen_build_id = build_error.children_build_ids and build_error.children_build_ids[-1] or False
|
build_error.first_seen_build_id = build_error.children_build_ids and build_error.children_build_ids[-1] or False
|
||||||
|
|
||||||
@api.depends('fingerprint')
|
@api.depends('fingerprint', 'child_ids.fingerprint')
|
||||||
def _compute_error_history_ids(self):
|
def _compute_error_history_ids(self):
|
||||||
for error in self:
|
for error in self:
|
||||||
fingerprints = [error.fingerprint] + [rec.fingerprint for rec in error.child_ids]
|
fingerprints = [error.fingerprint] + [rec.fingerprint for rec in error.child_ids]
|
||||||
@ -160,9 +161,9 @@ class RunbotBuildError(models.Model):
|
|||||||
|
|
||||||
@api.model
|
@api.model
|
||||||
def test_tags_list(self):
|
def test_tags_list(self):
|
||||||
active_errors = self.search([('test_tags', '!=', 'False'), ('random', '=', True)])
|
active_errors = self.search([('test_tags', '!=', False), ('random', '=', True)])
|
||||||
test_tag_list = active_errors.mapped('test_tags')
|
test_tag_list = active_errors.mapped('test_tags')
|
||||||
return [test_tag for error_tags in test_tag_list for test_tag in error_tags.split(',')]
|
return [test_tag for error_tags in test_tag_list for test_tag in (error_tags).split(',')]
|
||||||
|
|
||||||
@api.model
|
@api.model
|
||||||
def disabling_tags(self):
|
def disabling_tags(self):
|
||||||
@ -172,6 +173,7 @@ class RunbotBuildError(models.Model):
|
|||||||
class RunbotBuildErrorTag(models.Model):
|
class RunbotBuildErrorTag(models.Model):
|
||||||
|
|
||||||
_name = "runbot.build.error.tag"
|
_name = "runbot.build.error.tag"
|
||||||
|
_description = "Build error tag"
|
||||||
|
|
||||||
name = fields.Char('Tag')
|
name = fields.Char('Tag')
|
||||||
error_ids = fields.Many2many('runbot.build.error', string='Errors')
|
error_ids = fields.Many2many('runbot.build.error', string='Errors')
|
||||||
@ -180,6 +182,7 @@ class RunbotBuildErrorTag(models.Model):
|
|||||||
class RunbotErrorRegex(models.Model):
|
class RunbotErrorRegex(models.Model):
|
||||||
|
|
||||||
_name = "runbot.error.regex"
|
_name = "runbot.error.regex"
|
||||||
|
_description = "Build error regex"
|
||||||
_inherit = "mail.thread"
|
_inherit = "mail.thread"
|
||||||
_rec_name = 'id'
|
_rec_name = 'id'
|
||||||
_order = 'sequence, id'
|
_order = 'sequence, id'
|
||||||
|
@ -16,9 +16,8 @@ class runbot_event(models.Model):
|
|||||||
|
|
||||||
build_id = fields.Many2one('runbot.build', 'Build', index=True, ondelete='cascade')
|
build_id = fields.Many2one('runbot.build', 'Build', index=True, ondelete='cascade')
|
||||||
active_step_id = fields.Many2one('runbot.build.config.step', 'Active step', index=True)
|
active_step_id = fields.Many2one('runbot.build.config.step', 'Active step', index=True)
|
||||||
type = fields.Selection(TYPES, string='Type', required=True, index=True)
|
type = fields.Selection(selection_add=TYPES, string='Type', required=True, index=True)
|
||||||
|
|
||||||
@api.model_cr
|
|
||||||
def init(self):
|
def init(self):
|
||||||
parent_class = super(runbot_event, self)
|
parent_class = super(runbot_event, self)
|
||||||
if hasattr(parent_class, 'init'):
|
if hasattr(parent_class, 'init'):
|
||||||
@ -75,6 +74,7 @@ FOR EACH ROW EXECUTE PROCEDURE runbot_set_logging_build();
|
|||||||
|
|
||||||
class RunbotErrorLog(models.Model):
|
class RunbotErrorLog(models.Model):
|
||||||
_name = "runbot.error.log"
|
_name = "runbot.error.log"
|
||||||
|
_description = "Error log"
|
||||||
_auto = False
|
_auto = False
|
||||||
_order = 'id desc'
|
_order = 'id desc'
|
||||||
|
|
||||||
@ -130,7 +130,6 @@ class RunbotErrorLog(models.Model):
|
|||||||
BuildError._parse_logs(self)
|
BuildError._parse_logs(self)
|
||||||
|
|
||||||
|
|
||||||
@api.model_cr
|
|
||||||
def init(self):
|
def init(self):
|
||||||
""" Create an SQL view for ir.logging """
|
""" Create an SQL view for ir.logging """
|
||||||
tools.drop_view_if_exists(self._cr, 'runbot_error_log')
|
tools.drop_view_if_exists(self._cr, 'runbot_error_log')
|
||||||
|
@ -6,6 +6,7 @@ _logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
class RunboHost(models.Model):
|
class RunboHost(models.Model):
|
||||||
_name = "runbot.host"
|
_name = "runbot.host"
|
||||||
|
_description = "Host"
|
||||||
_order = 'id'
|
_order = 'id'
|
||||||
_inherit = 'mail.thread'
|
_inherit = 'mail.thread'
|
||||||
|
|
||||||
@ -37,7 +38,7 @@ class RunboHost(models.Model):
|
|||||||
host.nb_testing = count_by_host_state[host.name].get('testing', 0)
|
host.nb_testing = count_by_host_state[host.name].get('testing', 0)
|
||||||
host.nb_running = count_by_host_state[host.name].get('running', 0)
|
host.nb_running = count_by_host_state[host.name].get('running', 0)
|
||||||
|
|
||||||
@api.model
|
@api.model_create_single
|
||||||
def create(self, values):
|
def create(self, values):
|
||||||
if not 'disp_name' in values:
|
if not 'disp_name' in values:
|
||||||
values['disp_name'] = values['name']
|
values['disp_name'] = values['name']
|
||||||
@ -57,10 +58,15 @@ class RunboHost(models.Model):
|
|||||||
return int(icp.get_param('runbot.runbot_running_max', default=75))
|
return int(icp.get_param('runbot.runbot_running_max', default=75))
|
||||||
|
|
||||||
def set_psql_conn_count(self):
|
def set_psql_conn_count(self):
|
||||||
|
|
||||||
_logger.debug('Updating psql connection count...')
|
_logger.debug('Updating psql connection count...')
|
||||||
self.ensure_one()
|
self.ensure_one()
|
||||||
with local_pgadmin_cursor() as local_cr:
|
with local_pgadmin_cursor() as local_cr:
|
||||||
local_cr.execute("SELECT sum(numbackends) FROM pg_stat_database;")
|
local_cr.execute("SELECT sum(numbackends) FROM pg_stat_database;")
|
||||||
res = local_cr.fetchone()
|
res = local_cr.fetchone()
|
||||||
self.psql_conn_count = res and res[0] or 0
|
self.psql_conn_count = res and res[0] or 0
|
||||||
|
|
||||||
|
def _total_testing(self):
|
||||||
|
return sum(host.nb_testing for host in self)
|
||||||
|
|
||||||
|
def _total_workers(self):
|
||||||
|
return sum(host.get_nb_worker() for host in self)
|
||||||
|
@ -4,7 +4,7 @@ from dateutil.relativedelta import relativedelta
|
|||||||
from odoo import models, fields
|
from odoo import models, fields
|
||||||
|
|
||||||
odoo.service.server.SLEEP_INTERVAL = 5
|
odoo.service.server.SLEEP_INTERVAL = 5
|
||||||
odoo.addons.base.ir.ir_cron._intervalTypes['seconds'] = lambda interval: relativedelta(seconds=interval)
|
odoo.addons.base.models.ir_cron._intervalTypes['seconds'] = lambda interval: relativedelta(seconds=interval)
|
||||||
|
|
||||||
class ir_cron(models.Model):
|
class ir_cron(models.Model):
|
||||||
_inherit = "ir.cron"
|
_inherit = "ir.cron"
|
||||||
|
@ -30,10 +30,11 @@ class RunbotException(Exception):
|
|||||||
class runbot_repo(models.Model):
|
class runbot_repo(models.Model):
|
||||||
|
|
||||||
_name = "runbot.repo"
|
_name = "runbot.repo"
|
||||||
|
_description = "Repo"
|
||||||
_order = 'sequence, id'
|
_order = 'sequence, id'
|
||||||
|
|
||||||
name = fields.Char('Repository', required=True)
|
name = fields.Char('Repository', required=True)
|
||||||
short_name = fields.Char('Repository', compute='_compute_short_name', store=False, readonly=True)
|
short_name = fields.Char('Short name', compute='_compute_short_name', store=False, readonly=True)
|
||||||
sequence = fields.Integer('Sequence')
|
sequence = fields.Integer('Sequence')
|
||||||
path = fields.Char(compute='_get_path', string='Directory', readonly=True)
|
path = fields.Char(compute='_get_path', string='Directory', readonly=True)
|
||||||
base = fields.Char(compute='_get_base_url', string='Base URL', readonly=True) # Could be renamed to a more explicit name like base_url
|
base = fields.Char(compute='_get_base_url', string='Base URL', readonly=True) # Could be renamed to a more explicit name like base_url
|
||||||
@ -60,7 +61,7 @@ class runbot_repo(models.Model):
|
|||||||
token = fields.Char("Github token", groups="runbot.group_runbot_admin")
|
token = fields.Char("Github token", groups="runbot.group_runbot_admin")
|
||||||
group_ids = fields.Many2many('res.groups', string='Limited to groups')
|
group_ids = fields.Many2many('res.groups', string='Limited to groups')
|
||||||
|
|
||||||
repo_config_id = fields.Many2one('runbot.build.config', 'Run Config')
|
repo_config_id = fields.Many2one('runbot.build.config', 'Repo Config')
|
||||||
config_id = fields.Many2one('runbot.build.config', 'Run Config', compute='_compute_config_id', inverse='_inverse_config_id')
|
config_id = fields.Many2one('runbot.build.config', 'Run Config', compute='_compute_config_id', inverse='_inverse_config_id')
|
||||||
|
|
||||||
server_files = fields.Char('Server files', help='Comma separated list of possible server files') # odoo-bin,openerp-server,openerp-server.py
|
server_files = fields.Char('Server files', help='Comma separated list of possible server files') # odoo-bin,openerp-server,openerp-server.py
|
||||||
@ -104,20 +105,15 @@ class runbot_repo(models.Model):
|
|||||||
for repo in self:
|
for repo in self:
|
||||||
repo.hook_time = times.get(repo.id, 0)
|
repo.hook_time = times.get(repo.id, 0)
|
||||||
|
|
||||||
def write(self, values):
|
def set_hook_time(self, value):
|
||||||
# hooktime and reftime table are here to avoid sql update on repo.
|
|
||||||
# using inverse will still trigger write_date and write_uid update.
|
|
||||||
# this hack allows to avoid that
|
|
||||||
|
|
||||||
hook_time = values.pop('hook_time', None)
|
|
||||||
get_ref_time = values.pop('get_ref_time', None)
|
|
||||||
for repo in self:
|
for repo in self:
|
||||||
if hook_time:
|
self.env['runbot.repo.hooktime'].create({'time': value, 'repo_id': repo.id})
|
||||||
self.env['runbot.repo.hooktime'].create({'time': hook_time, 'repo_id': repo.id})
|
self.invalidate_cache()
|
||||||
if get_ref_time:
|
|
||||||
self.env['runbot.repo.reftime'].create({'time': get_ref_time, 'repo_id': repo.id})
|
def set_ref_time(self, value):
|
||||||
if values:
|
for repo in self:
|
||||||
super().write(values)
|
self.env['runbot.repo.reftime'].create({'time': value, 'repo_id': repo.id})
|
||||||
|
self.invalidate_cache()
|
||||||
|
|
||||||
def _gc_times(self):
|
def _gc_times(self):
|
||||||
self.env.cr.execute("""
|
self.env.cr.execute("""
|
||||||
@ -283,7 +279,7 @@ class runbot_repo(models.Model):
|
|||||||
|
|
||||||
get_ref_time = round(self._get_fetch_head_time(), 4)
|
get_ref_time = round(self._get_fetch_head_time(), 4)
|
||||||
if not self.get_ref_time or get_ref_time > self.get_ref_time:
|
if not self.get_ref_time or get_ref_time > self.get_ref_time:
|
||||||
self.get_ref_time = get_ref_time
|
self.set_ref_time(get_ref_time)
|
||||||
fields = ['refname', 'objectname', 'committerdate:iso8601', 'authorname', 'authoremail', 'subject', 'committername', 'committeremail']
|
fields = ['refname', 'objectname', 'committerdate:iso8601', 'authorname', 'authoremail', 'subject', 'committername', 'committeremail']
|
||||||
fmt = "%00".join(["%(" + field + ")" for field in fields])
|
fmt = "%00".join(["%(" + field + ")" for field in fields])
|
||||||
git_refs = self._git(['for-each-ref', '--format', fmt, '--sort=-committerdate', 'refs/heads', 'refs/pull'])
|
git_refs = self._git(['for-each-ref', '--format', fmt, '--sort=-committerdate', 'refs/heads', 'refs/pull'])
|
||||||
@ -392,7 +388,7 @@ class runbot_repo(models.Model):
|
|||||||
indirect.build_type = 'indirect'
|
indirect.build_type = 'indirect'
|
||||||
new_build.revdep_build_ids += indirect
|
new_build.revdep_build_ids += indirect
|
||||||
|
|
||||||
@api.multi
|
|
||||||
def _create_pending_builds(self):
|
def _create_pending_builds(self):
|
||||||
""" Find new commits in physical repos"""
|
""" Find new commits in physical repos"""
|
||||||
refs = {}
|
refs = {}
|
||||||
@ -451,7 +447,6 @@ class runbot_repo(models.Model):
|
|||||||
repo = self
|
repo = self
|
||||||
repo._git(['fetch', '-p', 'origin', '+refs/heads/*:refs/heads/*', '+refs/pull/*/head:refs/pull/*'])
|
repo._git(['fetch', '-p', 'origin', '+refs/heads/*:refs/heads/*', '+refs/pull/*/head:refs/pull/*'])
|
||||||
|
|
||||||
@api.multi
|
|
||||||
def _update(self, force=True):
|
def _update(self, force=True):
|
||||||
""" Update the physical git reposotories on FS"""
|
""" Update the physical git reposotories on FS"""
|
||||||
for repo in reversed(self):
|
for repo in reversed(self):
|
||||||
@ -462,10 +457,9 @@ class runbot_repo(models.Model):
|
|||||||
|
|
||||||
def _commit(self):
|
def _commit(self):
|
||||||
self.env.cr.commit()
|
self.env.cr.commit()
|
||||||
self.invalidate_cache()
|
self.env.cache.invalidate()
|
||||||
self.env.reset()
|
self.env.clear()
|
||||||
|
|
||||||
@api.multi
|
|
||||||
def _scheduler(self, host):
|
def _scheduler(self, host):
|
||||||
nb_workers = host.get_nb_worker()
|
nb_workers = host.get_nb_worker()
|
||||||
|
|
||||||
@ -693,7 +687,7 @@ class runbot_repo(models.Model):
|
|||||||
self._commit()
|
self._commit()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.env.cr.rollback()
|
self.env.cr.rollback()
|
||||||
self.env.reset()
|
self.env.clear()
|
||||||
_logger.exception(e)
|
_logger.exception(e)
|
||||||
message = str(e)
|
message = str(e)
|
||||||
if host.last_exception == message:
|
if host.last_exception == message:
|
||||||
@ -765,6 +759,7 @@ class runbot_repo(models.Model):
|
|||||||
|
|
||||||
class RefTime(models.Model):
|
class RefTime(models.Model):
|
||||||
_name = "runbot.repo.reftime"
|
_name = "runbot.repo.reftime"
|
||||||
|
_description = "Repo reftime"
|
||||||
_log_access = False
|
_log_access = False
|
||||||
|
|
||||||
time = fields.Float('Time', index=True, required=True)
|
time = fields.Float('Time', index=True, required=True)
|
||||||
@ -773,7 +768,8 @@ class RefTime(models.Model):
|
|||||||
|
|
||||||
class HookTime(models.Model):
|
class HookTime(models.Model):
|
||||||
_name = "runbot.repo.hooktime"
|
_name = "runbot.repo.hooktime"
|
||||||
|
_description = "Repo hooktime"
|
||||||
_log_access = False
|
_log_access = False
|
||||||
|
|
||||||
time = fields.Float('Time')
|
time = fields.Float('Time')
|
||||||
repo_id = fields.Many2one('runbot.repo', 'Repository', required=True, ondelete='cascade')
|
repo_id = fields.Many2one('runbot.repo', 'Repository', required=True, ondelete='cascade')
|
||||||
|
@ -33,7 +33,6 @@ class ResConfigSettings(models.TransientModel):
|
|||||||
)
|
)
|
||||||
return res
|
return res
|
||||||
|
|
||||||
@api.multi
|
|
||||||
def set_values(self):
|
def set_values(self):
|
||||||
super(ResConfigSettings, self).set_values()
|
super(ResConfigSettings, self).set_values()
|
||||||
set_param = self.env['ir.config_parameter'].sudo().set_param
|
set_param = self.env['ir.config_parameter'].sudo().set_param
|
||||||
|
@ -17,6 +17,10 @@
|
|||||||
<field name="implied_ids" eval="[(4, ref('runbot.group_user'))]"/>
|
<field name="implied_ids" eval="[(4, ref('runbot.group_user'))]"/>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
<record id="base.group_portal" model="res.groups">
|
||||||
|
<field name="implied_ids" eval="[(4, ref('runbot.group_user'))]"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
<record id="group_runbot_admin" model="res.groups">
|
<record id="group_runbot_admin" model="res.groups">
|
||||||
<field name="name">Manager</field>
|
<field name="name">Manager</field>
|
||||||
<field name="category_id" ref="module_category"/>
|
<field name="category_id" ref="module_category"/>
|
||||||
|
80
runbot/static/src/css/runbot.css
Normal file
80
runbot/static/src/css/runbot.css
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
.separator {
|
||||||
|
border-top: 2px solid #666;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-toggle="collapse"] .fa:before {
|
||||||
|
content: "\f139";
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-toggle="collapse"].collapsed .fa:before {
|
||||||
|
content: "\f13a";
|
||||||
|
}
|
||||||
|
|
||||||
|
body, .table{
|
||||||
|
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||||
|
color:#444;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-default {
|
||||||
|
background-color: #fff;
|
||||||
|
color: #444;
|
||||||
|
border-color: #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-default:hover {
|
||||||
|
background-color: #ccc;
|
||||||
|
color: #444;
|
||||||
|
border-color: #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-sm, .btn-group-sm > .btn {
|
||||||
|
padding: 0.25rem 0.5rem;
|
||||||
|
font-size: 0.89rem;
|
||||||
|
line-height: 1.5;
|
||||||
|
border-radius: 0.2rem;
|
||||||
|
}
|
||||||
|
.btn-ssm, .btn-group-ssm > .btn {
|
||||||
|
padding: 0.22rem 0.4rem;
|
||||||
|
font-size: 0.82rem;
|
||||||
|
line-height: 1;
|
||||||
|
border-radius: 0.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.killed, .bg-killed, .bg-killed-light {
|
||||||
|
background-color: #aaa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-toggle:after { content: none }
|
||||||
|
|
||||||
|
.branch_name {
|
||||||
|
max-width: 250px;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.branch_time {
|
||||||
|
float:right;
|
||||||
|
margin-left:10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg-success-light {
|
||||||
|
background-color: #dff0d8;
|
||||||
|
}
|
||||||
|
.bg-danger-light {
|
||||||
|
background-color: #f2dede;
|
||||||
|
}
|
||||||
|
.bg-info-light {
|
||||||
|
background-color: #d9edf7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-info{
|
||||||
|
color: #096b72 !important;
|
||||||
|
}
|
||||||
|
.build_subject_buttons {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
.build_buttons {
|
||||||
|
margin-left: auto
|
||||||
|
}
|
@ -41,7 +41,7 @@
|
|||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
$(function() {
|
//$(function() {
|
||||||
new Clipboard('.clipbtn');
|
// new Clipboard('.clipbtn');
|
||||||
});
|
//});
|
||||||
})(jQuery);
|
})(jQuery);
|
||||||
|
@ -1,12 +0,0 @@
|
|||||||
.separator {
|
|
||||||
border-top: 2px solid #666;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-toggle="collapse"] .fa:before {
|
|
||||||
content: "\f139";
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-toggle="collapse"].collapsed .fa:before {
|
|
||||||
content: "\f13a";
|
|
||||||
}
|
|
@ -33,7 +33,7 @@
|
|||||||
<t t-if="build.global_result == 'skipped'"><t t-set="rowclass">default</t></t>
|
<t t-if="build.global_result == 'skipped'"><t t-set="rowclass">default</t></t>
|
||||||
<t t-if="build.global_result in ['killed', 'manually_killed']"><t t-set="rowclass">killed</t></t>
|
<t t-if="build.global_result in ['killed', 'manually_killed']"><t t-set="rowclass">killed</t></t>
|
||||||
</t>
|
</t>
|
||||||
<tr t-attf-class="{{rowclass}}">
|
<tr t-attf-class="bg-{{rowclass}}-light">
|
||||||
<td><t t-esc="build.create_date" /></td>
|
<td><t t-esc="build.create_date" /></td>
|
||||||
<td><a t-attf-href="/runbot/build/{{build['id']}}" title="Build details" aria-label="Build details"><t t-esc="build.dest" /></a></td>
|
<td><a t-attf-href="/runbot/build/{{build['id']}}" title="Build details" aria-label="Build details"><t t-esc="build.dest" /></a></td>
|
||||||
<td>
|
<td>
|
||||||
|
@ -40,13 +40,6 @@
|
|||||||
</t>
|
</t>
|
||||||
</small>
|
</small>
|
||||||
</t>
|
</t>
|
||||||
<t t-set="nb_sum" t-value="bu.nb_pending+bu.nb_testing+bu.nb_running"/>
|
|
||||||
<t t-if="nb_sum > 1"><!-- maybe only display this info if > 3 -->
|
|
||||||
<span t-attf-title="{{bu.nb_pending}} pending, {{bu.nb_testing}} testing, {{bu.nb_running}} running">
|
|
||||||
<t t-esc="nb_sum"/>
|
|
||||||
<i class="fa fa-cogs"/>
|
|
||||||
</span>
|
|
||||||
</t>
|
|
||||||
</template>
|
</template>
|
||||||
<template id="runbot.build_button">
|
<template id="runbot.build_button">
|
||||||
<div t-attf-class="pull-right">
|
<div t-attf-class="pull-right">
|
||||||
@ -54,9 +47,9 @@
|
|||||||
<a t-if="bu.real_build.local_state=='running'" t-attf-href="http://{{bu['domain']}}/?db={{bu.real_build.dest}}-all" class="btn btn-primary" title="Sign in on this build" aria-label="Sign in on this build"><i class="fa fa-sign-in"/></a>
|
<a t-if="bu.real_build.local_state=='running'" t-attf-href="http://{{bu['domain']}}/?db={{bu.real_build.dest}}-all" class="btn btn-primary" title="Sign in on this build" aria-label="Sign in on this build"><i class="fa fa-sign-in"/></a>
|
||||||
<a t-if="bu.real_build.local_state=='done' and bu.real_build.requested_action != 'wake_up'" href="#" t-att-data-runbot-build="bu.real_build.id" class="btn btn-default runbot-wakeup" title="Wake up this build" aria-label="Wake up this build"><i class="fa fa-coffee"/></a>
|
<a t-if="bu.real_build.local_state=='done' and bu.real_build.requested_action != 'wake_up'" href="#" t-att-data-runbot-build="bu.real_build.id" class="btn btn-default runbot-wakeup" title="Wake up this build" aria-label="Wake up this build"><i class="fa fa-coffee"/></a>
|
||||||
<a t-attf-href="/runbot/build/{{bu['id']}}" class="btn btn-default" title="Build details" aria-label="Build details"><i class="fa fa-file-text-o"/></a>
|
<a t-attf-href="/runbot/build/{{bu['id']}}" class="btn btn-default" title="Build details" aria-label="Build details"><i class="fa fa-file-text-o"/></a>
|
||||||
<a t-attf-href="https://#{repo.base}/commit/#{bu['name']}" class="btn btn-default" title="Open commit on GitHub" aria-label="Open commit on GitHub"><i class="fa fa-github"/></a>
|
<a t-if="show_commit_button" t-attf-href="https://#{repo.base}/commit/#{bu['name']}" class="btn btn-default" title="Open commit on GitHub" aria-label="Open commit on GitHub"><i class="fa fa-github"/></a>
|
||||||
<button class="btn btn-default dropdown-toggle" data-toggle="dropdown" title="Build options" aria-label="Build options" aria-expanded="false"><i class="fa fa-cog"/><span class="caret"></span></button>
|
<button class="btn btn-default dropdown-toggle" data-toggle="dropdown" title="Build options" aria-label="Build options" aria-expanded="false"><i class="fa fa-cog"/><span class="caret"></span></button>
|
||||||
<ul class="dropdown-menu" role="menu">
|
<ul class="dropdown-menu dropdown-menu-right" role="menu">
|
||||||
<li t-if="bu.global_result=='skipped'" groups="runbot.group_runbot_admin">
|
<li t-if="bu.global_result=='skipped'" groups="runbot.group_runbot_admin">
|
||||||
<a href="#" class="runbot-rebuild" t-att-data-runbot-build="bu['id']">Force Build <i class="fa fa-level-up"></i></a>
|
<a href="#" class="runbot-rebuild" t-att-data-runbot-build="bu['id']">Force Build <i class="fa fa-level-up"></i></a>
|
||||||
</li>
|
</li>
|
||||||
@ -89,12 +82,12 @@
|
|||||||
<li t-if="bu.global_state not in ('testing', 'waiting', 'pending')" class="divider"></li>
|
<li t-if="bu.global_state not in ('testing', 'waiting', 'pending')" class="divider"></li>
|
||||||
<li><a t-attf-href="/runbot/build/{{bu['id']}}">Logs <i class="fa fa-file-text-o"/></a></li>
|
<li><a t-attf-href="/runbot/build/{{bu['id']}}">Logs <i class="fa fa-file-text-o"/></a></li>
|
||||||
<t t-set="log_url" t-value="'http://%s' % bu.real_build.host if bu.real_build.host != fqdn else ''"/>
|
<t t-set="log_url" t-value="'http://%s' % bu.real_build.host if bu.real_build.host != fqdn else ''"/>
|
||||||
<t t-if="bu.real_build.host" t-foreach="bu.log_list.split(',')" t-as="log_name" >
|
<t t-if="bu.real_build.host" t-foreach="(bu.log_list or '').split(',')" t-as="log_name" >
|
||||||
<li><a t-attf-href="{{log_url}}/runbot/static/build/#{bu['real_build'].dest}/logs/#{log_name}.txt">Full <t t-esc="log_name"/> logs <i class="fa fa-file-text-o"/></a></li>
|
<li><a t-attf-href="{{log_url}}/runbot/static/build/#{bu['real_build'].dest}/logs/#{log_name}.txt">Full <t t-esc="log_name"/> logs <i class="fa fa-file-text-o"/></a></li>
|
||||||
</t>
|
</t>
|
||||||
<li t-if="bu.coverage and bu.real_build.host"><a t-attf-href="http://{{bu.real_build.host}}/runbot/static/build/#{bu['real_build'].dest}/coverage/index.html">Coverage <i class="fa fa-file-text-o"/></a></li>
|
<li t-if="bu.coverage and bu.real_build.host"><a t-attf-href="http://{{bu.real_build.host}}/runbot/static/build/#{bu['real_build'].dest}/coverage/index.html">Coverage <i class="fa fa-file-text-o"/></a></li>
|
||||||
<li t-if="bu.global_state!='pending'" class="divider"></li>
|
<li t-if="bu.global_state!='pending'" class="divider"></li>
|
||||||
<li><a t-attf-href="{{br['branch'].branch_url}}">Branch or pull <i class="fa fa-github"/></a></li>
|
<li><a t-attf-href="{{br['branch'].branch_url}}"><t t-esc="'Branch ' if not br['branch'].pull_head_name else 'Pull '"/><i class="fa fa-github"/></a></li>
|
||||||
<li><a t-attf-href="https://{{repo.base}}/commit/{{bu['name']}}">Commit <i class="fa fa-github"/></a></li>
|
<li><a t-attf-href="https://{{repo.base}}/commit/{{bu['name']}}">Commit <i class="fa fa-github"/></a></li>
|
||||||
<li><a t-attf-href="https://{{repo.base}}/compare/{{br['branch'].branch_name}}">Compare <i class="fa fa-github"/></a></li>
|
<li><a t-attf-href="https://{{repo.base}}/compare/{{br['branch'].branch_name}}">Compare <i class="fa fa-github"/></a></li>
|
||||||
<!-- TODO branch.pull from -->
|
<!-- TODO branch.pull from -->
|
||||||
@ -118,54 +111,32 @@
|
|||||||
</template>
|
</template>
|
||||||
<template id="runbot.build">
|
<template id="runbot.build">
|
||||||
<t t-call='website.layout'>
|
<t t-call='website.layout'>
|
||||||
<div class="container" style="width: 100%;">
|
|
||||||
|
<t t-set="nav_form">
|
||||||
|
<form class="form-inline">
|
||||||
|
<div class="btn-group">
|
||||||
|
<t t-call="runbot.build_button">
|
||||||
|
<t t-set="bu" t-value="build"/>
|
||||||
|
<t t-set="klass" t-value="''"/>
|
||||||
|
<t t-set="show_commit_button" t-value="True"/>
|
||||||
|
</t>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<form class="form-inline" t-attf-action="/runbot/build/#{build['id']}/force" method='POST' t-if="request.params.get('ask_rebuild')" groups="runbot.group_user">
|
||||||
|
<a href='#' class="btn btn-danger runbot-rebuild" t-attf-data-runbot-build="#{build['id']}" > <i class='fa fa-refresh'/> Force Rebuild</a>
|
||||||
|
</form>
|
||||||
|
</t>
|
||||||
<div class="row" >
|
<div class="row" >
|
||||||
<div class='col-md-12'>
|
<div class='col-md-12'>
|
||||||
<nav class="navbar navbar-default" role="navigation">
|
|
||||||
<div class="container-fluid">
|
|
||||||
<div class="navbar-header">
|
|
||||||
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1">
|
|
||||||
<span class="sr-only">Toggle navigation</span>
|
|
||||||
<span class="icon-bar"></span>
|
|
||||||
<span class="icon-bar"></span>
|
|
||||||
<span class="icon-bar"></span>
|
|
||||||
</button>
|
|
||||||
<a class="navbar-brand" t-attf-href="/runbot/repo/#{ slug(repo) }"><b><t t-esc="repo.base"/></b></a>
|
|
||||||
<a class="navbar-brand" t-attf-href="/runbot/build/{{build['id']}}">
|
|
||||||
<t t-esc="build['dest']"/>
|
|
||||||
<t t-call="runbot.build_name">
|
|
||||||
<t t-set="bu" t-value="build"/>
|
|
||||||
</t>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
|
|
||||||
<form class="navbar-form navbar-left form-inline">
|
|
||||||
<div class="btn-group">
|
|
||||||
<t t-call="runbot.build_button">
|
|
||||||
<t t-set="bu" t-value="build"/>
|
|
||||||
<t t-set="klass" t-value="''"/>
|
|
||||||
</t>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
<p class="navbar-text">
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<form class="navbar-form navbar-left form-inline" t-attf-action="/runbot/build/#{build['id']}/force" method='POST' t-if="request.params.get('ask_rebuild')" groups="runbot.group_user">
|
|
||||||
<a href='#' class="btn btn-danger runbot-rebuild" t-attf-data-runbot-build="#{build['id']}" > <i class='fa fa-refresh'/> Force Rebuild</a>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<table class="table table-condensed tabel-bordered">
|
<table class="table table-condensed tabel-bordered">
|
||||||
<tr>
|
<tr>
|
||||||
<t t-set="rowclass"><t t-call="runbot.build_class"><t t-set="build" t-value="build"/></t></t>
|
<t t-set="rowclass"><t t-call="runbot.build_class"><t t-set="build" t-value="build"/></t></t>
|
||||||
<td t-attf-class="{{rowclass}}">
|
<td t-attf-class="bg-{{rowclass.strip()}}-light">
|
||||||
Subject: <t t-esc="build['subject']"/><br/>
|
Subject: <t t-esc="build['subject']"/><br/>
|
||||||
Author: <t t-esc="build['author']"/><br/>
|
Author: <t t-esc="build['author']"/><br/>
|
||||||
Committer: <t t-esc="build['committer']"/><br/>
|
Committer: <t t-esc="build['committer']"/><br/>
|
||||||
Commit: <a t-attf-href="https://{{build.repo_id.base}}/commit/{{build.name}}"><t t-esc="build.name"/></a><br/>
|
Commit: <a t-attf-href="https://{{build.repo_id.base}}/commit/{{build.name}}"><t t-esc="build.name"/></a><br/>
|
||||||
<t t-foreach="build.dependency_ids" t-as="dep">
|
<t t-foreach="build.sudo().dependency_ids" t-as="dep">
|
||||||
Dep: <t t-esc="dep.dependecy_repo_id.short_name"/>:<a t-attf-href="https://{{dep.dependecy_repo_id.base}}/commit/{{dep.dependency_hash}}"><t t-esc="dep.dependency_hash"/></a>
|
Dep: <t t-esc="dep.dependecy_repo_id.short_name"/>:<a t-attf-href="https://{{dep.dependecy_repo_id.base}}/commit/{{dep.dependency_hash}}"><t t-esc="dep.dependency_hash"/></a>
|
||||||
<t t-if='dep.closest_branch_id'> from branch <t t-esc="dep.closest_branch_id.name"/></t>
|
<t t-if='dep.closest_branch_id'> from branch <t t-esc="dep.closest_branch_id.name"/></t>
|
||||||
<br/>
|
<br/>
|
||||||
@ -173,26 +144,27 @@
|
|||||||
Branch: <span id="branchclp"><t t-esc="build.branch_id.branch_name"/></span>
|
Branch: <span id="branchclp"><t t-esc="build.branch_id.branch_name"/></span>
|
||||||
<a href="#" class="clipbtn octicon octicon-clippy" data-clipboard-target="#branchclp" title="Copy branch name to clipboard"/><br/>
|
<a href="#" class="clipbtn octicon octicon-clippy" data-clipboard-target="#branchclp" title="Copy branch name to clipboard"/><br/>
|
||||||
Build host: <t t-esc="build.real_build.host"/><br/>
|
Build host: <t t-esc="build.real_build.host"/><br/>
|
||||||
|
Build dest: <t t-esc="build['dest']"/><br/>
|
||||||
</td>
|
</td>
|
||||||
<td t-if="build.real_build.children_ids">
|
<td t-if="build.real_build.children_ids">
|
||||||
Children:
|
Children:
|
||||||
<t t-if="build.real_build.nb_pending > 0"><t t-esc="build.real_build.nb_pending"/> pending </t>
|
|
||||||
<t t-if="build.real_build.nb_testing > 0"><t t-esc="build.real_build.nb_testing"/> testing </t>
|
|
||||||
<t t-if="build.real_build.nb_running > 0"><t t-esc="build.real_build.nb_running"/> running </t>
|
|
||||||
<table class="table table-condensed">
|
<table class="table table-condensed">
|
||||||
<t t-foreach="build.real_build.children_ids.sorted('id')" t-as="child">
|
<t t-foreach="build.real_build.children_ids.sorted('id')" t-as="child">
|
||||||
<t t-set="rowclass"><t t-call="runbot.build_class"><t t-set="build" t-value="child"/></t></t>
|
<t t-set="rowclass"><t t-call="runbot.build_class"><t t-set="build" t-value="child"/></t></t>
|
||||||
<tr><td t-attf-class="{{rowclass}}">
|
<tr><td t-attf-class="bg-{{rowclass.strip()}}-light">
|
||||||
<a t-attf-href="/runbot/build/{{child.id}}" >Build <t t-esc="child.id"/></a>
|
<a t-attf-href="/runbot/build/{{child.id}}" >Build <t t-esc="child.id"/></a>
|
||||||
with config <a t-attf-href="/web#id={{child.config_id.id}}&view_type=form&model=runbot.build.config"><t t-esc="child.config_id.name"/></a>
|
with config <t t-esc="child.config_id.name"/>
|
||||||
|
<a groups="runbot.group_build_config_user" t-attf-href="/web#id={{child.config_id.id}}&view_type=form&model=runbot.build.config">...</a>
|
||||||
<t t-if="child.orphan_result"><i class="fa fa-chain-broken" title="Build result ignored for parent" /></t>
|
<t t-if="child.orphan_result"><i class="fa fa-chain-broken" title="Build result ignored for parent" /></t>
|
||||||
<t t-if="child.job"> Running step: <t t-esc="child.job"/></t>
|
<t t-if="child.job"> Running step: <t t-esc="child.job"/></t>
|
||||||
<t t-if="child.global_state in ['testing', 'waiting']">
|
<t t-if="child.global_state in ['testing', 'waiting']">
|
||||||
<i class="fa fa-spinner fa-spin"/>
|
<i class="fa fa-spinner fa-spin"/>
|
||||||
<t t-esc="child.global_state"/>
|
<t t-esc="child.global_state"/>
|
||||||
</t>
|
</t>
|
||||||
<a t-if="child.real_build.local_state=='running'" t-attf-href="http://{{child.domain}}/?db={{child.real_build.dest}}-all" title="Sign in on this build" aria-label="Sign in on this build"><i class="fa fa-sign-in"/></a>
|
<t t-call="runbot.build_button">
|
||||||
<a t-if="child.real_build.local_state=='done' and child.real_build.requested_action != 'wake_up'" href="#" t-att-data-runbot-build="child.real_build.id" class="runbot-wakeup" title="Wake up this build" aria-label="Wake up this build"><i class="fa fa-coffee"/></a>
|
<t t-set="bu" t-value="child"/>
|
||||||
|
<t t-set="klass" t-value="'btn-group-ssm'"/>
|
||||||
|
</t>
|
||||||
|
|
||||||
</td></tr>
|
</td></tr>
|
||||||
</t>
|
</t>
|
||||||
@ -203,7 +175,7 @@
|
|||||||
<p t-if="build.parent_id">Child of <a t-attf-href="/runbot/build/#{build.parent_id.id}"><t t-esc="build.parent_id.dest"/></a>
|
<p t-if="build.parent_id">Child of <a t-attf-href="/runbot/build/#{build.parent_id.id}"><t t-esc="build.parent_id.dest"/></a>
|
||||||
<t t-if="build.orphan_result">&nbsp;<i class="fa fa-chain-broken" title="Build result ignored for parent" />&nbsp;Orphaned build, the result does not affect parent build result</t></p>
|
<t t-if="build.orphan_result">&nbsp;<i class="fa fa-chain-broken" title="Build result ignored for parent" />&nbsp;Orphaned build, the result does not affect parent build result</t></p>
|
||||||
<p t-if="build.duplicate_id">Duplicate of <a t-attf-href="/runbot/build/#{build.duplicate_id.id}"><t t-esc="build.duplicate_id.dest"/></a></p>
|
<p t-if="build.duplicate_id">Duplicate of <a t-attf-href="/runbot/build/#{build.duplicate_id.id}"><t t-esc="build.duplicate_id.dest"/></a></p>
|
||||||
<table class="table table-condensed table-striped">
|
<table class="table table-condensed">
|
||||||
<tr>
|
<tr>
|
||||||
<th>Date</th>
|
<th>Date</th>
|
||||||
<th>Level</th>
|
<th>Level</th>
|
||||||
@ -213,13 +185,13 @@
|
|||||||
<t t-foreach="build.real_build.sudo().log_ids" t-as="l">
|
<t t-foreach="build.real_build.sudo().log_ids" t-as="l">
|
||||||
<t t-set="subbuild" t-value="(([child for child in build.real_build.children_ids if child.id == int(l.path)] if l.type == 'subbuild' else False) or [build.browse()])[0]"/>
|
<t t-set="subbuild" t-value="(([child for child in build.real_build.children_ids if child.id == int(l.path)] if l.type == 'subbuild' else False) or [build.browse()])[0]"/>
|
||||||
<t t-set="logclass" t-value="dict(CRITICAL='danger', ERROR='danger', WARNING='warning', OK='success', SEPARATOR='separator').get(l.level)"/>
|
<t t-set="logclass" t-value="dict(CRITICAL='danger', ERROR='danger', WARNING='warning', OK='success', SEPARATOR='separator').get(l.level)"/>
|
||||||
<tr t-att-class="logclass">
|
<tr t-attf-class="bg-{{logclass}}-light">
|
||||||
<td style="white-space: nowrap; width:1%;"><t t-esc="l.create_date"/></td>
|
<td style="white-space: nowrap; width:1%;"><t t-esc="l.create_date"/></td>
|
||||||
<td style="white-space: nowrap; width:1%;"><b t-if="l.level != 'SEPARATOR' and l.type != 'link'" t-esc="l.level"/></td>
|
<td style="white-space: nowrap; width:1%;"><b t-if="l.level != 'SEPARATOR' and l.type != 'link'" t-esc="l.level"/></td>
|
||||||
<td style="white-space: nowrap; width:1%;"><t t-if="l.level != 'SEPARATOR' and l.type != 'link'" t-esc="l.type"/></td>
|
<td style="white-space: nowrap; width:1%;"><t t-if="l.level != 'SEPARATOR' and l.type != 'link'" t-esc="l.type"/></td>
|
||||||
<t t-set="message_class" t-value="''"/>
|
<t t-set="message_class" t-value="''"/>
|
||||||
<t t-if="subbuild" t-set="message_class"><t t-call="runbot.build_class"><t t-set="build" t-value="subbuild"/></t></t>
|
<t t-if="subbuild" t-set="message_class"><t t-call="runbot.build_class"><t t-set="build" t-value="subbuild"/></t></t>
|
||||||
<td t-att-class="message_class">
|
<td t-attf-class="bg-{{message_class.strip() or logclass}}-light">
|
||||||
<t t-if="l.type not in ('runbot', 'link')">
|
<t t-if="l.type not in ('runbot', 'link')">
|
||||||
<t t-if="l.type == 'subbuild'">
|
<t t-if="l.type == 'subbuild'">
|
||||||
<a t-attf-href="/runbot/build/{{l.path}}">Build #<t t-esc="l.path"/></a>
|
<a t-attf-href="/runbot/build/{{l.path}}">Build #<t t-esc="l.path"/></a>
|
||||||
@ -241,7 +213,7 @@
|
|||||||
<t t-if="l.type == 'subbuild' and subbuild.sudo().error_log_ids">
|
<t t-if="l.type == 'subbuild' and subbuild.sudo().error_log_ids">
|
||||||
<a data-toggle="collapse" t-attf-data-target="#subbuild-{{subbuild.id}}"><i class="fa"></i></a>
|
<a data-toggle="collapse" t-attf-data-target="#subbuild-{{subbuild.id}}"><i class="fa"></i></a>
|
||||||
<div t-attf-id="subbuild-{{subbuild.id}}" class="collapse in">
|
<div t-attf-id="subbuild-{{subbuild.id}}" class="collapse in">
|
||||||
<table class="table table-condensed table-striped" style="margin-bottom:0;">
|
<table class="table table-condensed" style="margin-bottom:0;">
|
||||||
<t t-foreach="subbuild.sudo().error_log_ids" t-as="sl">
|
<t t-foreach="subbuild.sudo().error_log_ids" t-as="sl">
|
||||||
<tr>
|
<tr>
|
||||||
<td t-att-class="dict(CRITICAL='danger', ERROR='danger', WARNING='warning', OK='success', SEPARATOR='separator').get(sl.level)">
|
<td t-att-class="dict(CRITICAL='danger', ERROR='danger', WARNING='warning', OK='success', SEPARATOR='separator').get(sl.level)">
|
||||||
@ -262,7 +234,6 @@
|
|||||||
</t>
|
</t>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</t>
|
</t>
|
||||||
</template>
|
</template>
|
||||||
|
@ -141,21 +141,7 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
<div>
|
<div>
|
||||||
<span t-attf-class="label label-{{pending_level}}">Pending: <t t-esc="pending_total"/></span>
|
<t t-call="slots_infos"/>
|
||||||
<t t-set="testing">0</t>
|
|
||||||
<t t-set="workers">0</t>
|
|
||||||
|
|
||||||
<t t-foreach="hosts_data.sorted(key=lambda h:h.name)" t-as="host">
|
|
||||||
<t t-set="testing" t-value="int(testing) + host.nb_testing"/>
|
|
||||||
<t t-set="workers" t-value="int(workers) + host.sudo().get_nb_worker()"/>
|
|
||||||
</t>
|
|
||||||
<t t-set="klass">success</t>
|
|
||||||
|
|
||||||
<t t-if="int(testing)/workers > 0"><t t-set="klass">info</t></t>
|
|
||||||
<t t-if="int(testing)/workers > 0.75"><t t-set="klass">warning</t></t>
|
|
||||||
<t t-if="int(testing)/workers >= 1"><t t-set="klass">danger</t></t>
|
|
||||||
|
|
||||||
<span t-attf-class="label label-{{klass}}">Testing: <t t-esc="testing"/>/<t t-esc="workers"/></span>
|
|
||||||
</div>
|
</div>
|
||||||
<t t-foreach="glances_data.keys()" t-as="repo">
|
<t t-foreach="glances_data.keys()" t-as="repo">
|
||||||
<div>
|
<div>
|
||||||
@ -178,9 +164,9 @@
|
|||||||
<t t-if="host.nb_testing > host.sudo().get_nb_worker()"><t t-set="klass">danger</t></t>
|
<t t-if="host.nb_testing > host.sudo().get_nb_worker()"><t t-set="klass">danger</t></t>
|
||||||
<span t-attf-class="label label-{{klass}}"><span t-esc="host.nb_testing"/>/<span t-esc="host.sudo().get_nb_worker()"/></span>
|
<span t-attf-class="label label-{{klass}}"><span t-esc="host.nb_testing"/>/<span t-esc="host.sudo().get_nb_worker()"/></span>
|
||||||
<t t-esc="host.nb_running"/>
|
<t t-esc="host.nb_running"/>
|
||||||
<t t-set="succes_time" t-value="int(datetime.datetime.now().timestamp() - datetime.datetime.strptime(host.last_success, '%Y-%m-%d %H:%M:%S').timestamp())"/>
|
<t t-set="succes_time" t-value="int(datetime.datetime.now().timestamp() - host.last_success.timestamp())"/>
|
||||||
<t t-set="start_time" t-value="int(datetime.datetime.now().timestamp() - datetime.datetime.strptime(host.last_start_loop, '%Y-%m-%d %H:%M:%S').timestamp())"/>
|
<t t-set="start_time" t-value="int(datetime.datetime.now().timestamp() - host.last_start_loop.timestamp())"/>
|
||||||
<t t-set="end_time" t-value="int(datetime.datetime.now().timestamp() - datetime.datetime.strptime(host.last_end_loop, '%Y-%m-%d %H:%M:%S').timestamp())"/>
|
<t t-set="end_time" t-value="int(datetime.datetime.now().timestamp() - host.last_end_loop.timestamp())"/>
|
||||||
|
|
||||||
<t t-set="klass">success</t>
|
<t t-set="klass">success</t>
|
||||||
<t t-if="succes_time > 30"><t t-set="klass">info</t></t>
|
<t t-if="succes_time > 30"><t t-set="klass">info</t></t>
|
||||||
|
@ -2,13 +2,48 @@
|
|||||||
<odoo>
|
<odoo>
|
||||||
<data>
|
<data>
|
||||||
<!-- Replace default menu ( Home / Contactus and co...) with 5 first repos) -->
|
<!-- Replace default menu ( Home / Contactus and co...) with 5 first repos) -->
|
||||||
<template id="inherits_branch_in_menu" inherit_id="website.layout" name="Inherits Show top 5 branches in menu">
|
<template id="inherits_branch_in_menu" inherit_id="website.layout" name="Inherits Show top 6 repo in menu and dropdown">
|
||||||
<xpath expr="//t[@t-foreach="website.menu_id.child_id"][@t-as="submenu"]" position="replace">
|
<xpath expr="//footer" position="replace">
|
||||||
<t t-if="repos" >
|
</xpath>
|
||||||
<t t-foreach="repos[:5]" t-as="re">
|
<xpath expr="//nav" position="replace">
|
||||||
<li><a t-attf-href="/runbot/repo/{{slug(re)}}?search={{request.params.get('search', '')}}"><i class='fa fa-github' /> <t t-esc="re.short_name"/></a></li>
|
<nav class="navbar navbar-expand-md navbar-light bg-light">
|
||||||
</t>
|
<a t-if="repo" t-attf-href="/runbot/repo/{{slug(repo)}}?search={{request.params.get('search', '')}}">
|
||||||
</t>
|
<b style="color:#777;"><t t-esc="repo.short_name"/></b>
|
||||||
|
</a>
|
||||||
|
<button type="button" class="navbar-toggler" data-toggle="collapse" data-target="#top_menu_collapse">
|
||||||
|
<span class="navbar-toggler-icon"/>
|
||||||
|
</button>
|
||||||
|
<div class="collapse navbar-collapse" id="top_menu_collapse">
|
||||||
|
<ul class="nav navbar-nav ml-auto text-right" id="top_menu">
|
||||||
|
<t t-if="repos" >
|
||||||
|
<t t-foreach="repos[:6]" t-as="re">
|
||||||
|
<li ><a t-attf-href="/runbot/repo/{{slug(re)}}?search={{request.params.get('search', '')}}"><i class='fa fa-github' /> <t t-esc="re.short_name"/></a></li>
|
||||||
|
</t>
|
||||||
|
<li t-if="len(repos)>6" class="dropdown">
|
||||||
|
<a href="#" class="dropdown-toggle" data-toggle="dropdown" aria-expanded="false"><i class="fa fa-plus"/></a>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
<t t-foreach='repos[6:]' t-as='re'>
|
||||||
|
<li><a t-attf-href="/runbot/repo/{{slug(re)}}"><t t-esc="re.short_name"/></a></li>
|
||||||
|
</t>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
</t>
|
||||||
|
<li class="nav-item divider" t-ignore="true" t-if="not user_id._is_public()"/>
|
||||||
|
<li class="nav-item dropdown" t-ignore="true" t-if="not user_id._is_public()">
|
||||||
|
<a href="#" class="nav-link dropdown-toggle" data-toggle="dropdown">
|
||||||
|
<b>
|
||||||
|
<span t-esc="user_id.name[:23] + '...' if user_id.name and len(user_id.name) > 25 else user_id.name"/>
|
||||||
|
</b>
|
||||||
|
</a>
|
||||||
|
<div class="dropdown-menu js_usermenu" role="menu">
|
||||||
|
<a id="o_logout" class="dropdown-item" t-attf-href="/web/session/logout?redirect=/" role="menuitem">Logout</a>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<t t-raw="nav_form or ''">
|
||||||
|
</t>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
</xpath>
|
</xpath>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -21,6 +56,18 @@
|
|||||||
<attribute name="t-value">'o_connected_user' if env['ir.ui.view'].user_has_groups('base.group_website_publisher') else None</attribute>
|
<attribute name="t-value">'o_connected_user' if env['ir.ui.view'].user_has_groups('base.group_website_publisher') else None</attribute>
|
||||||
</xpath>
|
</xpath>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<template id="runbot.slots_infos" name="Hosts slot nb pending/testing/slots">
|
||||||
|
<span t-attf-class="label label-{{pending_level}}">Pending: <t t-esc="pending_total"/></span>
|
||||||
|
<t t-set="testing" t-value="hosts_data._total_testing()"/>
|
||||||
|
<t t-set="workers" t-value="hosts_data._total_workers()"/>
|
||||||
|
<t t-set="klass">success</t>
|
||||||
|
<t t-if="int(testing)/workers > 0"><t t-set="klass">info</t></t>
|
||||||
|
<t t-if="int(testing)/workers > 0.75"><t t-set="klass">warning</t></t>
|
||||||
|
<t t-if="int(testing)/workers >= 1"><t t-set="klass">danger</t></t>
|
||||||
|
<span t-attf-class="label label-{{klass}}">Testing: <t t-esc="testing"/>/<t t-esc="workers"/></span>
|
||||||
|
</template>
|
||||||
|
|
||||||
<!-- Frontend repository block -->
|
<!-- Frontend repository block -->
|
||||||
<template id="runbot.repo">
|
<template id="runbot.repo">
|
||||||
<t t-call='website.layout'>
|
<t t-call='website.layout'>
|
||||||
@ -28,58 +75,20 @@
|
|||||||
<t t-if="refresh">
|
<t t-if="refresh">
|
||||||
<meta http-equiv="refresh" t-att-content="refresh"/>
|
<meta http-equiv="refresh" t-att-content="refresh"/>
|
||||||
</t>
|
</t>
|
||||||
<style>
|
</t>
|
||||||
.killed {
|
<t t-set="nav_form">
|
||||||
background-color: #aaa;
|
<form class="form-inline my-2 my-lg-0" role="search" t-att-action="qu(search='')" method="get">
|
||||||
}
|
<div class="input-group md-form form-sm form-2 pl-0">
|
||||||
</style>
|
<input class="form-control my-0 py-1 red-border" type="text" placeholder="Search" aria-label="Search" name="search" t-att-value="search"/>
|
||||||
|
<div class="input-group-append">
|
||||||
|
<button type='submit' class="input-group-text red lighten-3" id="basic-text1"><i class="fa fa-search text-grey"></i></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
</t>
|
</t>
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class='col-md-12'>
|
<div class='col-md-12'>
|
||||||
<nav class="navbar navbar-default" role="navigation">
|
|
||||||
<div class="container-fluid">
|
|
||||||
<div class="navbar-header">
|
|
||||||
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1">
|
|
||||||
<span class="sr-only">Toggle navigation</span>
|
|
||||||
<span class="icon-bar"></span>
|
|
||||||
<span class="icon-bar"></span>
|
|
||||||
<span class="icon-bar"></span>
|
|
||||||
</button>
|
|
||||||
<t t-if="repo">
|
|
||||||
<ul class="nav navbar-nav">
|
|
||||||
<li class="dropdown">
|
|
||||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown" aria-expanded="false"><b style="font-size: 18px;"><t t-esc="repo.base"/></b><b class="caret"></b></a>
|
|
||||||
<ul class="dropdown-menu">
|
|
||||||
<t t-foreach='repos' t-as='re'>
|
|
||||||
<li><a t-attf-href="/runbot/repo/{{slug(re)}}"><t t-esc="re.base"/></a></li>
|
|
||||||
</t>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
</t>
|
|
||||||
</div>
|
|
||||||
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
|
|
||||||
<t t-if="repo">
|
|
||||||
<form class="navbar-form navbar-right" role="search" t-att-action="qu(search='')" method="get">
|
|
||||||
<div class="form-group">
|
|
||||||
<input type="search" name="search" class="form-control" placeholder="Search" t-att-value="search"/>
|
|
||||||
<button type="submit" class="btn btn-default">Search</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</t>
|
|
||||||
</div>
|
|
||||||
<p class="text-center">
|
|
||||||
<t t-foreach="host_stats" t-as="hs">
|
|
||||||
<span class="label label-default">
|
|
||||||
<t t-esc="hs['host']"/>: <t t-esc="hs['testing']"/> testing
|
|
||||||
</span>&nbsp;
|
|
||||||
</t>
|
|
||||||
<span t-attf-class="label label-{{pending_level}}">Pending: <t t-esc="pending_total"/></span>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
<div t-if="message" class="alert alert-warning" role="alert">
|
<div t-if="message" class="alert alert-warning" role="alert">
|
||||||
<t t-esc="message" />
|
<t t-esc="message" />
|
||||||
</div>
|
</div>
|
||||||
@ -90,21 +99,16 @@
|
|||||||
<table t-if="repo" class="table table-condensed table-bordered" style="table-layout: initial;">
|
<table t-if="repo" class="table table-condensed table-bordered" style="table-layout: initial;">
|
||||||
<tr>
|
<tr>
|
||||||
<th>Branch</th>
|
<th>Branch</th>
|
||||||
<td colspan="4" class="text-right">
|
<td colspan="4">
|
||||||
<t t-esc="repo.base"/>:
|
<span class="pull-right" t-call="runbot.slots_infos"/>
|
||||||
<t t-esc="testing"/> testing,
|
|
||||||
<t t-esc="running"/> running,
|
|
||||||
<t t-esc="pending"/> pending.
|
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
</tr>
|
</tr>
|
||||||
<tr t-foreach="branches" t-as="br">
|
<tr t-foreach="branches" t-as="br">
|
||||||
<td>
|
<td style="width:12%">
|
||||||
<i t-if="br['branch'].sticky" class="fa fa-star" style="color: #f0ad4e" />
|
<small class="branch_time" ><t t-esc="br['builds'] and br['builds'][0].get_formated_build_time()"/></small>
|
||||||
<a t-attf-href="/runbot/branch/#{br['branch'].id}"><b t-esc="br['branch'].branch_name"/></a>
|
<div class="branch_name"><i t-if="br['branch'].sticky" class="fa fa-star" style="color: #f0ad4e" /><a t-attf-href="/runbot/branch/#{br['branch'].id}"><b t-esc="br['branch'].branch_name"/></a></div>
|
||||||
<small><t t-esc="br['builds'] and br['builds'][0].get_formated_build_time()"/></small><br/>
|
<div class="btn-group btn-group-xs">
|
||||||
<div class="btn-group btn-group-xs">
|
<a t-attf-href="{{br['branch'].branch_url}}" class="btn btn-default btn-xs"><t t-esc="'Branch ' if not br['branch'].pull_head_name else 'Pull '"/><i class="fa fa-github"/></a>
|
||||||
<a t-attf-href="{{br['branch'].branch_url}}" class="btn btn-default btn-xs">Branch or pull <i class="fa fa-github"/></a>
|
|
||||||
<a t-attf-href="/runbot/quick_connect/#{br['branch'].id}" class="btn btn-default btn-xs" aria-label="Quick Connect"><i class="fa fa-fast-forward" title="Quick Connect"/></a>
|
<a t-attf-href="/runbot/quick_connect/#{br['branch'].id}" class="btn btn-default btn-xs" aria-label="Quick Connect"><i class="fa fa-fast-forward" title="Quick Connect"/></a>
|
||||||
</div>
|
</div>
|
||||||
<t t-if="br['branch'].sticky">
|
<t t-if="br['branch'].sticky">
|
||||||
@ -134,20 +138,21 @@
|
|||||||
<t t-if="bu.global_result == 'skipped'"><t t-set="klass">default</t></t>
|
<t t-if="bu.global_result == 'skipped'"><t t-set="klass">default</t></t>
|
||||||
<t t-if="bu.global_result in ['killed', 'manually_killed']"><t t-set="klass">killed</t></t>
|
<t t-if="bu.global_result in ['killed', 'manually_killed']"><t t-set="klass">killed</t></t>
|
||||||
</t>
|
</t>
|
||||||
<td t-attf-class="{{klass}}">
|
<td t-attf-class="bg-{{klass}}-light" style="width:22%">
|
||||||
<t t-call="runbot.build_button">
|
<t t-call="runbot.build_button">
|
||||||
<t t-set="klass">btn-group-sm</t>
|
<t t-set="klass">btn-group-sm</t>
|
||||||
<t t-set="show_rebuild_button" t-value="bu==br['builds'][0]"></t>
|
<t t-set="show_rebuild_button" t-value="bu==br['builds'][0]"></t>
|
||||||
|
<t t-set="show_commit_button" t-value="True"/>
|
||||||
</t>
|
</t>
|
||||||
<t t-if="bu['build_type']=='scheduled'"><i class="fa fa-moon-o" t-att-title="bu.build_type_label()" t-att-aria-label="bu.build_type_label()"/></t>
|
<t t-if="bu['build_type']=='scheduled'"><i class="fa fa-moon-o" t-att-title="bu.build_type_label()" t-att-aria-label="bu.build_type_label()"/></t>
|
||||||
<t t-if="bu['build_type'] in ('rebuild', 'indirect')"><i class="fa fa-recycle" t-att-title="bu.build_type_label()" t-att-aria-label="bu.build_type_label()"/></t>
|
<t t-if="bu['build_type'] in ('rebuild', 'indirect')"><i class="fa fa-recycle" t-att-title="bu.build_type_label()" t-att-aria-label="bu.build_type_label()"/></t>
|
||||||
<t t-if="bu['subject']">
|
<span t-if="bu['subject']" class="build_subject">
|
||||||
<t t-if="bu.config_id != bu.branch_id.config_id">
|
<t t-if="bu.config_id != bu.branch_id.config_id">
|
||||||
<b t-esc="bu.config_id.name"/>
|
<b t-esc="bu.config_id.name"/>
|
||||||
</t>
|
</t>
|
||||||
<span t-esc="bu['subject'][:32] + ('...' if bu['subject'][32:] else '') " t-att-title="bu['subject']"/>
|
<span t-esc="bu['subject'][:32] + ('...' if bu['subject'][32:] else '') " t-att-title="bu['subject']"/>
|
||||||
<br/>
|
<br/>
|
||||||
</t>
|
</span>
|
||||||
<t t-id="bu['author']">
|
<t t-id="bu['author']">
|
||||||
<t t-esc="bu['author']"/>
|
<t t-esc="bu['author']"/>
|
||||||
<t t-if="bu['committer'] and bu['author'] != bu['committer']" t-id="bu['committer']">
|
<t t-if="bu['committer'] and bu['author'] != bu['committer']" t-id="bu['committer']">
|
||||||
|
@ -39,6 +39,8 @@ class RunbotCase(TransactionCase):
|
|||||||
self.start_patcher('docker_build', 'odoo.addons.runbot.models.build.docker_build')
|
self.start_patcher('docker_build', 'odoo.addons.runbot.models.build.docker_build')
|
||||||
self.start_patcher('docker_ps', 'odoo.addons.runbot.models.repo.docker_ps', [])
|
self.start_patcher('docker_ps', 'odoo.addons.runbot.models.repo.docker_ps', [])
|
||||||
self.start_patcher('docker_stop', 'odoo.addons.runbot.models.repo.docker_stop')
|
self.start_patcher('docker_stop', 'odoo.addons.runbot.models.repo.docker_stop')
|
||||||
|
self.start_patcher('cr_commit', 'odoo.sql_db.Cursor.commit', None)
|
||||||
|
self.start_patcher('repo_commit', 'odoo.addons.runbot.models.repo.runbot_repo._commit', None)
|
||||||
|
|
||||||
def start_patcher(self, patcher_name, patcher_path, return_value=Dummy, side_effect=Dummy):
|
def start_patcher(self, patcher_name, patcher_path, return_value=Dummy, side_effect=Dummy):
|
||||||
patcher = patch(patcher_path)
|
patcher = patch(patcher_path)
|
||||||
|
@ -120,7 +120,7 @@ class TestBranchRelations(RunbotCase):
|
|||||||
self.assertEqual(b.previous_version.branch_name, '13.0')
|
self.assertEqual(b.previous_version.branch_name, '13.0')
|
||||||
self.assertEqual(sorted(b.intermediate_stickies.mapped('branch_name')), ['saas-13.1', 'saas-13.2'])
|
self.assertEqual(sorted(b.intermediate_stickies.mapped('branch_name')), ['saas-13.1', 'saas-13.2'])
|
||||||
|
|
||||||
b.closest_sticky = self.last
|
b.defined_sticky = self.last
|
||||||
|
|
||||||
self.assertEqual(b.closest_sticky.branch_name, 'saas-13.2')
|
self.assertEqual(b.closest_sticky.branch_name, 'saas-13.2')
|
||||||
self.assertEqual(b.previous_version.branch_name, '13.0')
|
self.assertEqual(b.previous_version.branch_name, '13.0')
|
||||||
|
@ -327,68 +327,66 @@ class Test_Build(RunbotCase):
|
|||||||
'extra_params': '5',
|
'extra_params': '5',
|
||||||
})
|
})
|
||||||
|
|
||||||
def assert_state(nb_pending, nb_testing, nb_running, global_state, build):
|
def assert_state(global_state, build):
|
||||||
self.assertEqual(build.nb_pending, nb_pending)
|
|
||||||
self.assertEqual(build.nb_testing, nb_testing)
|
|
||||||
self.assertEqual(build.nb_running, nb_running)
|
|
||||||
self.assertEqual(build.global_state, global_state)
|
self.assertEqual(build.global_state, global_state)
|
||||||
|
|
||||||
assert_state(5, 0, 0, 'pending', build1)
|
assert_state('pending', build1)
|
||||||
assert_state(3, 0, 0, 'pending', build1_1)
|
assert_state('pending', build1_1)
|
||||||
assert_state(1, 0, 0, 'pending', build1_2)
|
assert_state('pending', build1_2)
|
||||||
assert_state(1, 0, 0, 'pending', build1_1_1)
|
assert_state('pending', build1_1_1)
|
||||||
assert_state(1, 0, 0, 'pending', build1_1_2)
|
assert_state('pending', build1_1_2)
|
||||||
|
|
||||||
build1.local_state = 'testing'
|
build1.local_state = 'testing'
|
||||||
build1_1.local_state = 'testing'
|
build1_1.local_state = 'testing'
|
||||||
build1.local_state = 'done'
|
build1.local_state = 'done'
|
||||||
build1_1.local_state = 'done'
|
build1_1.local_state = 'done'
|
||||||
|
|
||||||
assert_state(3, 0, 0, 'waiting', build1)
|
assert_state('waiting', build1)
|
||||||
assert_state(2, 0, 0, 'waiting', build1_1)
|
assert_state('waiting', build1_1)
|
||||||
assert_state(1, 0, 0, 'pending', build1_2)
|
assert_state('pending', build1_2)
|
||||||
assert_state(1, 0, 0, 'pending', build1_1_1)
|
assert_state('pending', build1_1_1)
|
||||||
assert_state(1, 0, 0, 'pending', build1_1_2)
|
assert_state('pending', build1_1_2)
|
||||||
|
|
||||||
build1_1_1.local_state = 'testing'
|
build1_1_1.local_state = 'testing'
|
||||||
|
|
||||||
assert_state(2, 1, 0, 'waiting', build1)
|
assert_state('waiting', build1)
|
||||||
assert_state(1, 1, 0, 'waiting', build1_1)
|
assert_state('waiting', build1_1)
|
||||||
assert_state(1, 0, 0, 'pending', build1_2)
|
assert_state('pending', build1_2)
|
||||||
assert_state(0, 1, 0, 'testing', build1_1_1)
|
assert_state('testing', build1_1_1)
|
||||||
assert_state(1, 0, 0, 'pending', build1_1_2)
|
assert_state('pending', build1_1_2)
|
||||||
|
|
||||||
build1_2.local_state = 'testing'
|
build1_2.local_state = 'testing'
|
||||||
|
|
||||||
assert_state(1, 2, 0, 'waiting', build1)
|
assert_state('waiting', build1)
|
||||||
assert_state(1, 1, 0, 'waiting', build1_1)
|
assert_state('waiting', build1_1)
|
||||||
assert_state(0, 1, 0, 'testing', build1_2)
|
assert_state('testing', build1_2)
|
||||||
assert_state(0, 1, 0, 'testing', build1_1_1)
|
assert_state('testing', build1_1_1)
|
||||||
assert_state(1, 0, 0, 'pending', build1_1_2)
|
assert_state('pending', build1_1_2)
|
||||||
|
|
||||||
build1_2.local_state = 'testing' # writing same state a second time
|
build1_2.local_state = 'testing' # writing same state a second time
|
||||||
|
|
||||||
assert_state(1, 2, 0, 'waiting', build1)
|
assert_state('waiting', build1)
|
||||||
assert_state(1, 1, 0, 'waiting', build1_1)
|
assert_state('waiting', build1_1)
|
||||||
assert_state(0, 1, 0, 'testing', build1_2)
|
assert_state('testing', build1_2)
|
||||||
assert_state(0, 1, 0, 'testing', build1_1_1)
|
assert_state('testing', build1_1_1)
|
||||||
assert_state(1, 0, 0, 'pending', build1_1_2)
|
assert_state('pending', build1_1_2)
|
||||||
|
|
||||||
build1_1_2.local_state = 'done'
|
build1_1_2.local_state = 'done'
|
||||||
build1_1_1.local_state = 'done'
|
build1_1_1.local_state = 'done'
|
||||||
build1_2.local_state = 'done'
|
build1_2.local_state = 'done'
|
||||||
|
|
||||||
assert_state(0, 0, 0, 'done', build1)
|
assert_state('done', build1)
|
||||||
assert_state(0, 0, 0, 'done', build1_1)
|
assert_state('done', build1_1)
|
||||||
assert_state(0, 0, 0, 'done', build1_2)
|
assert_state('done', build1_2)
|
||||||
assert_state(0, 0, 0, 'done', build1_1_1)
|
assert_state('done', build1_1_1)
|
||||||
assert_state(0, 0, 0, 'done', build1_1_2)
|
assert_state('done', build1_1_2)
|
||||||
|
|
||||||
def test_duplicate_childrens(self):
|
def test_duplicate_childrens(self):
|
||||||
build_old = self.create_build({
|
build_old = self.create_build({
|
||||||
'branch_id': self.branch_10.id,
|
'branch_id': self.branch_10.id,
|
||||||
'name': 'd0d0caca0000ffffffffffffffffffffffffffff',
|
'name': 'd0d0caca0000ffffffffffffffffffffffffffff',
|
||||||
'extra_params': '0',
|
'extra_params': '0',
|
||||||
|
'local_state': 'done'
|
||||||
})
|
})
|
||||||
build_parent = self.create_build({
|
build_parent = self.create_build({
|
||||||
'branch_id': self.branch_10.id,
|
'branch_id': self.branch_10.id,
|
||||||
@ -404,8 +402,7 @@ class Test_Build(RunbotCase):
|
|||||||
build_parent.local_state = 'done'
|
build_parent.local_state = 'done'
|
||||||
self.assertEqual(build_child.local_state, 'duplicate')
|
self.assertEqual(build_child.local_state, 'duplicate')
|
||||||
self.assertEqual(build_child.duplicate_id, build_old)
|
self.assertEqual(build_child.duplicate_id, build_old)
|
||||||
self.assertEqual(build_parent.nb_pending, 0)
|
self.assertEqual(build_child.global_state, 'done')
|
||||||
self.assertEqual(build_parent.nb_testing, 0)
|
|
||||||
self.assertEqual(build_parent.global_state, 'done')
|
self.assertEqual(build_parent.global_state, 'done')
|
||||||
|
|
||||||
|
|
||||||
@ -734,6 +731,10 @@ class TestClosestBranch(RunbotCase):
|
|||||||
'repo_id': self.community_repo.id,
|
'repo_id': self.community_repo.id,
|
||||||
'name': 'refs/pull/123456'
|
'name': 'refs/pull/123456'
|
||||||
})
|
})
|
||||||
|
|
||||||
|
# trigger compute and ensure that mock_github is used. (using correct side effect would work too)
|
||||||
|
self.assertEqual(server_pr.pull_head_name, 'foo-dev:bar_branch')
|
||||||
|
|
||||||
mock_github.return_value = {
|
mock_github.return_value = {
|
||||||
'head': {'label': 'foo-dev:foobar_branch'},
|
'head': {'label': 'foo-dev:foobar_branch'},
|
||||||
'base': {'ref': '10.0'},
|
'base': {'ref': '10.0'},
|
||||||
@ -743,6 +744,8 @@ class TestClosestBranch(RunbotCase):
|
|||||||
'repo_id': self.enterprise_repo.id,
|
'repo_id': self.enterprise_repo.id,
|
||||||
'name': 'refs/pull/789101'
|
'name': 'refs/pull/789101'
|
||||||
})
|
})
|
||||||
|
self.assertEqual(addons_pr.pull_head_name, 'foo-dev:foobar_branch')
|
||||||
|
closest = addons_pr._get_closest_branch(self.community_repo.id)
|
||||||
self.assertEqual((self.branch_odoo_10, 'pr_target'), addons_pr._get_closest_branch(self.community_repo.id))
|
self.assertEqual((self.branch_odoo_10, 'pr_target'), addons_pr._get_closest_branch(self.community_repo.id))
|
||||||
|
|
||||||
def test_closest_branch_05_master(self):
|
def test_closest_branch_05_master(self):
|
||||||
|
@ -92,6 +92,7 @@ class TestBuildError(RunbotCase):
|
|||||||
self.assertIn(ko_build_new, new_build_error.build_ids, 'The parsed build with a re-apearing error should generate a new runbot.build.error')
|
self.assertIn(ko_build_new, new_build_error.build_ids, 'The parsed build with a re-apearing error should generate a new runbot.build.error')
|
||||||
self.assertIn(build_error, new_build_error.error_history_ids, 'The old error should appear in history')
|
self.assertIn(build_error, new_build_error.error_history_ids, 'The old error should appear in history')
|
||||||
|
|
||||||
|
|
||||||
def test_build_error_links(self):
|
def test_build_error_links(self):
|
||||||
build_a = self.create_test_build({'local_result': 'ko'})
|
build_a = self.create_test_build({'local_result': 'ko'})
|
||||||
build_b = self.create_test_build({'local_result': 'ko'})
|
build_b = self.create_test_build({'local_result': 'ko'})
|
||||||
@ -111,8 +112,7 @@ class TestBuildError(RunbotCase):
|
|||||||
# test that the random bug is parent when linking errors
|
# test that the random bug is parent when linking errors
|
||||||
all_errors = error_a | error_b
|
all_errors = error_a | error_b
|
||||||
all_errors.link_errors()
|
all_errors.link_errors()
|
||||||
|
self.assertEqual(error_b.child_ids, error_a, 'Random error should be the parent')
|
||||||
self.assertIn(error_b.child_ids, error_a, 'Random error should be the parent')
|
|
||||||
|
|
||||||
# Test that changing bug resolution is propagated to children
|
# Test that changing bug resolution is propagated to children
|
||||||
error_b.active = True
|
error_b.active = True
|
||||||
|
@ -53,9 +53,6 @@ class Test_Frontend(RunbotCase):
|
|||||||
self.assertEqual(self.sticky_branch, context['branches'][0]['branch'], "The sticky branch should be in first place")
|
self.assertEqual(self.sticky_branch, context['branches'][0]['branch'], "The sticky branch should be in first place")
|
||||||
self.assertEqual(self.branch, context['branches'][1]['branch'], "The non sticky branch should be in second place")
|
self.assertEqual(self.branch, context['branches'][1]['branch'], "The non sticky branch should be in second place")
|
||||||
self.assertEqual(len(context['branches'][0]['builds']), 4, "Only the 4 last builds should appear in the context")
|
self.assertEqual(len(context['branches'][0]['builds']), 4, "Only the 4 last builds should appear in the context")
|
||||||
self.assertEqual(context['pending'], 2, "There should be 2 pending builds")
|
|
||||||
self.assertEqual(context['running'], 2, "There should be 2 running builds")
|
|
||||||
self.assertEqual(context['testing'], 2, "There should be 2 testing builds")
|
|
||||||
self.assertEqual(context['pending_total'], 2, "There should be 2 pending builds")
|
self.assertEqual(context['pending_total'], 2, "There should be 2 pending builds")
|
||||||
self.assertEqual(context['pending_level'], 'info', "The pending level should be info")
|
self.assertEqual(context['pending_level'], 'info', "The pending level should be info")
|
||||||
return Response()
|
return Response()
|
||||||
|
@ -176,20 +176,22 @@ class Test_Repo(RunbotCase):
|
|||||||
|
|
||||||
_logger.info('Create pending builds took: %ssec', (time.time() - inserted_time))
|
_logger.info('Create pending builds took: %ssec', (time.time() - inserted_time))
|
||||||
|
|
||||||
|
|
||||||
|
@common.warmup
|
||||||
def test_times(self):
|
def test_times(self):
|
||||||
def _test_times(model, field_name):
|
def _test_times(model, setter, field_name):
|
||||||
repo1 = self.Repo.create({'name': 'bla@example.com:foo/bar'})
|
repo1 = self.Repo.create({'name': 'bla@example.com:foo/bar'})
|
||||||
repo2 = self.Repo.create({'name': 'bla@example.com:foo2/bar2'})
|
repo2 = self.Repo.create({'name': 'bla@example.com:foo2/bar2'})
|
||||||
count = self.cr.sql_log_count
|
count = self.cr.sql_log_count
|
||||||
repo1[field_name] = 1.1
|
with self.assertQueryCount(1):
|
||||||
self.assertEqual(self.cr.sql_log_count - count, 1, "Only one insert should have been triggered")
|
getattr(repo1, setter)(1.1)
|
||||||
repo2[field_name] = 1.2
|
getattr(repo2, setter)(1.2)
|
||||||
self.assertEqual(len(self.env[model].search([])), 2)
|
self.assertEqual(len(self.env[model].search([])), 2)
|
||||||
self.assertEqual(repo1[field_name], 1.1)
|
self.assertEqual(repo1[field_name], 1.1)
|
||||||
self.assertEqual(repo2[field_name], 1.2)
|
self.assertEqual(repo2[field_name], 1.2)
|
||||||
|
|
||||||
repo1[field_name] = 1.3
|
getattr(repo1, setter)(1.3)
|
||||||
repo2[field_name] = 1.4
|
getattr(repo2, setter)(1.4)
|
||||||
|
|
||||||
self.assertEqual(len(self.env[model].search([])), 4)
|
self.assertEqual(len(self.env[model].search([])), 4)
|
||||||
self.assertEqual(repo1[field_name], 1.3)
|
self.assertEqual(repo1[field_name], 1.3)
|
||||||
@ -205,8 +207,8 @@ class Test_Repo(RunbotCase):
|
|||||||
self.assertEqual(repo1[field_name], 1.3)
|
self.assertEqual(repo1[field_name], 1.3)
|
||||||
self.assertEqual(repo2[field_name], 1.4)
|
self.assertEqual(repo2[field_name], 1.4)
|
||||||
|
|
||||||
_test_times('runbot.repo.hooktime', 'hook_time')
|
_test_times('runbot.repo.hooktime', 'set_hook_time', 'hook_time')
|
||||||
_test_times('runbot.repo.reftime', 'get_ref_time')
|
_test_times('runbot.repo.reftime', 'set_ref_time', 'get_ref_time')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -239,11 +241,9 @@ class Test_Github(TransactionCase):
|
|||||||
|
|
||||||
class Test_Repo_Scheduler(RunbotCase):
|
class Test_Repo_Scheduler(RunbotCase):
|
||||||
|
|
||||||
def setUp(self ):
|
def setUp(self):
|
||||||
# as the _scheduler method commits, we need to protect the database
|
# as the _scheduler method commits, we need to protect the database
|
||||||
registry = odoo.registry()
|
registry = odoo.registry()
|
||||||
registry.enter_test_mode()
|
|
||||||
self.addCleanup(registry.leave_test_mode)
|
|
||||||
super(Test_Repo_Scheduler, self).setUp()
|
super(Test_Repo_Scheduler, self).setUp()
|
||||||
|
|
||||||
self.fqdn_patcher = patch('odoo.addons.runbot.models.host.fqdn')
|
self.fqdn_patcher = patch('odoo.addons.runbot.models.host.fqdn')
|
||||||
@ -261,6 +261,7 @@ class Test_Repo_Scheduler(RunbotCase):
|
|||||||
@patch('odoo.addons.runbot.models.build.runbot_build._schedule')
|
@patch('odoo.addons.runbot.models.build.runbot_build._schedule')
|
||||||
@patch('odoo.addons.runbot.models.build.runbot_build._init_pendings')
|
@patch('odoo.addons.runbot.models.build.runbot_build._init_pendings')
|
||||||
def test_repo_scheduler(self, mock_init_pendings, mock_schedule, mock_kill):
|
def test_repo_scheduler(self, mock_init_pendings, mock_schedule, mock_kill):
|
||||||
|
|
||||||
self.env['ir.config_parameter'].set_param('runbot.runbot_workers', 6)
|
self.env['ir.config_parameter'].set_param('runbot.runbot_workers', 6)
|
||||||
builds = []
|
builds = []
|
||||||
# create 6 builds that are testing on the host to verify that
|
# create 6 builds that are testing on the host to verify that
|
||||||
@ -305,7 +306,3 @@ class Test_Repo_Scheduler(RunbotCase):
|
|||||||
self.Build.search([('name', '=', 'a')]).write({'local_state': 'done'})
|
self.Build.search([('name', '=', 'a')]).write({'local_state': 'done'})
|
||||||
|
|
||||||
self.foo_repo._scheduler(host)
|
self.foo_repo._scheduler(host)
|
||||||
build.invalidate_cache()
|
|
||||||
scheduled_build.invalidate_cache()
|
|
||||||
self.assertEqual(build.host, 'host.runbot.com')
|
|
||||||
self.assertFalse(scheduled_build.host)
|
|
||||||
|
@ -11,8 +11,6 @@ class TestSchedule(RunbotCase):
|
|||||||
def setUp(self):
|
def setUp(self):
|
||||||
# entering test mode to avoid that the _schedule method commits records
|
# entering test mode to avoid that the _schedule method commits records
|
||||||
registry = odoo.registry()
|
registry = odoo.registry()
|
||||||
registry.enter_test_mode()
|
|
||||||
self.addCleanup(registry.leave_test_mode)
|
|
||||||
super(TestSchedule, self).setUp()
|
super(TestSchedule, self).setUp()
|
||||||
|
|
||||||
self.repo = self.Repo.create({'name': 'bla@example.com:foo/bar'})
|
self.repo = self.Repo.create({'name': 'bla@example.com:foo/bar'})
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
<data>
|
<data>
|
||||||
<template id="assets_front_end" inherit_id="web.assets_frontend" name="runbot assets">
|
<template id="assets_front_end" inherit_id="web.assets_frontend" name="runbot assets">
|
||||||
<xpath expr="." position="inside">
|
<xpath expr="." position="inside">
|
||||||
<link rel="stylesheet" href="/runbot/static/src/less/runbot.less"/>
|
<link rel="stylesheet" href="/runbot/static/src/css/runbot.css"/>
|
||||||
</xpath>
|
</xpath>
|
||||||
</template>
|
</template>
|
||||||
</data>
|
</data>
|
||||||
|
@ -121,7 +121,7 @@
|
|||||||
<filter string="Fixed" name="fixed_errors" domain="[('active', '=', False)]"/>
|
<filter string="Fixed" name="fixed_errors" domain="[('active', '=', False)]"/>
|
||||||
<filter string="Not Fixed" name="not_fixed_errors" domain="[('active', '=', True)]"/>
|
<filter string="Not Fixed" name="not_fixed_errors" domain="[('active', '=', True)]"/>
|
||||||
<separator/>
|
<separator/>
|
||||||
<filter string="Not Asigned" name="not_assigned_errors" domain="[('responsible', '=', False)]"/>
|
<filter string="Not Assigned" name="not_assigned_errors" domain="[('responsible', '=', False)]"/>
|
||||||
</search>
|
</search>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
@ -82,21 +82,19 @@
|
|||||||
<field name="name"/>
|
<field name="name"/>
|
||||||
<field name="global_state"/>
|
<field name="global_state"/>
|
||||||
<field name="dest"/>
|
<field name="dest"/>
|
||||||
<separator/>
|
<filter string="Pending" name='pending' domain="[('global_state','=', 'pending')]"/>
|
||||||
<filter string="Pending" domain="[('global_state','=', 'pending')]"/>
|
<filter string="Testing" name='testing' domain="[('global_state','in', ('testing', 'waiting'))]"/>
|
||||||
<filter string="Testing" domain="[('global_state','in', ('testing', 'waiting'))]"/>
|
<filter string="Running" name='running' domain="[('global_state','=', 'running')]"/>
|
||||||
<filter string="Running" domain="[('global_state','=', 'running')]"/>
|
<filter string="Done" name='done' domain="[('global_state','=','done')]"/>
|
||||||
<filter string="Done" domain="[('global_state','=','done')]"/>
|
<filter string="Duplicate" name='duplicate' domain="[('local_state','=', 'duplicate')]"/>
|
||||||
<filter string="Duplicate" domain="[('local_state','=', 'duplicate')]"/>
|
|
||||||
<separator />
|
|
||||||
<group expand="0" string="Group By...">
|
<group expand="0" string="Group By...">
|
||||||
<filter string="Repo" domain="[]" context="{'group_by':'repo_id'}"/>
|
<filter string="Repo" name='repo' domain="[]" context="{'group_by':'repo_id'}"/>
|
||||||
<filter string="Branch" domain="[]" context="{'group_by':'branch_id'}"/>
|
<filter string="Branch" name='branch' domain="[]" context="{'group_by':'branch_id'}"/>
|
||||||
<filter string="Status" domain="[]" context="{'group_by':'global_state'}"/>
|
<filter string="Status" name='status' domain="[]" context="{'group_by':'global_state'}"/>
|
||||||
<filter string="Result" domain="[]" context="{'group_by':'global_result'}"/>
|
<filter string="Result" name='result' domain="[]" context="{'group_by':'global_result'}"/>
|
||||||
<filter string="Start" domain="[]" context="{'group_by':'job_start'}"/>
|
<filter string="Start" name='start' domain="[]" context="{'group_by':'job_start'}"/>
|
||||||
<filter string="Host" domain="[]" context="{'group_by':'host'}"/>
|
<filter string="Host" name='host' domain="[]" context="{'group_by':'host'}"/>
|
||||||
<filter string="Create Date" domain="[]" context="{'group_by':'create_date'}"/>
|
<filter string="Create Date" name='create_date' domain="[]" context="{'group_by':'create_date'}"/>
|
||||||
</group>
|
</group>
|
||||||
</search>
|
</search>
|
||||||
</field>
|
</field>
|
||||||
@ -105,7 +103,6 @@
|
|||||||
<field name="name">Builds</field>
|
<field name="name">Builds</field>
|
||||||
<field name="type">ir.actions.act_window</field>
|
<field name="type">ir.actions.act_window</field>
|
||||||
<field name="res_model">runbot.build</field>
|
<field name="res_model">runbot.build</field>
|
||||||
<field name="view_type">form</field>
|
|
||||||
<field name="view_mode">tree,form,graph,pivot</field>
|
<field name="view_mode">tree,form,graph,pivot</field>
|
||||||
</record>
|
</record>
|
||||||
<menuitem id="menu_build" action="action_build" parent="runbot_menu_root"/>
|
<menuitem id="menu_build" action="action_build" parent="runbot_menu_root"/>
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
<field name="update_github_state" groups="base.group_no_one"/>
|
<field name="update_github_state" groups="base.group_no_one"/>
|
||||||
<field name="protected" groups="base.group_no_one"/>
|
<field name="protected" groups="base.group_no_one"/>
|
||||||
<field name="group" groups="base.group_no_one"/>
|
<field name="group" groups="base.group_no_one"/>
|
||||||
|
<field name="monitoring_view_id" groups="base.group_no_one"/>
|
||||||
</group>
|
</group>
|
||||||
</sheet>
|
</sheet>
|
||||||
<div class="oe_chatter">
|
<div class="oe_chatter">
|
||||||
@ -113,7 +114,7 @@
|
|||||||
<search string="Search config">
|
<search string="Search config">
|
||||||
<field name="name"/>
|
<field name="name"/>
|
||||||
<field name="group_name"/>
|
<field name="group_name"/>
|
||||||
<filter string="Is in a group" domain="[(['group', '!=', False])]"/>
|
<filter string="Is in a group" name='is_in_group' domain="[(['group', '!=', False])]"/>
|
||||||
<filter string="No step's defined" name="no_step" domain="[(['step_order_ids', '=', False])]"/>
|
<filter string="No step's defined" name="no_step" domain="[(['step_order_ids', '=', False])]"/>
|
||||||
</search>
|
</search>
|
||||||
</field>
|
</field>
|
||||||
@ -126,12 +127,12 @@
|
|||||||
<search string="Search config step">
|
<search string="Search config step">
|
||||||
<field name="name"/>
|
<field name="name"/>
|
||||||
<field name="group_name"/>
|
<field name="group_name"/>
|
||||||
<filter string="Install job" domain="[(['job_type', '=', 'install_odoo'])]"/>
|
<filter string="Install job" name='install_job' domain="[(['job_type', '=', 'install_odoo'])]"/>
|
||||||
<filter string="Run job" domain="[(['job_type', '=', 'run_odoo'])]"/>
|
<filter string="Run job" name='run_job' domain="[(['job_type', '=', 'run_odoo'])]"/>
|
||||||
<filter string="Python job" domain="[(['job_type', '=', 'python'])]"/>
|
<filter string="Python job" name='python_job' domain="[(['job_type', '=', 'python'])]"/>
|
||||||
<filter string="Create job" domain="[(['job_type', '=', 'create_build'])]"/>
|
<filter string="Create job" name='create_job' domain="[(['job_type', '=', 'create_build'])]"/>
|
||||||
<separator/>
|
<separator/>
|
||||||
<filter string="Is in a group" domain="[(['group', '!=', False])]"/>
|
<filter string="Is in a group" name='is_in_group' domain="[(['group', '!=', False])]"/>
|
||||||
<separator/>
|
<separator/>
|
||||||
<filter string="No config defined" name="no_step" domain="[(['step_order_ids', '=', False])]"/>
|
<filter string="No config defined" name="no_step" domain="[(['step_order_ids', '=', False])]"/>
|
||||||
</search>
|
</search>
|
||||||
|
@ -16,8 +16,6 @@
|
|||||||
<field name="last_success" readonly='1'/>
|
<field name="last_success" readonly='1'/>
|
||||||
<field name="assigned_only"/>
|
<field name="assigned_only"/>
|
||||||
<field name="nb_worker"/>
|
<field name="nb_worker"/>
|
||||||
<field name="nb_testing"/>
|
|
||||||
<field name="nb_running"/>
|
|
||||||
<field name="last_exception" readonly='1'/>
|
<field name="last_exception" readonly='1'/>
|
||||||
<field name="exception_count" readonly='1'/>
|
<field name="exception_count" readonly='1'/>
|
||||||
</group>
|
</group>
|
||||||
|
@ -6,6 +6,7 @@ from odoo import fields, models, api
|
|||||||
class MultiBuildWizard(models.TransientModel):
|
class MultiBuildWizard(models.TransientModel):
|
||||||
|
|
||||||
_name = 'runbot.build.config.multi.wizard'
|
_name = 'runbot.build.config.multi.wizard'
|
||||||
|
_description = "Multi wizard"
|
||||||
|
|
||||||
base_name = fields.Char('Generic name', required=True)
|
base_name = fields.Char('Generic name', required=True)
|
||||||
prefix = fields.Char('Prefix', help="Leave blank to use login.")
|
prefix = fields.Char('Prefix', help="Leave blank to use login.")
|
||||||
|
@ -30,7 +30,6 @@
|
|||||||
<field name="name">Generate Multi Build Config</field>
|
<field name="name">Generate Multi Build Config</field>
|
||||||
<field name="type">ir.actions.act_window</field>
|
<field name="type">ir.actions.act_window</field>
|
||||||
<field name="res_model">runbot.build.config.multi.wizard</field>
|
<field name="res_model">runbot.build.config.multi.wizard</field>
|
||||||
<field name="view_type">form</field>
|
|
||||||
<field name="view_mode">form</field>
|
<field name="view_mode">form</field>
|
||||||
<field name="view_id" ref="runbot_multi_build_wizard_form"/>
|
<field name="view_id" ref="runbot_multi_build_wizard_form"/>
|
||||||
<field name="target">new</field>
|
<field name="target">new</field>
|
||||||
|
@ -43,12 +43,12 @@ class RunbotClient():
|
|||||||
sleep_time = self.env['runbot.repo']._scheduler_loop_turn(host)
|
sleep_time = self.env['runbot.repo']._scheduler_loop_turn(host)
|
||||||
host.last_end_loop = fields.Datetime.now()
|
host.last_end_loop = fields.Datetime.now()
|
||||||
self.env.cr.commit()
|
self.env.cr.commit()
|
||||||
self.env.reset()
|
self.env.clear()
|
||||||
self.sleep(sleep_time)
|
self.sleep(sleep_time)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
_logger.exception('Builder main loop failed with: %s', e)
|
_logger.exception('Builder main loop failed with: %s', e)
|
||||||
self.env.cr.rollback()
|
self.env.cr.rollback()
|
||||||
self.env.reset()
|
self.env.clear()
|
||||||
self.sleep(10)
|
self.sleep(10)
|
||||||
|
|
||||||
if self.ask_interrupt.is_set():
|
if self.ask_interrupt.is_set():
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
'name': 'Runbot CLA',
|
'name': 'Runbot CLA',
|
||||||
'category': 'Website',
|
'category': 'Website',
|
||||||
'summary': 'Runbot CLA',
|
'summary': 'Runbot CLA',
|
||||||
'version': '2.0',
|
'version': '2.1',
|
||||||
'description': "Runbot CLA",
|
'description': "Runbot CLA",
|
||||||
'author': 'Odoo SA',
|
'author': 'Odoo SA',
|
||||||
'depends': ['runbot'],
|
'depends': ['runbot'],
|
||||||
|
@ -5,11 +5,4 @@
|
|||||||
<field name="job_type">cla_check</field>
|
<field name="job_type">cla_check</field>
|
||||||
<field name="protected" eval="True"/>
|
<field name="protected" eval="True"/>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record id="runbot.runbot_build_config_default" model="runbot.build.config">
|
|
||||||
<field name="step_order_ids" eval="[(0, 0, {'step_id': ref('runbot_build_config_step_check_cla')})]"/>
|
|
||||||
</record>
|
|
||||||
<record id="runbot.runbot_build_config_default_no_run" model="runbot.build.config">
|
|
||||||
<field name="step_order_ids" eval="[(0, 0, {'step_id': ref('runbot_build_config_step_check_cla')})]"/>
|
|
||||||
</record>
|
|
||||||
</odoo>
|
</odoo>
|
||||||
|
Loading…
Reference in New Issue
Block a user