mirror of
https://github.com/odoo/runbot.git
synced 2025-03-27 13:25:47 +07:00
[ADD] runbot_merge: substitution filter on PR labels
This commit is contained in:
parent
314622e326
commit
c235e9f6cc
@ -220,6 +220,12 @@ class Repository(models.Model):
|
|||||||
default='legal/cla,ci/runbot'
|
default='legal/cla,ci/runbot'
|
||||||
)
|
)
|
||||||
branch_filter = fields.Char(default='[(1, "=", 1)]', help="Filter branches valid for this repository")
|
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'):
|
def github(self, token_field='github_token'):
|
||||||
return github.GH(self.project_id[token_field], self.name)
|
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
|
branches = self.env['runbot_merge.branch'].search
|
||||||
return self.filtered(lambda r: branch in branches(ast.literal_eval(r.branch_filter)))
|
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):
|
class Branch(models.Model):
|
||||||
_name = _description = 'runbot_merge.branch'
|
_name = _description = 'runbot_merge.branch'
|
||||||
_order = 'sequence, name'
|
_order = 'sequence, name'
|
||||||
@ -1004,7 +1024,7 @@ class PullRequests(models.Model):
|
|||||||
message += '\n\n' + body
|
message += '\n\n' + body
|
||||||
return self.env['runbot_merge.pull_requests'].create({
|
return self.env['runbot_merge.pull_requests'].create({
|
||||||
'number': description['number'],
|
'number': description['number'],
|
||||||
'label': description['head']['label'],
|
'label': repo._remap_label(description['head']['label']),
|
||||||
'author': author.id,
|
'author': author.id,
|
||||||
'target': branch.id,
|
'target': branch.id,
|
||||||
'repository': repo.id,
|
'repository': repo.id,
|
||||||
|
@ -8,6 +8,7 @@ are staged concurrently in all repos
|
|||||||
import json
|
import json
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
import requests
|
||||||
|
|
||||||
from test_utils import re_matches, get_partner
|
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
|
assert r.mapped('review_rights.repository_id') == repo_a | repo_b | repo_c
|
||||||
r.write({'review_rights': [(5, 0, 0)]})
|
r.write({'review_rights': [(5, 0, 0)]})
|
||||||
assert r.mapped('review_rights.repository_id') == env['runbot_merge.repository']
|
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"
|
||||||
|
Loading…
Reference in New Issue
Block a user