[ADD] runbot_merge: option to disable staging without cron

Because the mergebot crons are on such a tight scheduling, and just
them finding out they have nothing to do can take a while, disabling
them can be a chore. Disabling staging via the project is much less
likely to cause issues as the projects don't normally (or ever?) get
exclusively locked, so they can generally be written to at any moment.

Furthermore, if we ever get in a situation where we have multiple
active projects (not really the case currently, we have multiple
projects but only one is really active) it's less disruptive to
disable stagings on a single specific project.

Fixes #860
This commit is contained in:
Xavier Morel 2023-11-29 13:58:40 +01:00
parent d4fa1fd353
commit 9f54e6f209
6 changed files with 65 additions and 4 deletions

View File

@ -0,0 +1,14 @@
ADD: stagings can now be disabled on a per-project basis
Currently stopping stagings requires stopping the staging cron(s), which causes
several issues:
- the staging cron runs very often, so it can be difficult to find a window to
deactivate it (as the cron runner acquires an exclusive lock on the cron)
- the staging cron is global, so it does not disable staging only on the
problematic project (to say nothing of branch) but on all of them
The latter is not currently a huge issue as only one of the mergebot-tracked
projects is ultra active (spreadsheet activity is on the order of a few
single-PR stagings a day), but the former is really annoying when trying to
stop runaway broken stagings.

View File

@ -24,6 +24,7 @@ class Project(models.Model):
help="Branches of all project's repos which are managed by the merge bot. Also "\
"target branches of PR this project handles."
)
staging_enabled = fields.Boolean(default=True)
ci_timeout = fields.Integer(
default=60, required=True, group_operator=None,
@ -141,6 +142,7 @@ class Project(models.Model):
('active_staging_id', '=', False),
('active', '=', True),
('staging_enabled', '=', True),
('project_id.staging_enabled', '=', True),
]):
try:
with self.env.cr.savepoint(), \

View File

@ -1716,7 +1716,7 @@ class Stagings(models.Model):
def cancel(self, reason, *args):
self = self.filtered('active')
if not self:
return
return False
_logger.info("Cancelling staging %s: " + reason, self, *args)
self.mapped('batch_ids').write({'active': False})
@ -1725,6 +1725,7 @@ class Stagings(models.Model):
'state': 'cancelled',
'reason': reason % args,
})
return True
def fail(self, message, prs=None):
_logger.info("Staging %s failed: %s", self, message)
@ -1745,6 +1746,7 @@ class Stagings(models.Model):
'state': 'failure',
'reason': message,
})
return True
def try_splitting(self):
batches = len(self.batch_ids)

View File

@ -0,0 +1,42 @@
import pytest
from utils import Commit, to_pr
@pytest.fixture
def repo(env, project, make_repo, users, setreviewers):
r = make_repo('repo')
project.write({'repo_ids': [(0, 0, {
'name': r.name,
'group_id': False,
'required_statuses': 'default',
})]})
setreviewers(*project.repo_ids)
return r
def test_disable_staging(env, project, repo, config):
"""In order to avoid issues of cron locking, as well as not disable staging
for every project when trying to freeze just one of them (cough cough), a
toggle is available on the project to skip staging for it.
"""
with repo:
[m] = repo.make_commits(None, Commit("m", tree={"a": "1"}), ref="heads/master")
[c] = repo.make_commits(m, Commit("c", tree={"a": "2"}), ref="heads/other")
pr = repo.make_pr(title="whatever", target="master", head="other")
pr.post_comment("hansen r+", config["role_reviewer"]['token'])
repo.post_status(c, "success")
env.run_crons()
pr_id = to_pr(env, pr)
staging_1 = pr_id.staging_id
assert staging_1.active
project.staging_enabled = False
staging_1.cancel("because")
env.run_crons()
assert staging_1.active is False
assert staging_1.state == "cancelled"
assert not pr_id.staging_id.active,\
"should not be re-staged, because staging has been disabled"

View File

@ -9,7 +9,7 @@ def repo(env, project, make_repo, users, setreviewers):
project.write({'repo_ids': [(0, 0, {
'name': r.name,
'group_id': False,
'required_statuses': 'ci'
'required_statuses': 'default'
})]})
setreviewers(*project.repo_ids)
return r
@ -26,13 +26,13 @@ def test_staging_disabled_branch(env, project, repo, config):
[c1] = repo.make_commits(master_commit, Commit("thing", tree={'a': '2'}), ref='heads/master-thing')
master_pr = repo.make_pr(title="whatever", target="master", head="master-thing")
master_pr.post_comment("hansen r+", config['role_reviewer']['token'])
repo.post_status(c1, 'success', 'ci')
repo.post_status(c1, 'success')
[other_commit] = repo.make_commits(None, Commit("other", tree={'b': '1'}), ref='heads/other')
[c2] = repo.make_commits(other_commit, Commit("thing", tree={'b': '2'}), ref='heads/other-thing')
other_pr = repo.make_pr(title="whatever", target="other", head="other-thing")
other_pr.post_comment("hansen r+", config['role_reviewer']['token'])
repo.post_status(c2, 'success', 'ci')
repo.post_status(c2, 'success')
env.run_crons()
assert to_pr(env, master_pr).staging_id, \

View File

@ -32,6 +32,7 @@
<field name="secret"/>
</group>
<group>
<field name="staging_enabled" widget="boolean_toggle"/>
<field name="uniquifier"/>
<field name="ci_timeout"/>
<field name="batch_limit"/>