[ADD] runbot_merge: substitution filter on PR labels

This commit is contained in:
Xavier Morel 2020-02-11 14:20:32 +01:00
parent 314622e326
commit c235e9f6cc
2 changed files with 128 additions and 1 deletions

View File

@ -220,6 +220,12 @@ class Repository(models.Model):
default='legal/cla,ci/runbot'
)
branch_filter = fields.Char(default='[(1, "=", 1)]', help="Filter branches valid for this repository")
substitutions = fields.Text(
"label substitutions",
help="""sed-style substitution patterns applied to the label on input, one per line.
All substitutions are tentatively applied sequentially to the input.
""")
def github(self, token_field='github_token'):
return github.GH(self.project_id[token_field], self.name)
@ -291,6 +297,20 @@ class Repository(models.Model):
branches = self.env['runbot_merge.branch'].search
return self.filtered(lambda r: branch in branches(ast.literal_eval(r.branch_filter)))
def _remap_label(self, label):
print(self.substitutions)
for line in filter(None, (self.substitutions or '').splitlines()):
print(line)
sep = line[0]
_, pattern, repl, flags = line.split(sep)
label = re.sub(
pattern, repl, label,
count=0 if 'g' in flags else 1,
flags=(re.MULTILINE if 'm' in flags.lower() else 0)
| (re.IGNORECASE if 'i' in flags.lower() else 0)
)
return label
class Branch(models.Model):
_name = _description = 'runbot_merge.branch'
_order = 'sequence, name'
@ -1004,7 +1024,7 @@ class PullRequests(models.Model):
message += '\n\n' + body
return self.env['runbot_merge.pull_requests'].create({
'number': description['number'],
'label': description['head']['label'],
'label': repo._remap_label(description['head']['label']),
'author': author.id,
'target': branch.id,
'repository': repo.id,

View File

@ -8,6 +8,7 @@ are staged concurrently in all repos
import json
import pytest
import requests
from test_utils import re_matches, get_partner
@ -758,3 +759,109 @@ def test_remove_acl(env, partners, repo_a, repo_b, repo_c):
assert r.mapped('review_rights.repository_id') == repo_a | repo_b | repo_c
r.write({'review_rights': [(5, 0, 0)]})
assert r.mapped('review_rights.repository_id') == env['runbot_merge.repository']
class TestSubstitutions:
def test_substitution_patterns(self, env, port):
p = env['runbot_merge.project'].create({
'name': 'proj',
'github_token': 'wheeee',
'repo_ids': [(0, 0, {'name': 'xxx/xxx'})],
'branch_ids': [(0, 0, {'name': 'master'})]
})
r = p.repo_ids
# replacement pattern, pr label, stored label
cases = [
('/^foo:/foo-dev:/', 'foo:bar', 'foo-dev:bar'),
('/^foo:/foo-dev:/', 'foox:bar', 'foox:bar'),
('/^foo:/foo-dev:/i', 'FOO:bar', 'foo-dev:bar'),
('/o/x/g', 'foo:bar', 'fxx:bar'),
('@foo:@bar:@', 'foo:bar', 'bar:bar'),
('/foo:/bar:/\n/bar:/baz:/', 'foo:bar', 'baz:bar'),
]
for pr_number, (pattern, original, target) in enumerate(cases, start=1):
r.substitutions = pattern
requests.post(
'http://localhost:{}/runbot_merge/hooks'.format(port),
headers={'X-Github-Event': 'pull_request'},
json={
'action': 'opened',
'repository': {
'full_name': r.name,
},
'pull_request': {
'user': {'login': 'bob'},
'base': {
'repo': {'full_name': r.name},
'ref': p.branch_ids.name,
},
'number': pr_number,
'title': "a pr",
'body': None,
'commits': 1,
'head': {
'label': original,
'sha': format(pr_number, 'x')*40,
}
}
}
)
pr = env['runbot_merge.pull_requests'].search([
('repository', '=', r.id),
('number', '=', pr_number)
])
assert pr.label == target
def test_substitutions_staging(self, env, repo_a, repo_b, config):
""" Different repos from the same project may have different policies for
sourcing PRs. So allow for remapping labels on input in order to match.
"""
repo_b_id = env['runbot_merge.repository'].search([
('name', '=', repo_b.name)
])
# in repo b, replace owner part by repo_a's owner
repo_b_id.substitutions = r"/.+:/%s:/" % repo_a.owner
with repo_a:
make_branch(repo_a, 'master', 'initial', {'a': '0'})
with repo_b:
make_branch(repo_b, 'master', 'initial', {'b': '0'})
# policy is that repo_a PRs are created in the same repo while repo_b PRs
# are created in personal forks
with repo_a:
repo_a.make_commits('master', repo_a.Commit('bop', tree={'a': '1'}), ref='heads/abranch')
pra = repo_a.make_pr(target='master', head='abranch')
b_fork = repo_b.fork()
with b_fork, repo_b:
b_fork.make_commits('master', b_fork.Commit('pob', tree={'b': '1'}), ref='heads/abranch')
prb = repo_b.make_pr(
title="a pr",
target='master', head='%s:abranch' % b_fork.owner
)
pra_id = env['runbot_merge.pull_requests'].search([
('repository.name', '=', repo_a.name),
('number', '=', pra.number)
])
prb_id = env['runbot_merge.pull_requests'].search([
('repository.name', '=', repo_b.name),
('number', '=', prb.number)
])
assert pra_id.label.endswith(':abranch')
assert prb_id.label.endswith(':abranch')
with repo_a, repo_b:
repo_a.post_status(pra.head, 'success', 'legal/cla')
repo_a.post_status(pra.head, 'success', 'ci/runbot')
pra.post_comment('hansen r+', config['role_reviewer']['token'])
repo_b.post_status(prb.head, 'success', 'legal/cla')
repo_b.post_status(prb.head, 'success', 'ci/runbot')
prb.post_comment('hansen r+', config['role_reviewer']['token'])
env.run_crons()
assert pra_id.staging_id, 'PR A should be staged'
assert prb_id.staging_id, "PR B should be staged"
assert pra_id.staging_id == prb_id.staging_id, "both prs should be staged together"
assert pra_id.batch_id == prb_id.batch_id, "both prs should be part of the same batch"