[MERGE] v13 migration of mergebot/forwardbot

This commit is contained in:
Xavier Morel 2020-01-23 16:35:10 +01:00
commit 4bd65a4dff
6 changed files with 119 additions and 66 deletions

View File

@ -17,7 +17,33 @@
<field name="perm_write">1</field>
<field name="perm_unlink">1</field>
</record>
<record id="access_forwardport_tagging_admin" model="ir.model.access">
<field name="name">Admin access to tagging</field>
<field name="model_id" ref="model_forwardport_tagging"/>
<field name="group_id" ref="runbot_merge.group_admin"/>
<field name="perm_read">1</field>
<field name="perm_create">1</field>
<field name="perm_write">1</field>
<field name="perm_unlink">1</field>
</record>
<record id="access_forwardport_branch_remover_admin" model="ir.model.access">
<field name="name">Admin access to branch remover</field>
<field name="model_id" ref="model_forwardport_branch_remover"/>
<field name="group_id" ref="runbot_merge.group_admin"/>
<field name="perm_read">1</field>
<field name="perm_create">1</field>
<field name="perm_write">1</field>
<field name="perm_unlink">1</field>
</record>
<record id="access_forwardport_tagging" model="ir.model.access">
<field name="name">No normal access to tagging</field>
<field name="model_id" ref="model_forwardport_tagging"/>
<field name="perm_read">1</field>
<field name="perm_create">0</field>
<field name="perm_write">0</field>
<field name="perm_unlink">0</field>
</record>
<record id="access_forwardport_batches" model="ir.model.access">
<field name="name">No normal access to batches</field>
<field name="model_id" ref="model_forwardport_batches"/>

View File

@ -886,6 +886,7 @@ class Feedback(models.Model):
class Tagging(models.Model):
_name = 'forwardport.tagging'
_description = "ad-hoc forwardport tagging commands"
token_field = fields.Selection([
('github_token', 'Mergebot'),

View File

@ -26,7 +26,7 @@ WAIT_FOR_VISIBILITY = [10, 10, 10, 10]
_logger = logging.getLogger(__name__)
class Project(models.Model):
_name = 'runbot_merge.project'
_name = _description = 'runbot_merge.project'
name = fields.Char(required=True, index=True)
repo_ids = fields.One2many(
@ -213,7 +213,7 @@ class Project(models.Model):
return bool(self.env.cr.rowcount)
class Repository(models.Model):
_name = 'runbot_merge.repository'
_name = _description = 'runbot_merge.repository'
name = fields.Char(required=True)
project_id = fields.Many2one('runbot_merge.project', required=True)
@ -285,7 +285,7 @@ class Repository(models.Model):
})
class Branch(models.Model):
_name = 'runbot_merge.branch'
_name = _description = 'runbot_merge.branch'
_order = 'sequence, name'
name = fields.Char(required=True)
@ -507,7 +507,7 @@ class Branch(models.Model):
ACL = collections.namedtuple('ACL', 'is_admin is_reviewer is_author')
class PullRequests(models.Model):
_name = 'runbot_merge.pull_requests'
_name = _description = 'runbot_merge.pull_requests'
_order = 'number desc'
target = fields.Many2one('runbot_merge.branch', required=True, index=True)
@ -544,18 +544,14 @@ class PullRequests(models.Model):
reviewed_by = fields.Many2one('res.partner')
delegates = fields.Many2many('res.partner', help="Delegate reviewers, not intrinsically reviewers but can review this PR")
priority = fields.Selection([
(0, 'Urgent'),
(1, 'Pressing'),
(2, 'Normal'),
], default=2, index=True)
priority = fields.Integer(default=2, index=True)
statuses = fields.Text(compute='_compute_statuses')
status = fields.Char(compute='_compute_statuses')
previous_failure = fields.Char(default='{}')
batch_id = fields.Many2one('runbot_merge.batch',compute='_compute_active_batch', store=True)
batch_ids = fields.Many2many('runbot_merge.batch')
batch_id = fields.Many2one('runbot_merge.batch', string="Active Batch", compute='_compute_active_batch', store=True)
batch_ids = fields.Many2many('runbot_merge.batch', string="Batches")
staging_id = fields.Many2one(related='batch_id.staging_id', store=True)
commits_map = fields.Char(help="JSON-encoded mapping of PR commits to actually integrated commits. The integration head (either a merge commit or the PR's topmost) is mapped from the 'empty' pr commit (the key is an empty string, because you can't put a null key in json maps).", default='{}')
@ -574,22 +570,10 @@ class PullRequests(models.Model):
return super(PullRequests, self)._compute_display_name()
def name_get(self):
return {
p.id: '%s#%s' % (p.repository.name, p.number)
return [
(p.id, '%s#%d' % (p.repository.name, p.number))
for p in self
}
def __str__(self):
if len(self) == 0:
separator = ''
elif len(self) == 1:
separator = ' '
else:
separator = 's '
return '<pull_request%s%s>' % (separator, ' '.join(
'{0.id} ({0.display_name})'.format(p)
for p in self
))
]
# missing link to other PRs
@api.depends('priority', 'state', 'squash', 'merge_method', 'batch_id.active', 'label')
@ -608,6 +592,7 @@ class PullRequests(models.Model):
for s in self:
c = Commits.search([('sha', '=', s.head)])
if not (c and c.statuses):
s.status = s.statuses = False
continue
statuses = json.loads(c.statuses)
@ -965,7 +950,6 @@ class PullRequests(models.Model):
'message': message,
})
@api.multi
def write(self, vals):
oldstate = { pr: pr._tagstate for pr in self }
@ -988,7 +972,6 @@ class PullRequests(models.Model):
})
return w
@api.multi
def unlink(self):
for pr in self:
self.env['runbot_merge.pull_requests.tagging'].create({
@ -1212,7 +1195,7 @@ class PullRequests(models.Model):
WHERE id = %s AND state != 'merged'
''', [self.id])
self.env.cr.commit()
self.invalidate_cache(fnames=['state'], ids=[self.id])
self.modified(['state'])
if self.env.cr.rowcount:
self.env['runbot_merge.pull_requests.tagging'].create({
'pull_request': self.number,
@ -1259,7 +1242,7 @@ class Tagging(models.Model):
way of that. Instead, queue tagging changes into this table whose
execution can be cron-driven.
"""
_name = 'runbot_merge.pull_requests.tagging'
_name = _description = 'runbot_merge.pull_requests.tagging'
repository = fields.Many2one('runbot_merge.repository', required=True)
# store the PR number (not id) as we need a Tagging for PR objects
@ -1290,7 +1273,7 @@ class Tagging(models.Model):
class Feedback(models.Model):
""" Queue of feedback comments to send to PR users
"""
_name = 'runbot_merge.pull_requests.feedback'
_name = _description = 'runbot_merge.pull_requests.feedback'
repository = fields.Many2one('runbot_merge.repository', required=True)
# store the PR number (not id) as we may want to send feedback to PR
@ -1310,7 +1293,7 @@ class Commit(models.Model):
independent of everything else as commits can be created by
statuses only, by PR pushes, by branch updates, ...
"""
_name = 'runbot_merge.commit'
_name = _description = 'runbot_merge.commit'
sha = fields.Char(required=True)
statuses = fields.Char(help="json-encoded mapping of status contexts to states", default="{}")
@ -1366,7 +1349,7 @@ class Commit(models.Model):
return res
class Stagings(models.Model):
_name = 'runbot_merge.stagings'
_name = _description = 'runbot_merge.stagings'
target = fields.Many2one('runbot_merge.branch', required=True)
@ -1466,7 +1449,6 @@ class Stagings(models.Model):
vals['timeout_limit'] = fields.Datetime.to_string(datetime.datetime.now() + datetime.timedelta(minutes=s.target.project_id.ci_timeout))
s.write(vals)
@api.multi
def action_cancel(self):
self.cancel("explicitly cancelled by %s", self.env.user.display_name)
return { 'type': 'ir.actions.act_window_close' }
@ -1691,7 +1673,7 @@ class Stagings(models.Model):
return repo_name
class Split(models.Model):
_name = 'runbot_merge.split'
_name = _description = 'runbot_merge.split'
target = fields.Many2one('runbot_merge.branch', required=True)
batch_ids = fields.One2many('runbot_merge.batch', 'split_id', context={'active_test': False})
@ -1703,7 +1685,7 @@ class Batch(models.Model):
repositories e.g. change an API in repo1, this breaks use of that API
in repo2 which now needs to be updated.
"""
_name = 'runbot_merge.batch'
_name = _description = 'runbot_merge.batch'
target = fields.Many2one('runbot_merge.branch', required=True)
staging_id = fields.Many2one('runbot_merge.stagings')
@ -1796,7 +1778,7 @@ class Batch(models.Model):
})
class FetchJob(models.Model):
_name = 'runbot_merge.fetch_job'
_name = _description = 'runbot_merge.fetch_job'
active = fields.Boolean(default=True)
repository = fields.Many2one('runbot_merge.repository', required=True)

View File

@ -8,7 +8,7 @@ class Partner(models.Model):
reviewer = fields.Boolean(default=False, help="Can review PRs (maybe m2m to repos/branches?)")
self_reviewer = fields.Boolean(default=False, help="Can review own PRs (independent from reviewer)")
delegate_reviewer = fields.Many2many('runbot_merge.pull_requests')
formatted_email = fields.Char(compute='_rfc5322_formatted')
formatted_email = fields.Char(string="commit email", compute='_rfc5322_formatted')
def _auto_init(self):
res = super(Partner, self)._auto_init()

View File

@ -0,0 +1,61 @@
// FIX: bs4 shit-heap colors and styles
body {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
color: #666666;
}
h1, h2, h3, h4, h5, h6{
color: inherit;
margin-top: 0.66em;
margin-bottom: 0.33em;
}
h5 { font-size: 1em; }
.bg-success, .bg-info, .bg-warning, .bg-danger, .bg-gray-lighter {
color: inherit;
}
.dropdown-item, .dropdown-menu, .dropdown-menu a {
color: inherit;
}
.bg-success {
background-color: #dff0d8 !important;
}
.bg-info {
background-color: #d9edf7 !important;
}
.bg-warning {
background-color: #fcf8e3 !important;
}
.bg-danger {
background-color: #f2dede !important;
}
.list-inline {
margin-bottom: 10px;
}
.list-inline > li {
padding: 0 5px;
margin-right: 0;
}
// mergebot layouting
.stagings {
display: flex;
align-items: stretch;
}
.stagings > li {
flex: 1;
padding: 0.1em;
padding-left: 0.5em;
}
.stagings > li:not(:last-child) {
border-right: 1px solid lightgray;
}
.batch:not(:last-child) {
border-bottom: 1px solid lightgray;
}
.batch a:not(:last-of-type) a:after {
content: ",";
}
.pr-listing > * { display: inline-block; }
.pr-awaiting { opacity: 0.8; }
.pr-blocked { opacity: 0.6; }
.pr-failed { opacity: 0.9; }

View File

@ -1,34 +1,17 @@
<odoo>
<template id="dashboard" name="mergebot dashboard">
<t t-set="styles">
<style>
.stagings {
display: flex;
align-items: stretch;
}
.stagings > li {
flex: 1;
<function model="website.page" name="write">
<value eval="ref('website.homepage_page')"/>
<value eval="{'active': False}"/>
</function>
padding: 0.1em;
padding-left: 0.5em;
}
.stagings > li:not(:last-child) {
border-right: 1px solid lightgray;
}
.batch:not(:last-child) {
border-bottom: 1px solid lightgray;
}
.batch a:not(:last-of-type) a:after {
content: ",";
}
.pr-listing > * { display: inline-block; }
.pr-awaiting { opacity: 0.8; }
.pr-blocked { opacity: 0.6; }
.pr-failed { opacity: 0.9; }
</style>
</t>
<template id="assets_frontend" inherit_id="web.assets_frontend">
<xpath expr="link[last()]" position="after">
<link rel="stylesheet" type="text/scss" href="/runbot_merge/static/scss/runbot_merge.scss"/>
</xpath>
</template>
<template id="dashboard" name="mergebot dashboard">
<t t-call="website.layout">
<t t-set="head" t-value="(head or '') + styles"/>
<div id="wrap"><div class="container-fluid">
<section t-foreach="projects.with_context(active_test=False)" t-as="project" class="row">
<h1 class="col-md-12"><t t-esc="project.name"/></h1>