mirror of
https://github.com/odoo/runbot.git
synced 2025-03-27 13:25:47 +07:00
[MERGE] v13 migration of mergebot/forwardbot
This commit is contained in:
commit
4bd65a4dff
@ -17,7 +17,33 @@
|
|||||||
<field name="perm_write">1</field>
|
<field name="perm_write">1</field>
|
||||||
<field name="perm_unlink">1</field>
|
<field name="perm_unlink">1</field>
|
||||||
</record>
|
</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">
|
<record id="access_forwardport_batches" model="ir.model.access">
|
||||||
<field name="name">No normal access to batches</field>
|
<field name="name">No normal access to batches</field>
|
||||||
<field name="model_id" ref="model_forwardport_batches"/>
|
<field name="model_id" ref="model_forwardport_batches"/>
|
||||||
|
@ -886,6 +886,7 @@ class Feedback(models.Model):
|
|||||||
|
|
||||||
class Tagging(models.Model):
|
class Tagging(models.Model):
|
||||||
_name = 'forwardport.tagging'
|
_name = 'forwardport.tagging'
|
||||||
|
_description = "ad-hoc forwardport tagging commands"
|
||||||
|
|
||||||
token_field = fields.Selection([
|
token_field = fields.Selection([
|
||||||
('github_token', 'Mergebot'),
|
('github_token', 'Mergebot'),
|
||||||
|
@ -26,7 +26,7 @@ WAIT_FOR_VISIBILITY = [10, 10, 10, 10]
|
|||||||
|
|
||||||
_logger = logging.getLogger(__name__)
|
_logger = logging.getLogger(__name__)
|
||||||
class Project(models.Model):
|
class Project(models.Model):
|
||||||
_name = 'runbot_merge.project'
|
_name = _description = 'runbot_merge.project'
|
||||||
|
|
||||||
name = fields.Char(required=True, index=True)
|
name = fields.Char(required=True, index=True)
|
||||||
repo_ids = fields.One2many(
|
repo_ids = fields.One2many(
|
||||||
@ -213,7 +213,7 @@ class Project(models.Model):
|
|||||||
return bool(self.env.cr.rowcount)
|
return bool(self.env.cr.rowcount)
|
||||||
|
|
||||||
class Repository(models.Model):
|
class Repository(models.Model):
|
||||||
_name = 'runbot_merge.repository'
|
_name = _description = 'runbot_merge.repository'
|
||||||
|
|
||||||
name = fields.Char(required=True)
|
name = fields.Char(required=True)
|
||||||
project_id = fields.Many2one('runbot_merge.project', required=True)
|
project_id = fields.Many2one('runbot_merge.project', required=True)
|
||||||
@ -285,7 +285,7 @@ class Repository(models.Model):
|
|||||||
})
|
})
|
||||||
|
|
||||||
class Branch(models.Model):
|
class Branch(models.Model):
|
||||||
_name = 'runbot_merge.branch'
|
_name = _description = 'runbot_merge.branch'
|
||||||
_order = 'sequence, name'
|
_order = 'sequence, name'
|
||||||
|
|
||||||
name = fields.Char(required=True)
|
name = fields.Char(required=True)
|
||||||
@ -507,7 +507,7 @@ class Branch(models.Model):
|
|||||||
|
|
||||||
ACL = collections.namedtuple('ACL', 'is_admin is_reviewer is_author')
|
ACL = collections.namedtuple('ACL', 'is_admin is_reviewer is_author')
|
||||||
class PullRequests(models.Model):
|
class PullRequests(models.Model):
|
||||||
_name = 'runbot_merge.pull_requests'
|
_name = _description = 'runbot_merge.pull_requests'
|
||||||
_order = 'number desc'
|
_order = 'number desc'
|
||||||
|
|
||||||
target = fields.Many2one('runbot_merge.branch', required=True, index=True)
|
target = fields.Many2one('runbot_merge.branch', required=True, index=True)
|
||||||
@ -544,18 +544,14 @@ class PullRequests(models.Model):
|
|||||||
|
|
||||||
reviewed_by = fields.Many2one('res.partner')
|
reviewed_by = fields.Many2one('res.partner')
|
||||||
delegates = fields.Many2many('res.partner', help="Delegate reviewers, not intrinsically reviewers but can review this PR")
|
delegates = fields.Many2many('res.partner', help="Delegate reviewers, not intrinsically reviewers but can review this PR")
|
||||||
priority = fields.Selection([
|
priority = fields.Integer(default=2, index=True)
|
||||||
(0, 'Urgent'),
|
|
||||||
(1, 'Pressing'),
|
|
||||||
(2, 'Normal'),
|
|
||||||
], default=2, index=True)
|
|
||||||
|
|
||||||
statuses = fields.Text(compute='_compute_statuses')
|
statuses = fields.Text(compute='_compute_statuses')
|
||||||
status = fields.Char(compute='_compute_statuses')
|
status = fields.Char(compute='_compute_statuses')
|
||||||
previous_failure = fields.Char(default='{}')
|
previous_failure = fields.Char(default='{}')
|
||||||
|
|
||||||
batch_id = fields.Many2one('runbot_merge.batch',compute='_compute_active_batch', store=True)
|
batch_id = fields.Many2one('runbot_merge.batch', string="Active Batch", compute='_compute_active_batch', store=True)
|
||||||
batch_ids = fields.Many2many('runbot_merge.batch')
|
batch_ids = fields.Many2many('runbot_merge.batch', string="Batches")
|
||||||
staging_id = fields.Many2one(related='batch_id.staging_id', store=True)
|
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='{}')
|
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()
|
return super(PullRequests, self)._compute_display_name()
|
||||||
|
|
||||||
def name_get(self):
|
def name_get(self):
|
||||||
return {
|
return [
|
||||||
p.id: '%s#%s' % (p.repository.name, p.number)
|
(p.id, '%s#%d' % (p.repository.name, p.number))
|
||||||
for p in self
|
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
|
# missing link to other PRs
|
||||||
@api.depends('priority', 'state', 'squash', 'merge_method', 'batch_id.active', 'label')
|
@api.depends('priority', 'state', 'squash', 'merge_method', 'batch_id.active', 'label')
|
||||||
@ -608,6 +592,7 @@ class PullRequests(models.Model):
|
|||||||
for s in self:
|
for s in self:
|
||||||
c = Commits.search([('sha', '=', s.head)])
|
c = Commits.search([('sha', '=', s.head)])
|
||||||
if not (c and c.statuses):
|
if not (c and c.statuses):
|
||||||
|
s.status = s.statuses = False
|
||||||
continue
|
continue
|
||||||
|
|
||||||
statuses = json.loads(c.statuses)
|
statuses = json.loads(c.statuses)
|
||||||
@ -965,7 +950,6 @@ class PullRequests(models.Model):
|
|||||||
'message': message,
|
'message': message,
|
||||||
})
|
})
|
||||||
|
|
||||||
@api.multi
|
|
||||||
def write(self, vals):
|
def write(self, vals):
|
||||||
oldstate = { pr: pr._tagstate for pr in self }
|
oldstate = { pr: pr._tagstate for pr in self }
|
||||||
|
|
||||||
@ -988,7 +972,6 @@ class PullRequests(models.Model):
|
|||||||
})
|
})
|
||||||
return w
|
return w
|
||||||
|
|
||||||
@api.multi
|
|
||||||
def unlink(self):
|
def unlink(self):
|
||||||
for pr in self:
|
for pr in self:
|
||||||
self.env['runbot_merge.pull_requests.tagging'].create({
|
self.env['runbot_merge.pull_requests.tagging'].create({
|
||||||
@ -1212,7 +1195,7 @@ class PullRequests(models.Model):
|
|||||||
WHERE id = %s AND state != 'merged'
|
WHERE id = %s AND state != 'merged'
|
||||||
''', [self.id])
|
''', [self.id])
|
||||||
self.env.cr.commit()
|
self.env.cr.commit()
|
||||||
self.invalidate_cache(fnames=['state'], ids=[self.id])
|
self.modified(['state'])
|
||||||
if self.env.cr.rowcount:
|
if self.env.cr.rowcount:
|
||||||
self.env['runbot_merge.pull_requests.tagging'].create({
|
self.env['runbot_merge.pull_requests.tagging'].create({
|
||||||
'pull_request': self.number,
|
'pull_request': self.number,
|
||||||
@ -1259,7 +1242,7 @@ class Tagging(models.Model):
|
|||||||
way of that. Instead, queue tagging changes into this table whose
|
way of that. Instead, queue tagging changes into this table whose
|
||||||
execution can be cron-driven.
|
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)
|
repository = fields.Many2one('runbot_merge.repository', required=True)
|
||||||
# store the PR number (not id) as we need a Tagging for PR objects
|
# 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):
|
class Feedback(models.Model):
|
||||||
""" Queue of feedback comments to send to PR users
|
""" 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)
|
repository = fields.Many2one('runbot_merge.repository', required=True)
|
||||||
# store the PR number (not id) as we may want to send feedback to PR
|
# 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
|
independent of everything else as commits can be created by
|
||||||
statuses only, by PR pushes, by branch updates, ...
|
statuses only, by PR pushes, by branch updates, ...
|
||||||
"""
|
"""
|
||||||
_name = 'runbot_merge.commit'
|
_name = _description = 'runbot_merge.commit'
|
||||||
|
|
||||||
sha = fields.Char(required=True)
|
sha = fields.Char(required=True)
|
||||||
statuses = fields.Char(help="json-encoded mapping of status contexts to states", default="{}")
|
statuses = fields.Char(help="json-encoded mapping of status contexts to states", default="{}")
|
||||||
@ -1366,7 +1349,7 @@ class Commit(models.Model):
|
|||||||
return res
|
return res
|
||||||
|
|
||||||
class Stagings(models.Model):
|
class Stagings(models.Model):
|
||||||
_name = 'runbot_merge.stagings'
|
_name = _description = 'runbot_merge.stagings'
|
||||||
|
|
||||||
target = fields.Many2one('runbot_merge.branch', required=True)
|
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))
|
vals['timeout_limit'] = fields.Datetime.to_string(datetime.datetime.now() + datetime.timedelta(minutes=s.target.project_id.ci_timeout))
|
||||||
s.write(vals)
|
s.write(vals)
|
||||||
|
|
||||||
@api.multi
|
|
||||||
def action_cancel(self):
|
def action_cancel(self):
|
||||||
self.cancel("explicitly cancelled by %s", self.env.user.display_name)
|
self.cancel("explicitly cancelled by %s", self.env.user.display_name)
|
||||||
return { 'type': 'ir.actions.act_window_close' }
|
return { 'type': 'ir.actions.act_window_close' }
|
||||||
@ -1691,7 +1673,7 @@ class Stagings(models.Model):
|
|||||||
return repo_name
|
return repo_name
|
||||||
|
|
||||||
class Split(models.Model):
|
class Split(models.Model):
|
||||||
_name = 'runbot_merge.split'
|
_name = _description = 'runbot_merge.split'
|
||||||
|
|
||||||
target = fields.Many2one('runbot_merge.branch', required=True)
|
target = fields.Many2one('runbot_merge.branch', required=True)
|
||||||
batch_ids = fields.One2many('runbot_merge.batch', 'split_id', context={'active_test': False})
|
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
|
repositories e.g. change an API in repo1, this breaks use of that API
|
||||||
in repo2 which now needs to be updated.
|
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)
|
target = fields.Many2one('runbot_merge.branch', required=True)
|
||||||
staging_id = fields.Many2one('runbot_merge.stagings')
|
staging_id = fields.Many2one('runbot_merge.stagings')
|
||||||
@ -1796,7 +1778,7 @@ class Batch(models.Model):
|
|||||||
})
|
})
|
||||||
|
|
||||||
class FetchJob(models.Model):
|
class FetchJob(models.Model):
|
||||||
_name = 'runbot_merge.fetch_job'
|
_name = _description = 'runbot_merge.fetch_job'
|
||||||
|
|
||||||
active = fields.Boolean(default=True)
|
active = fields.Boolean(default=True)
|
||||||
repository = fields.Many2one('runbot_merge.repository', required=True)
|
repository = fields.Many2one('runbot_merge.repository', required=True)
|
||||||
|
@ -8,7 +8,7 @@ class Partner(models.Model):
|
|||||||
reviewer = fields.Boolean(default=False, help="Can review PRs (maybe m2m to repos/branches?)")
|
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)")
|
self_reviewer = fields.Boolean(default=False, help="Can review own PRs (independent from reviewer)")
|
||||||
delegate_reviewer = fields.Many2many('runbot_merge.pull_requests')
|
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):
|
def _auto_init(self):
|
||||||
res = super(Partner, self)._auto_init()
|
res = super(Partner, self)._auto_init()
|
||||||
|
61
runbot_merge/static/scss/runbot_merge.scss
Normal file
61
runbot_merge/static/scss/runbot_merge.scss
Normal 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; }
|
@ -1,34 +1,17 @@
|
|||||||
<odoo>
|
<odoo>
|
||||||
<template id="dashboard" name="mergebot dashboard">
|
<function model="website.page" name="write">
|
||||||
<t t-set="styles">
|
<value eval="ref('website.homepage_page')"/>
|
||||||
<style>
|
<value eval="{'active': False}"/>
|
||||||
.stagings {
|
</function>
|
||||||
display: flex;
|
|
||||||
align-items: stretch;
|
|
||||||
}
|
|
||||||
.stagings > li {
|
|
||||||
flex: 1;
|
|
||||||
|
|
||||||
padding: 0.1em;
|
<template id="assets_frontend" inherit_id="web.assets_frontend">
|
||||||
padding-left: 0.5em;
|
<xpath expr="link[last()]" position="after">
|
||||||
}
|
<link rel="stylesheet" type="text/scss" href="/runbot_merge/static/scss/runbot_merge.scss"/>
|
||||||
.stagings > li:not(:last-child) {
|
</xpath>
|
||||||
border-right: 1px solid lightgray;
|
</template>
|
||||||
}
|
|
||||||
.batch:not(:last-child) {
|
<template id="dashboard" name="mergebot dashboard">
|
||||||
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>
|
|
||||||
<t t-call="website.layout">
|
<t t-call="website.layout">
|
||||||
<t t-set="head" t-value="(head or '') + styles"/>
|
|
||||||
<div id="wrap"><div class="container-fluid">
|
<div id="wrap"><div class="container-fluid">
|
||||||
<section t-foreach="projects.with_context(active_test=False)" t-as="project" class="row">
|
<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>
|
<h1 class="col-md-12"><t t-esc="project.name"/></h1>
|
||||||
|
Loading…
Reference in New Issue
Block a user