mirror of
https://github.com/odoo/runbot.git
synced 2025-03-27 13:25:47 +07:00
[IMP] forwardbot: show FP PRs in reminder message
When posting a reminder that there are open / waiting forward ports on a source PR, also post *which* PRs those are. While at it, move the cron code in a proper python file (so we can use stuff from odoo.tools), and fix display_name so we can straight use display_name as a github ref' ({owner}/{repo}#{number}). This impacts log-grepping but it seems like an improvement nonetheless. Closes odoo/runbot#228
This commit is contained in:
parent
036ae3a8ee
commit
3ce3dd9569
@ -80,6 +80,13 @@ def pytest_addoption(parser):
|
|||||||
def pytest_report_header(config):
|
def pytest_report_header(config):
|
||||||
return 'Running against database ' + config.getoption('--db')
|
return 'Running against database ' + config.getoption('--db')
|
||||||
|
|
||||||
|
@pytest.fixture(scope='session', autouse=True)
|
||||||
|
def _set_socket_timeout():
|
||||||
|
""" Avoid unlimited wait on standard sockets during tests, this is mostly
|
||||||
|
an issue for non-trivial cron calls
|
||||||
|
"""
|
||||||
|
socket.setdefaulttimeout(60.0)
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
@pytest.fixture(scope="session")
|
||||||
def config(pytestconfig):
|
def config(pytestconfig):
|
||||||
""" Flat version of the pytest config file (pytest.ini), parses to a
|
""" Flat version of the pytest config file (pytest.ini), parses to a
|
||||||
|
@ -23,27 +23,9 @@
|
|||||||
|
|
||||||
<record model="ir.cron" id="reminder">
|
<record model="ir.cron" id="reminder">
|
||||||
<field name="name">Remind open PR</field>
|
<field name="name">Remind open PR</field>
|
||||||
<field name="model_id" ref="model_forwardport_updates"/>
|
<field name="model_id" ref="model_runbot_merge_pull_requests"/>
|
||||||
<field name="state">code</field>
|
<field name="state">code</field>
|
||||||
<field name="code">
|
<field name="code">model._reminder()</field>
|
||||||
default_delta = dateutil.relativedelta.relativedelta(days=3)
|
|
||||||
cutoff = env.context.get('forwardport_updated_before') or (datetime.datetime.now() - default_delta).strftime('%Y-%m-%d %H:%M:%S')
|
|
||||||
|
|
||||||
for pr in env['runbot_merge.pull_requests'].search([
|
|
||||||
# only FP PRs
|
|
||||||
('source_id', '!=', False),
|
|
||||||
# active
|
|
||||||
('state', 'not in', ['merged', 'closed']),
|
|
||||||
# last updated more than a week ago
|
|
||||||
('write_date', '<', cutoff),
|
|
||||||
]).mapped('source_id'):
|
|
||||||
env['runbot_merge.pull_requests.feedback'].create({
|
|
||||||
'repository': pr.repository.id,
|
|
||||||
'pull_request': pr.number,
|
|
||||||
'message': "This pull request has forward-port PRs awaiting action",
|
|
||||||
'token_field': 'fp_github_token',
|
|
||||||
})
|
|
||||||
</field>
|
|
||||||
<field name="interval_number">1</field>
|
<field name="interval_number">1</field>
|
||||||
<field name="interval_type">days</field>
|
<field name="interval_type">days</field>
|
||||||
<field name="numbercall">-1</field>
|
<field name="numbercall">-1</field>
|
||||||
|
@ -13,6 +13,7 @@ it up), ...
|
|||||||
"""
|
"""
|
||||||
import base64
|
import base64
|
||||||
import contextlib
|
import contextlib
|
||||||
|
import datetime
|
||||||
import itertools
|
import itertools
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
@ -22,15 +23,17 @@ import re
|
|||||||
import subprocess
|
import subprocess
|
||||||
import tempfile
|
import tempfile
|
||||||
|
|
||||||
|
import dateutil
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
from odoo import _, models, fields, api
|
from odoo import _, models, fields, api
|
||||||
from odoo.exceptions import UserError
|
from odoo.exceptions import UserError
|
||||||
from odoo.tools import topological_sort
|
from odoo.tools import topological_sort, groupby
|
||||||
from odoo.tools.appdirs import user_cache_dir
|
from odoo.tools.appdirs import user_cache_dir
|
||||||
from odoo.addons.runbot_merge import utils
|
from odoo.addons.runbot_merge import utils
|
||||||
from odoo.addons.runbot_merge.models.pull_requests import RPLUS
|
from odoo.addons.runbot_merge.models.pull_requests import RPLUS
|
||||||
|
|
||||||
|
DEFAULT_DELTA = dateutil.relativedelta.relativedelta(days=3)
|
||||||
|
|
||||||
_logger = logging.getLogger('odoo.addons.forwardport')
|
_logger = logging.getLogger('odoo.addons.forwardport')
|
||||||
|
|
||||||
@ -228,8 +231,8 @@ class PullRequests(models.Model):
|
|||||||
])
|
])
|
||||||
if self.parent_id:
|
if self.parent_id:
|
||||||
msg = "Sorry, forward-port limit can only be set on an origin PR" \
|
msg = "Sorry, forward-port limit can only be set on an origin PR" \
|
||||||
" (#%d here) before it's merged and forward-ported." % (
|
" (%s here) before it's merged and forward-ported." % (
|
||||||
self._get_root().number
|
self._get_root().display_name
|
||||||
)
|
)
|
||||||
elif self.state in ['merged', 'closed']:
|
elif self.state in ['merged', 'closed']:
|
||||||
msg = "Sorry, forward-port limit can only be set before the PR is merged."
|
msg = "Sorry, forward-port limit can only be set before the PR is merged."
|
||||||
@ -417,16 +420,16 @@ class PullRequests(models.Model):
|
|||||||
target = base._find_next_target(ref)
|
target = base._find_next_target(ref)
|
||||||
if target is None:
|
if target is None:
|
||||||
_logger.info(
|
_logger.info(
|
||||||
"Will not forward-port %s#%s: no next target",
|
"Will not forward-port %s: no next target",
|
||||||
ref.repository.name, ref.number,
|
ref.display_name,
|
||||||
)
|
)
|
||||||
return # QUESTION: do the prs need to be updated?
|
return # QUESTION: do the prs need to be updated?
|
||||||
|
|
||||||
proj = self.mapped('target.project_id')
|
proj = self.mapped('target.project_id')
|
||||||
if not proj.fp_github_token:
|
if not proj.fp_github_token:
|
||||||
_logger.warning(
|
_logger.warning(
|
||||||
"Can not forward-port %s#%s: no token on project %s",
|
"Can not forward-port %s: no token on project %s",
|
||||||
ref.repository.name, ref.number,
|
ref.display_name,
|
||||||
proj.name
|
proj.name
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
@ -471,7 +474,7 @@ class PullRequests(models.Model):
|
|||||||
message = ''
|
message = ''
|
||||||
root = pr._get_root()
|
root = pr._get_root()
|
||||||
message += '\n'.join(
|
message += '\n'.join(
|
||||||
"Forward-Port-Of: %s#%s" % (p.repository.name, p.number)
|
"Forward-Port-Of: %s" % p.display_name
|
||||||
for p in root | source
|
for p in root | source
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -539,7 +542,7 @@ In the former case, you may want to edit this PR message as well.
|
|||||||
""" % (h, source.number, sout, serr)
|
""" % (h, source.number, sout, serr)
|
||||||
elif base._find_next_target(new_pr) is None:
|
elif base._find_next_target(new_pr) is None:
|
||||||
ancestors = "".join(
|
ancestors = "".join(
|
||||||
"* %s#%d\n" % (p.repository.name, p.number)
|
"* %s\n" % p.display_name
|
||||||
for p in pr._iter_ancestors()
|
for p in pr._iter_ancestors()
|
||||||
if p.parent_id
|
if p.parent_id
|
||||||
)
|
)
|
||||||
@ -767,6 +770,26 @@ stderr:
|
|||||||
repo.config('--add', 'remote.origin.fetch', '+refs/pull/*/head:refs/heads/pull/*')
|
repo.config('--add', 'remote.origin.fetch', '+refs/pull/*/head:refs/heads/pull/*')
|
||||||
return repo
|
return repo
|
||||||
|
|
||||||
|
def _reminder(self):
|
||||||
|
cutoff = self.env.context.get('forwardport_updated_before') or fields.Datetime.to_string(datetime.datetime.now() - DEFAULT_DELTA)
|
||||||
|
|
||||||
|
for source, prs in groupby(self.env['runbot_merge.pull_requests'].search([
|
||||||
|
# only FP PRs
|
||||||
|
('source_id', '!=', False),
|
||||||
|
# active
|
||||||
|
('state', 'not in', ['merged', 'closed']),
|
||||||
|
# last updated more than <cutoff> ago
|
||||||
|
('write_date', '<', cutoff),
|
||||||
|
]), lambda p: p.source_id):
|
||||||
|
self.env['runbot_merge.pull_requests.feedback'].create({
|
||||||
|
'repository': source.repository.id,
|
||||||
|
'pull_request': source.number,
|
||||||
|
'message': "This pull request has forward-port PRs awaiting action (not merged or closed): %s" % ', '.join(
|
||||||
|
pr.display_name for pr in sorted(prs, key=lambda p: p.number)
|
||||||
|
),
|
||||||
|
'token_field': 'fp_github_token',
|
||||||
|
})
|
||||||
|
|
||||||
class Stagings(models.Model):
|
class Stagings(models.Model):
|
||||||
_inherit = 'runbot_merge.stagings'
|
_inherit = 'runbot_merge.stagings'
|
||||||
|
|
||||||
|
@ -255,6 +255,6 @@ More info at https://github.com/odoo/odoo/wiki/Mergebot#forward-port
|
|||||||
"""),
|
"""),
|
||||||
(users['reviewer'], bot_name + ' up to b'),
|
(users['reviewer'], bot_name + ' up to b'),
|
||||||
(bot_name, "Sorry, forward-port limit can only be set on an origin PR"
|
(bot_name, "Sorry, forward-port limit can only be set on an origin PR"
|
||||||
" (#%d here) before it's merged and forward-ported." % p1.number
|
" (%s here) before it's merged and forward-ported." % p1.display_name
|
||||||
),
|
),
|
||||||
]
|
]
|
@ -109,14 +109,15 @@ def test_straightforward_flow(env, config, make_repo, users):
|
|||||||
env.run_crons()
|
env.run_crons()
|
||||||
env.run_crons('forwardport.reminder', 'runbot_merge.feedback_cron', context={'forwardport_updated_before': FAKE_PREV_WEEK})
|
env.run_crons('forwardport.reminder', 'runbot_merge.feedback_cron', context={'forwardport_updated_before': FAKE_PREV_WEEK})
|
||||||
|
|
||||||
|
pr0_, pr1_, pr2 = env['runbot_merge.pull_requests'].search([], order='number')
|
||||||
|
|
||||||
assert pr.comments == [
|
assert pr.comments == [
|
||||||
(users['reviewer'], 'hansen r+ rebase-ff'),
|
(users['reviewer'], 'hansen r+ rebase-ff'),
|
||||||
(users['user'], 'Merge method set to rebase and fast-forward'),
|
(users['user'], 'Merge method set to rebase and fast-forward'),
|
||||||
(users['user'], re_matches(r'Merged at [0-9a-f]{40}, thanks!')),
|
(users['user'], re_matches(r'Merged at [0-9a-f]{40}, thanks!')),
|
||||||
(users['user'], 'This pull request has forward-port PRs awaiting action'),
|
(users['user'], 'This pull request has forward-port PRs awaiting action (not merged or closed): ' + ', '.join((pr1 | pr2).mapped('display_name'))),
|
||||||
]
|
]
|
||||||
|
|
||||||
pr0_, pr1_, pr2 = env['runbot_merge.pull_requests'].search([], order='number')
|
|
||||||
assert pr0_ == pr0
|
assert pr0_ == pr0
|
||||||
assert pr1_ == pr1
|
assert pr1_ == pr1
|
||||||
assert pr1.parent_id == pr1.source_id == pr0
|
assert pr1.parent_id == pr1.source_id == pr0
|
||||||
@ -136,7 +137,7 @@ def test_straightforward_flow(env, config, make_repo, users):
|
|||||||
(users['user'], """\
|
(users['user'], """\
|
||||||
Ping @%s, @%s
|
Ping @%s, @%s
|
||||||
This PR targets c and is the last of the forward-port chain containing:
|
This PR targets c and is the last of the forward-port chain containing:
|
||||||
* %s#%d
|
* %s
|
||||||
|
|
||||||
To merge the full chain, say
|
To merge the full chain, say
|
||||||
> @%s r+
|
> @%s r+
|
||||||
@ -144,7 +145,7 @@ To merge the full chain, say
|
|||||||
More info at https://github.com/odoo/odoo/wiki/Mergebot#forward-port
|
More info at https://github.com/odoo/odoo/wiki/Mergebot#forward-port
|
||||||
""" % (
|
""" % (
|
||||||
users['other'], users['reviewer'],
|
users['other'], users['reviewer'],
|
||||||
pr1.repository.name, pr1.number,
|
pr1.display_name,
|
||||||
project.fp_github_name
|
project.fp_github_name
|
||||||
)),
|
)),
|
||||||
]
|
]
|
||||||
@ -594,8 +595,8 @@ def test_empty(env, config, make_repo, users):
|
|||||||
assert pr1.comments == [
|
assert pr1.comments == [
|
||||||
(users['reviewer'], 'hansen r+'),
|
(users['reviewer'], 'hansen r+'),
|
||||||
(users['user'], re_matches(r'Merged at [0-9a-f]{40}, thanks!')),
|
(users['user'], re_matches(r'Merged at [0-9a-f]{40}, thanks!')),
|
||||||
(users['other'], 'This pull request has forward-port PRs awaiting action'),
|
(users['other'], 'This pull request has forward-port PRs awaiting action (not merged or closed): ' + fail_id.display_name),
|
||||||
(users['other'], 'This pull request has forward-port PRs awaiting action'),
|
(users['other'], 'This pull request has forward-port PRs awaiting action (not merged or closed): ' + fail_id.display_name),
|
||||||
], "each cron run should trigger a new message on the ancestor"
|
], "each cron run should trigger a new message on the ancestor"
|
||||||
# check that this stops if we close the PR
|
# check that this stops if we close the PR
|
||||||
with prod:
|
with prod:
|
||||||
@ -604,8 +605,8 @@ def test_empty(env, config, make_repo, users):
|
|||||||
assert pr1.comments == [
|
assert pr1.comments == [
|
||||||
(users['reviewer'], 'hansen r+'),
|
(users['reviewer'], 'hansen r+'),
|
||||||
(users['user'], re_matches(r'Merged at [0-9a-f]{40}, thanks!')),
|
(users['user'], re_matches(r'Merged at [0-9a-f]{40}, thanks!')),
|
||||||
(users['other'], 'This pull request has forward-port PRs awaiting action'),
|
(users['other'], 'This pull request has forward-port PRs awaiting action (not merged or closed): ' + fail_id.display_name),
|
||||||
(users['other'], 'This pull request has forward-port PRs awaiting action'),
|
(users['other'], 'This pull request has forward-port PRs awaiting action (not merged or closed): ' + fail_id.display_name),
|
||||||
]
|
]
|
||||||
|
|
||||||
def test_partially_empty(env, config, make_repo):
|
def test_partially_empty(env, config, make_repo):
|
||||||
|
@ -528,7 +528,7 @@ class PullRequests(models.Model):
|
|||||||
|
|
||||||
def name_get(self):
|
def name_get(self):
|
||||||
return {
|
return {
|
||||||
p.id: '%s:%s' % (p.repository.name, p.number)
|
p.id: '%s#%s' % (p.repository.name, p.number)
|
||||||
for p in self
|
for p in self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -540,7 +540,7 @@ class PullRequests(models.Model):
|
|||||||
else:
|
else:
|
||||||
separator = 's '
|
separator = 's '
|
||||||
return '<pull_request%s%s>' % (separator, ' '.join(
|
return '<pull_request%s%s>' % (separator, ' '.join(
|
||||||
'{0.id} ({0.repository.name}:{0.number})'.format(p)
|
'{0.id} ({0.display_name})'.format(p)
|
||||||
for p in self
|
for p in self
|
||||||
))
|
))
|
||||||
|
|
||||||
@ -988,7 +988,7 @@ class PullRequests(models.Model):
|
|||||||
'pull_request': r.number,
|
'pull_request': r.number,
|
||||||
'message': "Linked pull request(s) {} not ready. Linked PRs are not staged until all of them are ready.".format(
|
'message': "Linked pull request(s) {} not ready. Linked PRs are not staged until all of them are ready.".format(
|
||||||
', '.join(map(
|
', '.join(map(
|
||||||
'{0.repository.name}#{0.number}'.format,
|
'{0.display_name}'.format,
|
||||||
unready
|
unready
|
||||||
))
|
))
|
||||||
)
|
)
|
||||||
@ -1032,7 +1032,7 @@ class PullRequests(models.Model):
|
|||||||
repository=self.repository.name.replace('/', '\\/')
|
repository=self.repository.name.replace('/', '\\/')
|
||||||
)
|
)
|
||||||
if not re.search(pattern, m.body):
|
if not re.search(pattern, m.body):
|
||||||
m.body += '\n\ncloses {pr.repository.name}#{pr.number}'.format(pr=self)
|
m.body += '\n\ncloses {pr.display_name}'.format(pr=self)
|
||||||
|
|
||||||
if self.reviewed_by:
|
if self.reviewed_by:
|
||||||
m.headers.add('signed-off-by', self.reviewed_by.formatted_email)
|
m.headers.add('signed-off-by', self.reviewed_by.formatted_email)
|
||||||
|
Loading…
Reference in New Issue
Block a user