mirror of
https://github.com/odoo/runbot.git
synced 2025-03-27 13:25:47 +07:00
[CHG] runbot_merge: convert rebase-ff staging to working locally
Implemented rebase (-ish?) by hand, following the model of the one built on the github API, so we have all the information we need *and* still don't need a local working copy. Had to update *several* tests due to formatting changes from using the CLI git (maybe that could be avoided by feeding the commit message through stdin but w/e). Also added a dupe of an existing helper for the formatting changes bit, will have to remove it once all's done.
This commit is contained in:
parent
6b72585ef1
commit
a9b4b373c2
@ -537,6 +537,9 @@ class Repo:
|
|||||||
self.hook = False
|
self.hook = False
|
||||||
repos.append(self)
|
repos.append(self)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f'<conftest.Repo {self.name}>'
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def owner(self):
|
def owner(self):
|
||||||
return self.name.split('/')[0]
|
return self.name.split('/')[0]
|
||||||
|
@ -6,7 +6,7 @@ from datetime import datetime, timedelta
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from utils import seen, Commit, make_basic, REF_PATTERN, MESSAGE_TEMPLATE, validate_all, part_of
|
from utils import seen, Commit, make_basic, REF_PATTERN, MESSAGE_TEMPLATE, validate_all, part_of, part_of2
|
||||||
|
|
||||||
FMT = '%Y-%m-%d %H:%M:%S'
|
FMT = '%Y-%m-%d %H:%M:%S'
|
||||||
FAKE_PREV_WEEK = (datetime.now() + timedelta(days=1)).strftime(FMT)
|
FAKE_PREV_WEEK = (datetime.now() + timedelta(days=1)).strftime(FMT)
|
||||||
@ -206,7 +206,7 @@ More info at https://github.com/odoo/odoo/wiki/Mergebot#forward-port
|
|||||||
old_b = prod.read_tree(b_head)
|
old_b = prod.read_tree(b_head)
|
||||||
head_b = prod.commit('b')
|
head_b = prod.commit('b')
|
||||||
assert head_b.message == message_template % pr1.number
|
assert head_b.message == message_template % pr1.number
|
||||||
assert prod.commit(head_b.parents[0]).message == part_of(f'p_0\n\nX-original-commit: {p_0_merged}', pr1, separator='\n')
|
assert prod.commit(head_b.parents[0]).message == part_of2(f'p_0\n\nX-original-commit: {p_0_merged}', pr1, separator='\n')
|
||||||
b_tree = prod.read_tree(head_b)
|
b_tree = prod.read_tree(head_b)
|
||||||
assert b_tree == {
|
assert b_tree == {
|
||||||
**old_b,
|
**old_b,
|
||||||
@ -215,7 +215,7 @@ More info at https://github.com/odoo/odoo/wiki/Mergebot#forward-port
|
|||||||
old_c = prod.read_tree(c_head)
|
old_c = prod.read_tree(c_head)
|
||||||
head_c = prod.commit('c')
|
head_c = prod.commit('c')
|
||||||
assert head_c.message == message_template % pr2.number
|
assert head_c.message == message_template % pr2.number
|
||||||
assert prod.commit(head_c.parents[0]).message == part_of(f'p_0\n\nX-original-commit: {p_0_merged}', pr2, separator='\n')
|
assert prod.commit(head_c.parents[0]).message == part_of2(f'p_0\n\nX-original-commit: {p_0_merged}', pr2, separator='\n')
|
||||||
c_tree = prod.read_tree(head_c)
|
c_tree = prod.read_tree(head_c)
|
||||||
assert c_tree == {
|
assert c_tree == {
|
||||||
**old_c,
|
**old_c,
|
||||||
|
@ -8,7 +8,8 @@ MESSAGE_TEMPLATE = """{message}
|
|||||||
|
|
||||||
closes {repo}#{number}
|
closes {repo}#{number}
|
||||||
|
|
||||||
{headers}Signed-off-by: {name} <{email}>"""
|
{headers}Signed-off-by: {name} <{email}>
|
||||||
|
"""
|
||||||
# target branch '-' source branch '-' base64 unique '-fw'
|
# target branch '-' source branch '-' base64 unique '-fw'
|
||||||
REF_PATTERN = r'{target}-{source}-[a-zA-Z0-9_-]{{4}}-fw'
|
REF_PATTERN = r'{target}-{source}-[a-zA-Z0-9_-]{{4}}-fw'
|
||||||
|
|
||||||
@ -139,3 +140,6 @@ def part_of(label, pr_id, *, separator='\n\n'):
|
|||||||
""" Adds the "part-of" pseudo-header in the footer.
|
""" Adds the "part-of" pseudo-header in the footer.
|
||||||
"""
|
"""
|
||||||
return f'{label}{separator}Part-of: {pr_id.display_name}'
|
return f'{label}{separator}Part-of: {pr_id.display_name}'
|
||||||
|
|
||||||
|
def part_of2(label, pr_id, *, separator='\n\n'):
|
||||||
|
return part_of(label, pr_id, separator=separator) + '\n'
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
import dataclasses
|
import dataclasses
|
||||||
import itertools
|
import itertools
|
||||||
import logging
|
import logging
|
||||||
|
import os
|
||||||
import pathlib
|
import pathlib
|
||||||
import resource
|
import resource
|
||||||
import subprocess
|
import subprocess
|
||||||
from typing import Optional, TypeVar, Union, Generic
|
from typing import Optional, TypeVar, Union, Generic, Sequence, Tuple, Dict
|
||||||
|
|
||||||
from odoo.tools.appdirs import user_cache_dir
|
from odoo.tools.appdirs import user_cache_dir
|
||||||
|
from .github import MergeError, PrCommit
|
||||||
|
|
||||||
_logger = logging.getLogger(__name__)
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -113,6 +115,70 @@ class Repo(Generic[T]):
|
|||||||
)
|
)
|
||||||
return Repo(to)
|
return Repo(to)
|
||||||
|
|
||||||
|
def rebase(self, dest: str, commits: Sequence[PrCommit]) -> Tuple[str, Dict[str, str]]:
|
||||||
|
"""Implements rebase by hand atop plumbing so:
|
||||||
|
|
||||||
|
- we can work without a working copy
|
||||||
|
- we can track individual commits (and store the mapping)
|
||||||
|
|
||||||
|
It looks like `--merge-base` is not sufficient for `merge-tree` to
|
||||||
|
correctly keep track of history, so it loses contents. Therefore
|
||||||
|
implement in two passes as in the github version.
|
||||||
|
"""
|
||||||
|
repo = self.stdout().with_config(text=True, check=False)
|
||||||
|
|
||||||
|
logger = _logger.getChild('rebase')
|
||||||
|
logger.debug("rebasing %s on %s (reset=%s, commits=%s)",
|
||||||
|
self._repo, dest, len(commits))
|
||||||
|
if not commits:
|
||||||
|
raise MergeError("PR has no commits")
|
||||||
|
|
||||||
|
new_trees = []
|
||||||
|
parent = dest
|
||||||
|
for original in commits:
|
||||||
|
if len(original['parents']) != 1:
|
||||||
|
raise MergeError(
|
||||||
|
f"commits with multiple parents ({original['sha']}) can not be rebased, "
|
||||||
|
"either fix the branch to remove merges or merge without "
|
||||||
|
"rebasing")
|
||||||
|
|
||||||
|
new_trees.append(check(repo.merge_tree(parent, original['sha'])).stdout.strip())
|
||||||
|
parent = check(repo.commit_tree(
|
||||||
|
new_trees[-1],
|
||||||
|
'-p', parent,
|
||||||
|
'-p', original['sha'],
|
||||||
|
'-m', f'temp rebase {original["sha"]}',
|
||||||
|
)).stdout.strip()
|
||||||
|
|
||||||
|
mapping = {}
|
||||||
|
for original, t in zip(commits, new_trees):
|
||||||
|
authorship = check(repo.show('--no-patch', '--pretty="%an%n%ae%n%ai%n%cn%n%ce"', original['sha']))
|
||||||
|
author_name, author_email, author_date, committer_name, committer_email =\
|
||||||
|
authorship.stdout.splitlines()
|
||||||
|
|
||||||
|
c = check(repo.with_config(env={
|
||||||
|
**os.environ,
|
||||||
|
'GIT_AUTHOR_NAME': author_name,
|
||||||
|
'GIT_AUTHOR_EMAIL': author_email,
|
||||||
|
'GIT_AUTHOR_DATE': author_date,
|
||||||
|
'GIT_COMMITTER_NAME': committer_name,
|
||||||
|
'GIT_COMMITTER_EMAIL': committer_email,
|
||||||
|
'TZ': 'UTC',
|
||||||
|
}).commit_tree(t, p=dest, m=original['commit']['message']))\
|
||||||
|
.stdout.strip()
|
||||||
|
|
||||||
|
logger.debug('copied %s to %s (parent: %s)', original['sha'], c, dest)
|
||||||
|
dest = mapping[original['sha']] = c
|
||||||
|
|
||||||
|
return dest, mapping
|
||||||
|
|
||||||
|
def check(p: subprocess.CompletedProcess) -> subprocess.CompletedProcess:
|
||||||
|
if not p.returncode:
|
||||||
|
return p
|
||||||
|
|
||||||
|
_logger.info("rebase failed at %s\nstdout:\n%s\nstderr:\n%s", p.args, p.stdout, p.stderr)
|
||||||
|
raise MergeError(p.stderr or 'merge conflict')
|
||||||
|
|
||||||
|
|
||||||
@dataclasses.dataclass
|
@dataclasses.dataclass
|
||||||
class GitCommand(Generic[T]):
|
class GitCommand(Generic[T]):
|
||||||
|
@ -567,7 +567,16 @@ def stage_rebase_ff(pr: PullRequests, info: StagingSlice, target: str, commits:
|
|||||||
msg = pr._build_merge_message(commits[-1]['commit']['message'], related_prs=related_prs)
|
msg = pr._build_merge_message(commits[-1]['commit']['message'], related_prs=related_prs)
|
||||||
commits[-1]['commit']['message'] = str(msg)
|
commits[-1]['commit']['message'] = str(msg)
|
||||||
add_self_references(pr, commits[:-1])
|
add_self_references(pr, commits[:-1])
|
||||||
head, mapping = info.gh.rebase(pr.number, target, commits=commits)
|
head, mapping = info.repo.rebase(info.head, commits=commits)
|
||||||
|
|
||||||
|
# TODO: remove when we stop using tmp.
|
||||||
|
r = info.repo.with_config(text=True).check(False).push(
|
||||||
|
git.source_url(pr.repository, 'github'),
|
||||||
|
f'{head}:{target}'
|
||||||
|
)
|
||||||
|
if r.returncode:
|
||||||
|
raise exceptions.MergeError(pr, r.stderr)
|
||||||
|
|
||||||
pr.commits_map = json.dumps({**mapping, '': head})
|
pr.commits_map = json.dumps({**mapping, '': head})
|
||||||
return head
|
return head
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ import requests
|
|||||||
from lxml import html
|
from lxml import html
|
||||||
|
|
||||||
import odoo
|
import odoo
|
||||||
from utils import _simple_init, seen, re_matches, get_partner, Commit, pr_page, to_pr, part_of
|
from utils import _simple_init, seen, re_matches, get_partner, Commit, pr_page, to_pr, part_of, part_of2
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
@ -149,7 +149,7 @@ class TestCommitMessage:
|
|||||||
|
|
||||||
master = repo.commit('heads/master')
|
master = repo.commit('heads/master')
|
||||||
assert master.message == "simple commit message\n\ncloses {repo.name}#1"\
|
assert master.message == "simple commit message\n\ncloses {repo.name}#1"\
|
||||||
"\n\nSigned-off-by: {reviewer.formatted_email}"\
|
"\n\nSigned-off-by: {reviewer.formatted_email}\n"\
|
||||||
.format(repo=repo, reviewer=get_partner(env, users['reviewer']))
|
.format(repo=repo, reviewer=get_partner(env, users['reviewer']))
|
||||||
|
|
||||||
def test_commit_existing(self, env, repo, users, config):
|
def test_commit_existing(self, env, repo, users, config):
|
||||||
@ -174,7 +174,7 @@ class TestCommitMessage:
|
|||||||
master = repo.commit('heads/master')
|
master = repo.commit('heads/master')
|
||||||
# closes #1 is already present, should not modify message
|
# closes #1 is already present, should not modify message
|
||||||
assert master.message == "simple commit message that closes #1"\
|
assert master.message == "simple commit message that closes #1"\
|
||||||
"\n\nSigned-off-by: {reviewer.formatted_email}"\
|
"\n\nSigned-off-by: {reviewer.formatted_email}\n"\
|
||||||
.format(reviewer=get_partner(env, users['reviewer']))
|
.format(reviewer=get_partner(env, users['reviewer']))
|
||||||
|
|
||||||
def test_commit_other(self, env, repo, users, config):
|
def test_commit_other(self, env, repo, users, config):
|
||||||
@ -199,7 +199,7 @@ class TestCommitMessage:
|
|||||||
master = repo.commit('heads/master')
|
master = repo.commit('heads/master')
|
||||||
# closes on another repositoy, should modify the commit message
|
# closes on another repositoy, should modify the commit message
|
||||||
assert master.message == "simple commit message that closes odoo/enterprise#1\n\ncloses {repo.name}#1"\
|
assert master.message == "simple commit message that closes odoo/enterprise#1\n\ncloses {repo.name}#1"\
|
||||||
"\n\nSigned-off-by: {reviewer.formatted_email}"\
|
"\n\nSigned-off-by: {reviewer.formatted_email}\n"\
|
||||||
.format(repo=repo, reviewer=get_partner(env, users['reviewer']))
|
.format(repo=repo, reviewer=get_partner(env, users['reviewer']))
|
||||||
|
|
||||||
def test_commit_wrong_number(self, env, repo, users, config):
|
def test_commit_wrong_number(self, env, repo, users, config):
|
||||||
@ -224,7 +224,7 @@ class TestCommitMessage:
|
|||||||
master = repo.commit('heads/master')
|
master = repo.commit('heads/master')
|
||||||
# closes on another repositoy, should modify the commit message
|
# closes on another repositoy, should modify the commit message
|
||||||
assert master.message == "simple commit message that closes #11\n\ncloses {repo.name}#1"\
|
assert master.message == "simple commit message that closes #11\n\ncloses {repo.name}#1"\
|
||||||
"\n\nSigned-off-by: {reviewer.formatted_email}"\
|
"\n\nSigned-off-by: {reviewer.formatted_email}\n"\
|
||||||
.format(repo=repo, reviewer=get_partner(env, users['reviewer']))
|
.format(repo=repo, reviewer=get_partner(env, users['reviewer']))
|
||||||
|
|
||||||
def test_commit_delegate(self, env, repo, users, config):
|
def test_commit_delegate(self, env, repo, users, config):
|
||||||
@ -254,7 +254,7 @@ class TestCommitMessage:
|
|||||||
|
|
||||||
master = repo.commit('heads/master')
|
master = repo.commit('heads/master')
|
||||||
assert master.message == "simple commit message\n\ncloses {repo.name}#1"\
|
assert master.message == "simple commit message\n\ncloses {repo.name}#1"\
|
||||||
"\n\nSigned-off-by: {reviewer.formatted_email}"\
|
"\n\nSigned-off-by: {reviewer.formatted_email}\n"\
|
||||||
.format(repo=repo, reviewer=get_partner(env, users['other']))
|
.format(repo=repo, reviewer=get_partner(env, users['other']))
|
||||||
|
|
||||||
def test_commit_coauthored(self, env, repo, users, config):
|
def test_commit_coauthored(self, env, repo, users, config):
|
||||||
@ -292,7 +292,8 @@ Fixes a thing
|
|||||||
closes {repo.name}#1
|
closes {repo.name}#1
|
||||||
|
|
||||||
Signed-off-by: {reviewer.formatted_email}
|
Signed-off-by: {reviewer.formatted_email}
|
||||||
Co-authored-by: Bob <bob@example.com>""".format(
|
Co-authored-by: Bob <bob@example.com>
|
||||||
|
""".format(
|
||||||
repo=repo,
|
repo=repo,
|
||||||
reviewer=get_partner(env, users['reviewer'])
|
reviewer=get_partner(env, users['reviewer'])
|
||||||
)
|
)
|
||||||
@ -1628,8 +1629,8 @@ commits, I need to know how to merge it:
|
|||||||
# then compare to the dag version of the right graph
|
# then compare to the dag version of the right graph
|
||||||
nm2 = node('M2', node('M1', node('M0')))
|
nm2 = node('M2', node('M1', node('M0')))
|
||||||
reviewer = get_partner(env, users["reviewer"]).formatted_email
|
reviewer = get_partner(env, users["reviewer"]).formatted_email
|
||||||
nb1 = node(f'B1\n\ncloses {pr_id.display_name}\n\nSigned-off-by: {reviewer}',
|
nb1 = node(f'B1\n\ncloses {pr_id.display_name}\n\nSigned-off-by: {reviewer}\n',
|
||||||
node(part_of('B0', pr_id), nm2))
|
node(part_of2('B0', pr_id), nm2))
|
||||||
assert staging == nb1
|
assert staging == nb1
|
||||||
|
|
||||||
with repo:
|
with repo:
|
||||||
@ -1718,7 +1719,7 @@ commits, I need to know how to merge it:
|
|||||||
c0 = node('C0', m)
|
c0 = node('C0', m)
|
||||||
reviewer = get_partner(env, users["reviewer"]).formatted_email
|
reviewer = get_partner(env, users["reviewer"]).formatted_email
|
||||||
expected = node('gibberish\n\nblahblah\n\ncloses {}#{}'
|
expected = node('gibberish\n\nblahblah\n\ncloses {}#{}'
|
||||||
'\n\nSigned-off-by: {}'.format(repo.name, prx.number, reviewer), m, c0)
|
'\n\nSigned-off-by: {}\n'.format(repo.name, prx.number, reviewer), m, c0)
|
||||||
assert log_to_node(repo.log('heads/master')), expected
|
assert log_to_node(repo.log('heads/master')), expected
|
||||||
pr = env['runbot_merge.pull_requests'].search([
|
pr = env['runbot_merge.pull_requests'].search([
|
||||||
('repository.name', '=', repo.name),
|
('repository.name', '=', repo.name),
|
||||||
@ -1760,7 +1761,7 @@ commits, I need to know how to merge it:
|
|||||||
c0 = node('C0', m)
|
c0 = node('C0', m)
|
||||||
reviewer = get_partner(env, users["reviewer"]).formatted_email
|
reviewer = get_partner(env, users["reviewer"]).formatted_email
|
||||||
expected = node('gibberish\n\ncloses {}#{}'
|
expected = node('gibberish\n\ncloses {}#{}'
|
||||||
'\n\nSigned-off-by: {}'.format(repo.name, prx.number, reviewer), m, c0)
|
'\n\nSigned-off-by: {}\n'.format(repo.name, prx.number, reviewer), m, c0)
|
||||||
assert log_to_node(repo.log('heads/master')), expected
|
assert log_to_node(repo.log('heads/master')), expected
|
||||||
|
|
||||||
@pytest.mark.parametrize('separator', [
|
@pytest.mark.parametrize('separator', [
|
||||||
@ -1790,15 +1791,14 @@ commits, I need to know how to merge it:
|
|||||||
env.run_crons()
|
env.run_crons()
|
||||||
|
|
||||||
head = repo.commit('heads/master')
|
head = repo.commit('heads/master')
|
||||||
assert head.message == textwrap.dedent(f"""\
|
assert head.message == f"""\
|
||||||
title
|
title
|
||||||
|
|
||||||
first
|
first
|
||||||
|
|
||||||
closes {repo.name}#{pr.number}
|
closes {repo.name}#{pr.number}
|
||||||
|
|
||||||
Signed-off-by: {reviewer}
|
Signed-off-by: {reviewer}""", "should not contain the content which follows the thematic break"
|
||||||
""").strip(), "should not contain the content which follows the thematic break"
|
|
||||||
|
|
||||||
def test_pr_message_setex_title(self, repo, env, users, config):
|
def test_pr_message_setex_title(self, repo, env, users, config):
|
||||||
""" should not break on a proper SETEX-style title """
|
""" should not break on a proper SETEX-style title """
|
||||||
@ -1830,21 +1830,20 @@ removed
|
|||||||
env.run_crons()
|
env.run_crons()
|
||||||
|
|
||||||
head = repo.commit('heads/master')
|
head = repo.commit('heads/master')
|
||||||
assert head.message == textwrap.dedent(f"""\
|
assert head.message == f"""\
|
||||||
title
|
title
|
||||||
|
|
||||||
Title
|
Title
|
||||||
---
|
---
|
||||||
This is some text
|
This is some text
|
||||||
|
|
||||||
Title 2
|
Title 2
|
||||||
-------
|
-------
|
||||||
This is more text
|
This is more text
|
||||||
|
|
||||||
closes {repo.name}#{pr.number}
|
closes {repo.name}#{pr.number}
|
||||||
|
|
||||||
Signed-off-by: {reviewer}
|
Signed-off-by: {reviewer}""", "should not break the SETEX titles"
|
||||||
""").strip(), "should not break the SETEX titles"
|
|
||||||
|
|
||||||
def test_rebase_no_edit(self, repo, env, users, config):
|
def test_rebase_no_edit(self, repo, env, users, config):
|
||||||
""" Only the merge messages should be de-breaked
|
""" Only the merge messages should be de-breaked
|
||||||
@ -1867,17 +1866,17 @@ removed
|
|||||||
env.run_crons()
|
env.run_crons()
|
||||||
|
|
||||||
head = repo.commit('heads/master')
|
head = repo.commit('heads/master')
|
||||||
assert head.message == textwrap.dedent(f"""\
|
assert head.message == f"""\
|
||||||
Commit
|
Commit
|
||||||
|
|
||||||
first
|
first
|
||||||
***
|
***
|
||||||
second
|
second
|
||||||
|
|
||||||
closes {repo.name}#{pr.number}
|
closes {repo.name}#{pr.number}
|
||||||
|
|
||||||
Signed-off-by: {reviewer}
|
Signed-off-by: {reviewer}
|
||||||
""").strip(), "squashed / rebased messages should not be stripped"
|
""", "squashed / rebased messages should not be stripped"
|
||||||
|
|
||||||
def test_title_no_edit(self, repo, env, users, config):
|
def test_title_no_edit(self, repo, env, users, config):
|
||||||
"""The first line of a commit message should not be taken in account for
|
"""The first line of a commit message should not be taken in account for
|
||||||
@ -1909,13 +1908,15 @@ thing: thong
|
|||||||
|
|
||||||
closes {pr_id.display_name}
|
closes {pr_id.display_name}
|
||||||
|
|
||||||
Signed-off-by: {reviewer}"""
|
Signed-off-by: {reviewer}
|
||||||
|
"""
|
||||||
assert repo.commit(staging_head.parents[0]).message == f"""\
|
assert repo.commit(staging_head.parents[0]).message == f"""\
|
||||||
Some: thing
|
Some: thing
|
||||||
|
|
||||||
is odd
|
is odd
|
||||||
|
|
||||||
Part-of: {pr_id.display_name}"""
|
Part-of: {pr_id.display_name}
|
||||||
|
"""
|
||||||
|
|
||||||
def test_pr_mergehead(self, repo, env, config):
|
def test_pr_mergehead(self, repo, env, config):
|
||||||
""" if the head of the PR is a merge commit and one of the parents is
|
""" if the head of the PR is a merge commit and one of the parents is
|
||||||
@ -2741,8 +2742,8 @@ class TestBatching(object):
|
|||||||
|
|
||||||
staging = log_to_node(log)
|
staging = log_to_node(log)
|
||||||
reviewer = get_partner(env, users["reviewer"]).formatted_email
|
reviewer = get_partner(env, users["reviewer"]).formatted_email
|
||||||
expected = node('commit_PR2_00\n\ncloses {}#{}\n\nSigned-off-by: {}'.format(repo.name, pr2.number, reviewer),
|
expected = node('commit_PR2_00\n\ncloses {}#{}\n\nSigned-off-by: {}\n'.format(repo.name, pr2.number, reviewer),
|
||||||
node('commit_PR1_00\n\ncloses {}#{}\n\nSigned-off-by: {}'.format(repo.name, pr1.number, reviewer),
|
node('commit_PR1_00\n\ncloses {}#{}\n\nSigned-off-by: {}\n'.format(repo.name, pr1.number, reviewer),
|
||||||
node('initial')))
|
node('initial')))
|
||||||
assert staging == expected
|
assert staging == expected
|
||||||
|
|
||||||
|
@ -435,7 +435,7 @@ def test_merge_fail(env, project, repo_a, repo_b, users, config):
|
|||||||
)
|
)
|
||||||
env.run_crons()
|
env.run_crons()
|
||||||
|
|
||||||
s2 = to_pr(env, pr2a) | to_pr(env, pr2b)
|
pr2a_id, pr2b_id = s2 = to_pr(env, pr2a) | to_pr(env, pr2b)
|
||||||
st = env['runbot_merge.stagings'].search([])
|
st = env['runbot_merge.stagings'].search([])
|
||||||
assert set(st.batch_ids.prs.ids) == set(s2.ids)
|
assert set(st.batch_ids.prs.ids) == set(s2.ids)
|
||||||
|
|
||||||
@ -453,12 +453,14 @@ def test_merge_fail(env, project, repo_a, repo_b, users, config):
|
|||||||
c['commit']['message']
|
c['commit']['message']
|
||||||
for c in repo_a.log('heads/staging.master')
|
for c in repo_a.log('heads/staging.master')
|
||||||
] == [
|
] == [
|
||||||
"""commit_do-b-thing_00
|
f"""\
|
||||||
|
commit_do-b-thing_00
|
||||||
|
|
||||||
closes %s
|
closes {pr2a_id.display_name}
|
||||||
|
|
||||||
Related: %s
|
Related: {pr2b_id.display_name}
|
||||||
Signed-off-by: %s""" % (s2[0].display_name, s2[1].display_name, reviewer),
|
Signed-off-by: {reviewer}
|
||||||
|
""",
|
||||||
'initial'
|
'initial'
|
||||||
], "dummy commit + squash-merged PR commit + root commit"
|
], "dummy commit + squash-merged PR commit + root commit"
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user