[IMP] runbot: simplify views and adapt to odoo13

mainly fixes datetime and bootstrap layouts
This commit is contained in:
Xavier-Do 2020-01-10 11:54:29 +01:00
parent 9215a23cc1
commit 93b901dcdd
20 changed files with 239 additions and 200 deletions

View File

@ -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',
], ],
} }

View File

@ -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)

View File

@ -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)

View File

@ -0,0 +1,5 @@
<odoo>
<record id="website.homepage_page" model="website.page">
<field name="url">/home</field>
</record>
</odoo>

View File

@ -365,6 +365,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):
@ -375,6 +377,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):
@ -384,6 +388,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:

View File

@ -31,6 +31,7 @@ class Config(models.Model):
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('Group name', related='group.name') group_name = fields.Char('Group name', related='group.name')
monitoring_view_id = fields.Many2one('ir.ui.view', 'Monitoring view')
@api.model_create_single @api.model_create_single
def create(self, values): def create(self, values):

View File

@ -163,7 +163,7 @@ class RunbotBuildError(models.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 or '').split(',')]
@api.model @api.model
def disabling_tags(self): def disabling_tags(self):

View File

@ -58,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)

View 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
}

View File

@ -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";
}

View File

@ -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>

View File

@ -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,49 +111,27 @@
</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/>
@ -173,6 +144,7 @@
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:
@ -182,17 +154,20 @@
<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}}&amp;view_type=form&amp;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}}&amp;view_type=form&amp;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 +178,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">&amp;nbsp;<i class="fa fa-chain-broken" title="Build result ignored for parent" />&amp;nbsp;Orphaned build, the result does not affect parent build result</t></p> <t t-if="build.orphan_result">&amp;nbsp;<i class="fa fa-chain-broken" title="Build result ignored for parent" />&amp;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 +188,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="logclass"/>
<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()}}-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 +216,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 +237,6 @@
</t> </t>
</table> </table>
</div> </div>
</div>
</div> </div>
</t> </t>
</template> </template>

View File

@ -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>

View File

@ -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=&quot;website.menu_id.child_id&quot;][@t-as=&quot;submenu&quot;]" 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) &gt; 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>&amp;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']">

View File

@ -389,6 +389,7 @@ class Test_Build(RunbotCase):
'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,6 +405,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_child.global_state, 'done')
self.assertEqual(build_parent.nb_pending, 0) self.assertEqual(build_parent.nb_pending, 0)
self.assertEqual(build_parent.nb_testing, 0) self.assertEqual(build_parent.nb_testing, 0)
self.assertEqual(build_parent.global_state, 'done') self.assertEqual(build_parent.global_state, 'done')

View File

@ -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()

View File

@ -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>

View File

@ -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">

View File

@ -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'],

View File

@ -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>