mirror of
https://github.com/odoo/runbot.git
synced 2025-03-27 21:35:45 +07:00

Provides a less manual interface for creating the freeze: * takes the name of the branch to create * takes any number of PRs which must be part of the freeze * takes PRs representing the HEADs of the new branches Then essentially takes care of the test. Implementation of the actual wizard is not trivial but fairly straightforward and linear, biggest issue is not being able to `project_id.branch_ids[1]` to get the new branch, not sure why but it seems to ignore the ordering, clearing the cache doens't fix it. When creating the branches, add a sleep after each one for secondary rate limiting purposes. Same when deleting branches. Also the forwardbot has been updated to disable the forwardport cron while a freeze is ongoing, this simplifies the freezing process. Note: after recommendation of @aab-odoo, tried using `_applyChanges` in `_checkState` but it simply did not work: the two relational fields got completely frozen and were impossible to update, which is less than ideal. Oh well, hopefully it works well enough like this for now.
121 lines
4.3 KiB
Python
121 lines
4.3 KiB
Python
import logging
|
|
import re
|
|
|
|
from odoo import models, fields
|
|
|
|
_logger = logging.getLogger(__name__)
|
|
class Project(models.Model):
|
|
_name = _description = 'runbot_merge.project'
|
|
|
|
name = fields.Char(required=True, index=True)
|
|
repo_ids = fields.One2many(
|
|
'runbot_merge.repository', 'project_id',
|
|
help="Repos included in that project, they'll be staged together. "\
|
|
"*Not* to be used for cross-repo dependencies (that is to be handled by the CI)"
|
|
)
|
|
branch_ids = fields.One2many(
|
|
'runbot_merge.branch', 'project_id',
|
|
context={'active_test': False},
|
|
help="Branches of all project's repos which are managed by the merge bot. Also "\
|
|
"target branches of PR this project handles."
|
|
)
|
|
|
|
ci_timeout = fields.Integer(
|
|
default=60, required=True,
|
|
help="Delay (in minutes) before a staging is considered timed out and failed"
|
|
)
|
|
|
|
github_token = fields.Char("Github Token", required=True)
|
|
github_prefix = fields.Char(
|
|
required=True,
|
|
default="hanson", # mergebot du bot du bot du~
|
|
help="Prefix (~bot name) used when sending commands from PR "
|
|
"comments e.g. [hanson retry] or [hanson r+ p=1]"
|
|
)
|
|
|
|
batch_limit = fields.Integer(
|
|
default=8, help="Maximum number of PRs staged together")
|
|
|
|
secret = fields.Char(
|
|
help="Webhook secret. If set, will be checked against the signature "
|
|
"of (valid) incoming webhook signatures, failing signatures "
|
|
"will lead to webhook rejection. Should only use ASCII."
|
|
)
|
|
|
|
freeze_id = fields.Many2one('runbot_merge.project.freeze', compute='_compute_freeze')
|
|
freeze_reminder = fields.Text()
|
|
|
|
def _check_stagings(self, commit=False):
|
|
for branch in self.search([]).mapped('branch_ids').filtered('active'):
|
|
staging = branch.active_staging_id
|
|
if not staging:
|
|
continue
|
|
try:
|
|
with self.env.cr.savepoint():
|
|
staging.check_status()
|
|
except Exception:
|
|
_logger.exception("Failed to check staging for branch %r (staging %s)",
|
|
branch.name, staging)
|
|
else:
|
|
if commit:
|
|
self.env.cr.commit()
|
|
|
|
def _create_stagings(self, commit=False):
|
|
for branch in self.search([]).mapped('branch_ids').filtered('active'):
|
|
if not branch.active_staging_id:
|
|
try:
|
|
with self.env.cr.savepoint():
|
|
branch.try_staging()
|
|
except Exception:
|
|
_logger.exception("Failed to create staging for branch %r", branch.name)
|
|
else:
|
|
if commit:
|
|
self.env.cr.commit()
|
|
|
|
def _find_commands(self, comment):
|
|
return re.findall(
|
|
'^\s*[@|#]?{}:? (.*)$'.format(self.github_prefix),
|
|
comment, re.MULTILINE | re.IGNORECASE)
|
|
|
|
def _has_branch(self, name):
|
|
self.env.cr.execute("""
|
|
SELECT 1 FROM runbot_merge_branch
|
|
WHERE project_id = %s AND name = %s
|
|
LIMIT 1
|
|
""", (self.id, name))
|
|
return bool(self.env.cr.rowcount)
|
|
|
|
def _next_freeze(self):
|
|
prev = self.branch_ids[1:2].name
|
|
if not prev:
|
|
return None
|
|
|
|
m = re.search(r'(\d+)(?:\.(\d+))?$', prev)
|
|
if m:
|
|
return "%s.%d" % (m[1], (int(m[2] or 0) + 1))
|
|
else:
|
|
return f'post-{prev}'
|
|
|
|
def _compute_freeze(self):
|
|
freezes = {
|
|
f.project_id.id: f.id
|
|
for f in self.env['runbot_merge.project.freeze'].search([('project_id', 'in', self.ids)])
|
|
}
|
|
print(freezes)
|
|
for project in self:
|
|
project.freeze_id = freezes.get(project.id) or False
|
|
|
|
def action_prepare_freeze(self):
|
|
""" Initialises the freeze wizard and returns the corresponding action.
|
|
"""
|
|
self.check_access_rights('write')
|
|
self.check_access_rule('write')
|
|
Freeze = self.env['runbot_merge.project.freeze'].sudo()
|
|
|
|
w = Freeze.search([('project_id', '=', self.id)]) or Freeze.create({
|
|
'project_id': self.id,
|
|
'branch_name': self._next_freeze(),
|
|
'release_pr_ids': [(0, 0, {'repository_id': repo.id}) for repo in self.repo_ids]
|
|
})
|
|
return w.action_freeze()
|