runbot/runbot/models/branch.py
2018-11-29 16:31:38 +01:00

91 lines
4.0 KiB
Python

# -*- coding: utf-8 -*-
import logging
import re
from subprocess import CalledProcessError
from odoo import models, fields, api
_logger = logging.getLogger(__name__)
_re_coverage = re.compile(r'\bcoverage\b')
_re_patch = re.compile(r'.*patch-\d+$')
class runbot_branch(models.Model):
_name = "runbot.branch"
_order = 'name'
_sql_constraints = [('branch_repo_uniq', 'unique (name,repo_id)', 'The branch must be unique per repository !')]
repo_id = fields.Many2one('runbot.repo', 'Repository', required=True, ondelete='cascade')
name = fields.Char('Ref Name', required=True)
branch_name = fields.Char(compute='_get_branch_infos', string='Branch', readonly=1, store=True)
branch_url = fields.Char(compute='_get_branch_infos', string='Branch url', readonly=1)
pull_head_name = fields.Char(compute='_get_branch_infos', string='PR HEAD name', readonly=1, store=True)
target_branch_name = fields.Char(compute='_get_branch_infos', string='PR target branch', 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')
priority = fields.Boolean('Build priority', default=False)
@api.depends('name')
def _get_branch_infos(self):
"""compute branch_name, branch_url, pull_head_name and target_branch_name based on name"""
for branch in self:
if branch.name:
branch.branch_name = branch.name.split('/')[-1]
if re.match('^[0-9]+$', branch.branch_name):
branch.branch_url = "https://%s/pull/%s" % (branch.repo_id.base, branch.branch_name)
else:
branch.branch_url = "https://%s/tree/%s" % (branch.repo_id.base, branch.branch_name)
pi = branch._get_pull_info()
if pi:
branch.target_branch_name = pi['base']['ref']
if not _re_patch.match(pi['head']['label']):
# label is used to disambiguate PR with same branch name
branch.pull_head_name = pi['head']['label']
def _get_pull_info(self):
self.ensure_one()
repo = self.repo_id
if repo.token and self.name.startswith('refs/pull/'):
pull_number = self.name[len('refs/pull/'):]
return repo._github('/repos/:owner/:repo/pulls/%s' % pull_number, ignore_errors=True) or {}
return {}
def _is_on_remote(self):
# check that a branch still exists on remote
self.ensure_one()
branch = self
repo = branch.repo_id
try:
repo._git(['ls-remote', '-q', '--exit-code', repo.name, branch.name])
except CalledProcessError:
return False
return True
def create(self, vals):
vals.setdefault('coverage', _re_coverage.search(vals.get('name') or '') is not None)
return super(runbot_branch, self).create(vals)
def _get_branch_quickconnect_url(self, fqdn, dest):
self.ensure_one()
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_build(self):
""" Return the last build with a coverage value > 0"""
self.ensure_one()
return self.env['runbot.build'].search([
('branch_id.id', '=', self.id),
('state', 'in', ['done', 'running']),
('coverage_result', '>=', 0.0),
], order='sequence desc', limit=1)
def _get_last_coverage(self):
""" Compute the coverage result of the last build in branch """
for branch in self:
last_build = branch._get_last_coverage_build()
branch.coverage_result = last_build.coverage_result or 0.0