[IMP] runbot: add coverage result on builds

When the coverage is activated on a branch, the coverage result is not
stored.

With this commit, the coverage result will be stored on a build.
The last result will be shown on the frontend for sticky branches.

Also, an extra_parameter field is added on the build model.
This commit is contained in:
Christophe Monniez 2018-05-28 09:32:50 +02:00
parent 6dd997dcdd
commit 223ba61828
6 changed files with 38 additions and 4 deletions

View File

@ -33,7 +33,7 @@ class Runbot(http.Controller):
'port': real_build.port, 'port': real_build.port,
'server_match': real_build.server_match, 'server_match': real_build.server_match,
'duplicate_of': build.duplicate_id if build.state == 'duplicate' else False, '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), 'revdep_build_ids': sorted(build.revdep_build_ids, key=lambda x: x.repo_id.name),
} }

View File

@ -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) pull_head_name = fields.Char(compute='_get_pull_head_name', type='char', string='PR HEAD name', readonly=1, store=True)
sticky = fields.Boolean('Sticky') sticky = fields.Boolean('Sticky')
coverage = fields.Boolean('Coverage') coverage = fields.Boolean('Coverage')
coverage_result = fields.Float(compute='_get_last_coverage', type='Float', string='Last coverage', store=False)
state = fields.Char('Status') state = fields.Char('Status')
modules = fields.Char("Modules to Install", help="Comma-separated list of modules to install and test.") 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') 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 = {}
r[self.id] = "http://%s/web/login?db=%s-all&login=admin&redirect=/web?debug=1" % (fqdn, dest) r[self.id] = "http://%s/web/login?db=%s-all&login=admin&redirect=/web?debug=1" % (fqdn, dest)
return r 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

View File

@ -1,10 +1,12 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import coverage
import glob import glob
import logging import logging
import operator import operator
import os import os
import re import re
import resource import resource
import shlex
import shutil import shutil
import signal import signal
import subprocess import subprocess
@ -62,6 +64,9 @@ class runbot_build(models.Model):
revdep_build_ids = fields.Many2many('runbot.build', 'runbot_rev_dep_builds', revdep_build_ids = fields.Many2many('runbot.build', 'runbot_rev_dep_builds',
column1='rev_dep_id', column2='dependent_id', column1='rev_dep_id', column2='dependent_id',
string='Builds that depends on this build') 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): def copy(self, values=None):
raise UserError("Cannot duplicate build!") raise UserError("Cannot duplicate build!")
@ -749,6 +754,8 @@ class runbot_build(models.Model):
if grep(build._server("tools/config.py"), "test-enable"): if grep(build._server("tools/config.py"), "test-enable"):
cmd.append("--test-enable") cmd.append("--test-enable")
cmd += ['-d', '%s-base' % build.dest, '-i', 'base', '--stop-after-init', '--log-level=test', '--max-cron-threads=0'] 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) return self._spawn(cmd, lock_path, log_path, cpu_limit=300)
def _job_20_test_all(self, build, lock_path, log_path): 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"): if grep(build._server("tools/config.py"), "test-enable"):
cmd.append("--test-enable") cmd.append("--test-enable")
cmd += ['-d', '%s-all' % build.dest, '-i', mods, '--stop-after-init', '--log-level=test', '--max-cron-threads=0'] 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 env = None
if build.branch_id.coverage: if build.coverage:
pyversion = get_py_version(build) pyversion = get_py_version(build)
env = self._coverage_env(build) env = self._coverage_env(build)
available_modules = [ available_modules = [
@ -777,8 +786,8 @@ class runbot_build(models.Model):
def _coverage_env(self, build): def _coverage_env(self, build):
return dict(os.environ, COVERAGE_FILE=build._path('.coverage')) return dict(os.environ, COVERAGE_FILE=build._path('.coverage'))
def _job_21_coverage(self, build, lock_path, log_path): def _job_21_coverage_html(self, build, lock_path, log_path):
if not build.branch_id.coverage: if not build.coverage:
return -2 return -2
pyversion = get_py_version(build) pyversion = get_py_version(build)
cov_path = build._path('coverage') cov_path = build._path('coverage')
@ -786,6 +795,14 @@ class runbot_build(models.Model):
cmd = [pyversion, "-m", "coverage", "html", "-d", cov_path, "--ignore-errors"] cmd = [pyversion, "-m", "coverage", "html", "-d", cov_path, "--ignore-errors"]
return self._spawn(cmd, lock_path, log_path, env=self._coverage_env(build)) 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): def _job_30_run(self, build, lock_path, log_path):
# adjust job_end to record an accurate job_20 job_time # adjust job_end to record an accurate job_20 job_time
build._log('run', 'Start running build %s' % build.dest) build._log('run', 'Start running build %s' % build.dest)

View File

@ -195,6 +195,7 @@ class runbot_repo(models.Model):
'committer_email': committer_email, 'committer_email': committer_email,
'subject': subject, 'subject': subject,
'date': dateutil.parser.parse(date[:19]), 'date': dateutil.parser.parse(date[:19]),
'coverage': branch.coverage,
} }
if not branch.sticky: if not branch.sticky:
# pending builds are skipped as we have a new ref # pending builds are skipped as we have a new ref

View File

@ -125,6 +125,10 @@
<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="{{br['branch'].branch_url}}" class="btn btn-default btn-xs">Branch or pull <i class="fa fa-github"/></a>
<a t-attf-href="/runbot/#{repo.id}/#{br['branch'].branch_name}" 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/#{repo.id}/#{br['branch'].branch_name}" 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">
<br/>
<span class="label label-info">cov: <t t-esc="br['branch'].coverage_result"/>%</span>
</t>
</td> </td>
<t t-foreach="br['builds']" t-as="bu"> <t t-foreach="br['builds']" t-as="bu">
<t t-if="bu['state']=='pending'"><t t-set="klass">default</t></t> <t t-if="bu['state']=='pending'"><t t-set="klass">default</t></t>

View File

@ -50,6 +50,7 @@
<field name="port"/> <field name="port"/>
<field name="job"/> <field name="job"/>
<field name="result"/> <field name="result"/>
<field name="coverage_result"/>
<field name="pid"/> <field name="pid"/>
<field name="host"/> <field name="host"/>
<field name="job_start"/> <field name="job_start"/>