diff --git a/runbot/__manifest__.py b/runbot/__manifest__.py index 1583afd0..f29a7c28 100644 --- a/runbot/__manifest__.py +++ b/runbot/__manifest__.py @@ -6,7 +6,7 @@ 'author': "Odoo SA", 'website': "http://runbot.odoo.com", 'category': 'Website', - 'version': '4.6', + 'version': '4.8', 'depends': ['website', 'base'], 'data': [ 'security/runbot_security.xml', @@ -33,5 +33,6 @@ 'data/build_parse.xml', 'data/runbot_error_regex_data.xml', 'data/error_link.xml', + 'data/website_data.xml', ], } diff --git a/runbot/controllers/badge.py b/runbot/controllers/badge.py index 5903ac34..e6890e78 100644 --- a/runbot/controllers/badge.py +++ b/runbot/controllers/badge.py @@ -35,7 +35,7 @@ class RunbotBadge(Controller): build = builds[0] 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: return werkzeug.wrappers.Response(status=304) diff --git a/runbot/controllers/frontend.py b/runbot/controllers/frontend.py index 4ad29ddf..12b6442c 100644 --- a/runbot/controllers/frontend.py +++ b/runbot/controllers/frontend.py @@ -39,6 +39,7 @@ class Runbot(Controller): 'host_stats': [], 'pending_total': pending[0], 'pending_level': pending[1], + 'hosts_data': request.env['runbot.host'].search([]), 'search': search, 'refresh': refresh, } @@ -101,15 +102,11 @@ class Runbot(Controller): def branch_info(branch): return { 'branch': branch, - 'fqdn': fqdn(), 'builds': [build_dict[build_id] for build_id in build_by_branch_ids.get(branch.id) or []] } context.update({ '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), 'fqdn': fqdn(), }) @@ -117,14 +114,6 @@ class Runbot(Controller): # consider host gone if no build in last 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')}) return request.render('runbot.repo', context) @@ -302,21 +291,27 @@ class Runbot(Controller): return request.render("runbot.glances", qctx) @route('/runbot/monitoring', type='http', auth='user', website=True) - def monitoring(self, refresh=None): + @route('/runbot/monitoring/', type='http', auth='user', website=True) + @route('/runbot/monitoring//', type='http', auth='user', website=True) + def monitoring(self, config_id=None, view_id=None, refresh=None): glances_ctx = self._glances_ctx() pending = self._pending() hosts_data = request.env['runbot.host'].search([]) - monitored_config_id = int(request.env['ir.config_parameter'].sudo().get_param('runbot.monitored_config_id', 1)) - request.env.cr.execute("""SELECT DISTINCT ON (branch_id) branch_id, id FROM runbot_build - WHERE config_id = %s - AND global_state in ('running', 'done') - AND branch_id in (SELECT id FROM runbot_branch where sticky='t') - 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()]) + last_monitored = None + if config_id or config_id is None: + monitored_config_id = config_id or int(request.env['ir.config_parameter'].sudo().get_param('runbot.monitored_config_id', 1)) + request.env.cr.execute("""SELECT DISTINCT ON (branch_id) branch_id, id FROM runbot_build + WHERE config_id = %s + AND global_state in ('running', 'done') + AND branch_id in (SELECT id FROM runbot_branch where sticky='t') + 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 = { + 'config': config, 'refresh': refresh, 'pending_total': pending[0], 'pending_level': pending[1], @@ -326,7 +321,7 @@ class Runbot(Controller): 'auto_tags': request.env['runbot.build.error'].disabling_tags(), '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/', '/runbot/branch//page/'], website=True, auth='public', type='http') 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, total=builds_count, page=page, - step=50 + step=50, ) 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) diff --git a/runbot/data/website_data.xml b/runbot/data/website_data.xml new file mode 100644 index 00000000..104c9acf --- /dev/null +++ b/runbot/data/website_data.xml @@ -0,0 +1,5 @@ + + + /home + + diff --git a/runbot/models/build.py b/runbot/models/build.py index 1378482c..f76052b3 100644 --- a/runbot/models/build.py +++ b/runbot/models/build.py @@ -365,6 +365,8 @@ class runbot_build(models.Model): build.job_time = int(dt2time(build.job_end) - dt2time(build.job_start)) elif 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') 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)) elif 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') def _compute_build_age(self): @@ -384,6 +388,8 @@ class runbot_build(models.Model): build.build_age = build.duplicate_id.build_age elif build.job_start: build.build_age = int(time.time() - dt2time(build.build_start)) + else: + build.build_age = 0 def _get_params(self): try: diff --git a/runbot/models/build_config.py b/runbot/models/build_config.py index 08fe2da0..8518844c 100644 --- a/runbot/models/build_config.py +++ b/runbot/models/build_config.py @@ -31,6 +31,7 @@ class Config(models.Model): 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_name = fields.Char('Group name', related='group.name') + monitoring_view_id = fields.Many2one('ir.ui.view', 'Monitoring view') @api.model_create_single def create(self, values): diff --git a/runbot/models/build_error.py b/runbot/models/build_error.py index 0eb9d419..94ddef09 100644 --- a/runbot/models/build_error.py +++ b/runbot/models/build_error.py @@ -163,7 +163,7 @@ class RunbotBuildError(models.Model): def test_tags_list(self): active_errors = self.search([('test_tags', '!=', 'False'), ('random', '=', True)]) 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 def disabling_tags(self): diff --git a/runbot/models/host.py b/runbot/models/host.py index 46556b57..6ac52778 100644 --- a/runbot/models/host.py +++ b/runbot/models/host.py @@ -58,10 +58,15 @@ class RunboHost(models.Model): return int(icp.get_param('runbot.runbot_running_max', default=75)) def set_psql_conn_count(self): - _logger.debug('Updating psql connection count...') self.ensure_one() with local_pgadmin_cursor() as local_cr: local_cr.execute("SELECT sum(numbackends) FROM pg_stat_database;") res = local_cr.fetchone() 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) diff --git a/runbot/static/src/css/runbot.css b/runbot/static/src/css/runbot.css new file mode 100644 index 00000000..97439884 --- /dev/null +++ b/runbot/static/src/css/runbot.css @@ -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 +} diff --git a/runbot/static/src/less/runbot.less b/runbot/static/src/less/runbot.less deleted file mode 100644 index dbad1694..00000000 --- a/runbot/static/src/less/runbot.less +++ /dev/null @@ -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"; -} diff --git a/runbot/templates/branch.xml b/runbot/templates/branch.xml index 9e5f5f79..d92f8eb8 100644 --- a/runbot/templates/branch.xml +++ b/runbot/templates/branch.xml @@ -33,7 +33,7 @@ default killed - + diff --git a/runbot/templates/build.xml b/runbot/templates/build.xml index 1aa8b790..c2a1a2d9 100644 --- a/runbot/templates/build.xml +++ b/runbot/templates/build.xml @@ -40,13 +40,6 @@ - - - - - - - diff --git a/runbot/templates/dashboard.xml b/runbot/templates/dashboard.xml index da5630b3..07394522 100644 --- a/runbot/templates/dashboard.xml +++ b/runbot/templates/dashboard.xml @@ -141,21 +141,7 @@
- Pending: - 0 - 0 - - - - - - success - - info - warning - danger - - Testing: / +
@@ -178,9 +164,9 @@ danger / - - - + + + success info diff --git a/runbot/templates/frontend.xml b/runbot/templates/frontend.xml index 943a4650..c7a29692 100644 --- a/runbot/templates/frontend.xml +++ b/runbot/templates/frontend.xml @@ -2,13 +2,48 @@ - + + +