diff --git a/runbot/controllers/frontend.py b/runbot/controllers/frontend.py index 9f3b921f..3249c8b1 100644 --- a/runbot/controllers/frontend.py +++ b/runbot/controllers/frontend.py @@ -33,7 +33,7 @@ class Runbot(http.Controller): 'port': real_build.port, 'server_match': real_build.server_match, 'duplicate_of': build.duplicate_id if build.state == 'duplicate' else False, - 'coverage': build.branch_id.coverage, + 'coverage': build.coverage or build.branch_id.coverage, 'revdep_build_ids': sorted(build.revdep_build_ids, key=lambda x: x.repo_id.name), } diff --git a/runbot/models/branch.py b/runbot/models/branch.py index 0166f1f1..581f2964 100644 --- a/runbot/models/branch.py +++ b/runbot/models/branch.py @@ -21,6 +21,7 @@ class runbot_branch(models.Model): pull_head_name = fields.Char(compute='_get_pull_head_name', type='char', string='PR HEAD name', readonly=1, store=True) sticky = fields.Boolean('Sticky') coverage = fields.Boolean('Coverage') + coverage_result = fields.Float(compute='_get_last_coverage', type='Float', string='Last coverage', store=False) state = fields.Char('Status') modules = fields.Char("Modules to Install", help="Comma-separated list of modules to install and test.") job_timeout = fields.Integer('Job Timeout (minutes)', help='For default timeout: Mark it zero') @@ -79,3 +80,13 @@ class runbot_branch(models.Model): r = {} r[self.id] = "http://%s/web/login?db=%s-all&login=admin&redirect=/web?debug=1" % (fqdn, dest) return r + + def _get_last_coverage(self): + """ Return the coverage result of the last build in branch """ + for branch in self: + last_build = self.env['runbot.build'].search([ + ('branch_id.id', '=', branch.id), + ('state', 'in', ['done', 'running']), + ('coverage_result', '>=', 0.0), + ], order='sequence desc', limit=1) + branch.coverage_result = last_build.coverage_result or 0.0 diff --git a/runbot/models/build.py b/runbot/models/build.py index e0c42ac9..e573e27d 100644 --- a/runbot/models/build.py +++ b/runbot/models/build.py @@ -1,10 +1,12 @@ # -*- coding: utf-8 -*- +import coverage import glob import logging import operator import os import re import resource +import shlex import shutil import signal import subprocess @@ -62,6 +64,9 @@ class runbot_build(models.Model): revdep_build_ids = fields.Many2many('runbot.build', 'runbot_rev_dep_builds', column1='rev_dep_id', column2='dependent_id', string='Builds that depends on this build') + extra_params = fields.Char('Extra cmd args') + coverage = fields.Boolean('Enable code coverage') + coverage_result = fields.Float('Coverage result', digits=(5, 2)) def copy(self, values=None): raise UserError("Cannot duplicate build!") @@ -749,6 +754,8 @@ class runbot_build(models.Model): if grep(build._server("tools/config.py"), "test-enable"): cmd.append("--test-enable") cmd += ['-d', '%s-base' % build.dest, '-i', 'base', '--stop-after-init', '--log-level=test', '--max-cron-threads=0'] + if build.extra_params: + cmd.extend(shlex.split(build.extra_params)) return self._spawn(cmd, lock_path, log_path, cpu_limit=300) def _job_20_test_all(self, build, lock_path, log_path): @@ -758,8 +765,10 @@ class runbot_build(models.Model): if grep(build._server("tools/config.py"), "test-enable"): cmd.append("--test-enable") cmd += ['-d', '%s-all' % build.dest, '-i', mods, '--stop-after-init', '--log-level=test', '--max-cron-threads=0'] + if build.extra_params: + cmd.extend(build.extra_params.split(' ')) env = None - if build.branch_id.coverage: + if build.coverage: pyversion = get_py_version(build) env = self._coverage_env(build) available_modules = [ @@ -777,8 +786,8 @@ class runbot_build(models.Model): def _coverage_env(self, build): return dict(os.environ, COVERAGE_FILE=build._path('.coverage')) - def _job_21_coverage(self, build, lock_path, log_path): - if not build.branch_id.coverage: + def _job_21_coverage_html(self, build, lock_path, log_path): + if not build.coverage: return -2 pyversion = get_py_version(build) cov_path = build._path('coverage') @@ -786,6 +795,14 @@ class runbot_build(models.Model): cmd = [pyversion, "-m", "coverage", "html", "-d", cov_path, "--ignore-errors"] return self._spawn(cmd, lock_path, log_path, env=self._coverage_env(build)) + def _job_22_coverage_result(self, build, lock_path, log_path): + if not build.coverage: + return -2 + cov = coverage.coverage(data_file=build._path('.coverage')) + cov.load() + build.coverage_result = cov.report() + return -2 # nothing to wait for + def _job_30_run(self, build, lock_path, log_path): # adjust job_end to record an accurate job_20 job_time build._log('run', 'Start running build %s' % build.dest) diff --git a/runbot/models/repo.py b/runbot/models/repo.py index 13d0be7b..83be6af7 100644 --- a/runbot/models/repo.py +++ b/runbot/models/repo.py @@ -195,6 +195,7 @@ class runbot_repo(models.Model): 'committer_email': committer_email, 'subject': subject, 'date': dateutil.parser.parse(date[:19]), + 'coverage': branch.coverage, } if not branch.sticky: # pending builds are skipped as we have a new ref diff --git a/runbot/templates/frontend.xml b/runbot/templates/frontend.xml index 1ef9ed7e..eedda425 100644 --- a/runbot/templates/frontend.xml +++ b/runbot/templates/frontend.xml @@ -125,6 +125,10 @@ Branch or pull + +
+ cov: % +
default diff --git a/runbot/views/build_views.xml b/runbot/views/build_views.xml index ac460736..2d56aee9 100644 --- a/runbot/views/build_views.xml +++ b/runbot/views/build_views.xml @@ -50,6 +50,7 @@ +