2018-03-14 16:37:46 +07:00
|
|
|
import datetime
|
2018-10-17 19:18:49 +07:00
|
|
|
import itertools
|
2018-09-17 16:04:31 +07:00
|
|
|
import json
|
2021-01-12 18:24:34 +07:00
|
|
|
import textwrap
|
2022-02-07 18:00:31 +07:00
|
|
|
import time
|
2024-07-31 14:40:53 +07:00
|
|
|
from typing import Callable
|
2018-10-17 19:18:49 +07:00
|
|
|
from unittest import mock
|
2018-03-14 16:37:46 +07:00
|
|
|
|
|
|
|
import pytest
|
2022-12-07 19:25:08 +07:00
|
|
|
import requests
|
2023-08-10 18:21:21 +07:00
|
|
|
from lxml import html
|
2018-03-14 16:37:46 +07:00
|
|
|
|
|
|
|
import odoo
|
2024-10-07 13:06:03 +07:00
|
|
|
from utils import _simple_init, seen, matches, get_partner, Commit, pr_page, to_pr, part_of, ensure_one, read_tracking_value
|
|
|
|
|
2021-08-09 12:55:38 +07:00
|
|
|
|
[ADD] *: per-repository webhook secret
Currently webhook secrets are configured per *project* which is an
issue both because different repositories may have different
administrators and thus creates safety concerns, and because multiple
repositories can feed into different projects (e.g. on mergebot,
odoo-dev/odoo is both an ancillary repository to the main RD project,
and the main repository to the minor / legacy master-wowl
project). This means it can be necessary to have multiple projects
share the same secret as well, this then mandates the secret for more
repositories per (1).
This is a pain in the ass, so just detach secrets from projects and
link them *only* to repositories, it's cleaner and easier to manage
and set up progressively.
This requires a lot of changes to the tests, as they all need to
correctly configure the signaling.
For `runbot_merge` there was *some* setup sharing already via the
module-level `repo` fixtures`, those were merged into a conftest-level
fixture which could handle the signaling setup. A few tests which
unnecessarily set up repositories ad-hoc were also moved to the
fixture. But for most of the ad-hoc setup in `runbot_merge`, as well
as `forwardport` where it's all ad-hoc, events sources setup was just
appended as is. This should probably be cleaned up at one point, with
the various requirements collected and organised into a small set of
fixtures doing the job more uniformly.
Fixes #887
2024-06-06 16:07:57 +07:00
|
|
|
@pytest.fixture(autouse=True)
|
2024-07-31 14:40:53 +07:00
|
|
|
def _configure_statuses(request, project, repo):
|
|
|
|
if 'defaultstatuses' not in request.keywords:
|
|
|
|
project.repo_ids.required_statuses = 'legal/cla,ci/runbot'
|
2018-03-14 16:37:46 +07:00
|
|
|
|
2024-06-03 17:59:24 +07:00
|
|
|
@pytest.fixture(autouse=True, params=["statuses", "rpc"])
|
|
|
|
def stagings(request, env, project, repo):
|
|
|
|
"""Hook in support for validating stagings via RPC calls instead of CI
|
|
|
|
webhooks. Transparent for the tests as long as they send statuses to
|
|
|
|
symbolic refs (branch names) rather than commits, although commits *would*
|
|
|
|
probably be doable (look up the head for the commit, then what staging it's
|
|
|
|
part of)
|
|
|
|
"""
|
|
|
|
if request.param == "statuses":
|
|
|
|
yield
|
|
|
|
else:
|
|
|
|
env['res.users'].browse([env._uid]).write({
|
|
|
|
"groups_id": [(4, env.ref("runbot_merge.status").id, {})]
|
|
|
|
})
|
|
|
|
project.write({
|
|
|
|
"staging_rpc": True,
|
|
|
|
"staging_statuses": False,
|
|
|
|
})
|
|
|
|
RepoType = type(repo)
|
|
|
|
# apparently side_effect + wraps on unbound method don't work correctly,
|
|
|
|
# the wrapped method does get called when returning DEFAULT but *the
|
|
|
|
# instance (subject) is not sent along for the ride* so the call fails.
|
|
|
|
post_status = RepoType.post_status
|
|
|
|
def _post_status(repo, ref, status, context='default', **kw):
|
|
|
|
if not ref.startswith(('staging.', 'heads/staging.')):
|
|
|
|
return post_status(repo, ref, status, context, **kw)
|
|
|
|
|
|
|
|
c = repo.commit(ref)
|
|
|
|
branchname = ref.removeprefix('staging.').removeprefix('heads/staging.')
|
|
|
|
env['runbot_merge.stagings'].search([('target.name', '=', branchname)])\
|
|
|
|
.post_status(c.id, context, status, **kw)
|
|
|
|
|
|
|
|
with mock.patch.object(RepoType, "post_status", _post_status):
|
|
|
|
yield
|
2018-03-14 16:37:46 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
def test_trivial_flow(env, repo, page, users, config):
|
2018-03-14 16:37:46 +07:00
|
|
|
# create base branch
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
2022-07-29 17:37:23 +07:00
|
|
|
[m] = repo.make_commits(None, Commit("initial", tree={'a': 'some content'}), ref='heads/master')
|
2018-03-14 16:37:46 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
# create PR with 2 commits
|
2022-07-29 17:37:23 +07:00
|
|
|
_, c1 = repo.make_commits(
|
|
|
|
m,
|
|
|
|
Commit('replace file contents', tree={'a': 'some other content'}),
|
|
|
|
Commit('add file', tree={'b': 'a second file'}),
|
|
|
|
ref='heads/other'
|
|
|
|
)
|
|
|
|
pr = repo.make_pr(title="gibberish", body="blahblah", target='master', head='other')
|
2018-03-14 16:37:46 +07:00
|
|
|
|
[FIX] runbot_merge: tracking message author on PullRequest events
d4fa1fd35315d330566e37f515a937f722859ef7 added tracking to changes
from *comments* (as well as a few hacks around authorship transfer),
however it missed two things:
First, it set the `change-author` during comments handling only, so
changes from the `PullRequest` hook e.g. open, synchronise, close,
edit, don't get attributed to their actual source, and instead just
fall back to uid(1). This is easy enough to fix as the `sender` is
always provided, that can be resolved to a partner which is then set
as the author of whatever changes happen.
Second, I actually missed one of the message hooks: there's both
`_message_log` and `_message_log_batch` and they don't call one
another, so both have to be overridden in order for tracking to be
consistent. In this case, specifically, the *creation* of a tracked
object goes through `_message_log_batch` (since that's a very generic
message and so works on every tracked object created during the
transaction... even though batch has a message per record anyway...)
while *updates* go through `_message_log`.
Fixes #895
2024-06-21 21:33:44 +07:00
|
|
|
[c2] = repo.make_commits(
|
|
|
|
'other',
|
|
|
|
Commit('forgot a bit', tree={'whee': 'kjfdsh'}),
|
|
|
|
ref='heads/other',
|
|
|
|
make=False,
|
|
|
|
)
|
|
|
|
|
2021-07-23 20:45:23 +07:00
|
|
|
pr_id = to_pr(env, pr)
|
2020-11-17 21:21:21 +07:00
|
|
|
assert pr_id.state == 'opened'
|
2019-10-10 14:22:12 +07:00
|
|
|
env.run_crons()
|
2020-11-17 21:21:21 +07:00
|
|
|
assert pr.comments == [seen(env, pr, users)]
|
2022-07-29 17:37:23 +07:00
|
|
|
|
|
|
|
pr_dashboard = pr_page(page, pr)
|
|
|
|
s = pr_dashboard.cssselect('.alert-info > ul > li')
|
2020-11-17 21:21:21 +07:00
|
|
|
assert [it.get('class') for it in s] == ['fail', 'fail', ''],\
|
|
|
|
"merge method unset, review missing, no CI"
|
2022-07-29 17:37:23 +07:00
|
|
|
assert dict(zip(
|
|
|
|
[e.text_content() for e in pr_dashboard.cssselect('dl.runbot-merge-fields dt')],
|
|
|
|
[e.text_content() for e in pr_dashboard.cssselect('dl.runbot-merge-fields dd')],
|
|
|
|
)) == {
|
|
|
|
'label': f"{config['github']['owner']}:other",
|
[FIX] runbot_merge: tracking message author on PullRequest events
d4fa1fd35315d330566e37f515a937f722859ef7 added tracking to changes
from *comments* (as well as a few hacks around authorship transfer),
however it missed two things:
First, it set the `change-author` during comments handling only, so
changes from the `PullRequest` hook e.g. open, synchronise, close,
edit, don't get attributed to their actual source, and instead just
fall back to uid(1). This is easy enough to fix as the `sender` is
always provided, that can be resolved to a partner which is then set
as the author of whatever changes happen.
Second, I actually missed one of the message hooks: there's both
`_message_log` and `_message_log_batch` and they don't call one
another, so both have to be overridden in order for tracking to be
consistent. In this case, specifically, the *creation* of a tracked
object goes through `_message_log_batch` (since that's a very generic
message and so works on every tracked object created during the
transaction... even though batch has a message per record anyway...)
while *updates* go through `_message_log`.
Fixes #895
2024-06-21 21:33:44 +07:00
|
|
|
'head': c2,
|
2022-07-29 17:37:23 +07:00
|
|
|
}
|
2018-03-14 16:37:46 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
[FIX] runbot_merge: tracking message author on PullRequest events
d4fa1fd35315d330566e37f515a937f722859ef7 added tracking to changes
from *comments* (as well as a few hacks around authorship transfer),
however it missed two things:
First, it set the `change-author` during comments handling only, so
changes from the `PullRequest` hook e.g. open, synchronise, close,
edit, don't get attributed to their actual source, and instead just
fall back to uid(1). This is easy enough to fix as the `sender` is
always provided, that can be resolved to a partner which is then set
as the author of whatever changes happen.
Second, I actually missed one of the message hooks: there's both
`_message_log` and `_message_log_batch` and they don't call one
another, so both have to be overridden in order for tracking to be
consistent. In this case, specifically, the *creation* of a tracked
object goes through `_message_log_batch` (since that's a very generic
message and so works on every tracked object created during the
transaction... even though batch has a message per record anyway...)
while *updates* go through `_message_log`.
Fixes #895
2024-06-21 21:33:44 +07:00
|
|
|
repo.post_status(c2, 'success', 'legal/cla')
|
|
|
|
repo.post_status(c2, 'success', 'ci/runbot')
|
2019-10-10 14:22:12 +07:00
|
|
|
env.run_crons()
|
2020-11-17 21:21:21 +07:00
|
|
|
assert pr_id.state == 'validated'
|
|
|
|
|
|
|
|
s = pr_page(page, pr).cssselect('.alert-info > ul > li')
|
|
|
|
assert [it.get('class') for it in s] == ['fail', 'fail', 'ok'],\
|
|
|
|
"merge method unset, review missing, CI"
|
|
|
|
statuses = [
|
|
|
|
(l.find('a').text.split(':')[0], l.get('class').strip())
|
|
|
|
for l in s[2].cssselect('ul li')
|
|
|
|
]
|
|
|
|
assert statuses == [('legal/cla', 'ok'), ('ci/runbot', 'ok')]
|
2019-03-05 14:01:38 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
2020-11-17 21:21:21 +07:00
|
|
|
pr.post_comment('hansen r+ rebase-merge', config['role_reviewer']['token'])
|
|
|
|
assert pr_id.state == 'ready'
|
2018-03-14 16:37:46 +07:00
|
|
|
|
2018-03-28 21:43:48 +07:00
|
|
|
# can't check labels here as running the cron will stage it
|
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
env.run_crons()
|
2020-11-17 21:21:21 +07:00
|
|
|
assert pr_id.staging_id
|
|
|
|
assert pr_page(page, pr).cssselect('.alert-primary')
|
2018-03-14 16:37:46 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
2024-06-03 17:59:24 +07:00
|
|
|
repo.post_status('staging.master', 'success', 'ci/runbot', target_url='http://foo.com/pog')
|
|
|
|
repo.post_status('staging.master', 'success', 'legal/cla')
|
2019-10-10 14:22:12 +07:00
|
|
|
# the should not block the merge because it's not part of the requirements
|
2024-06-03 17:59:24 +07:00
|
|
|
repo.post_status('staging.master', 'failure', 'ci/lint', target_url='http://ignored.com/whocares')
|
2019-03-05 14:01:38 +07:00
|
|
|
# need to store this because after the crons have run the staging will
|
|
|
|
# have succeeded and been disabled
|
2020-11-17 21:21:21 +07:00
|
|
|
st = pr_id.staging_id
|
2019-10-10 14:22:12 +07:00
|
|
|
env.run_crons()
|
2018-10-19 22:24:01 +07:00
|
|
|
|
2021-07-23 20:45:23 +07:00
|
|
|
assert {tuple(t) for t in st.statuses} == {
|
2018-10-19 22:24:01 +07:00
|
|
|
(repo.name, 'legal/cla', 'success', ''),
|
|
|
|
(repo.name, 'ci/runbot', 'success', 'http://foo.com/pog'),
|
|
|
|
(repo.name, 'ci/lint', 'failure', 'http://ignored.com/whocares'),
|
|
|
|
}
|
2019-03-05 14:01:38 +07:00
|
|
|
|
2018-10-19 22:24:01 +07:00
|
|
|
p = html.fromstring(page('/runbot_merge'))
|
2022-11-30 18:44:25 +07:00
|
|
|
s = p.cssselect('.staging div.dropdown a')
|
|
|
|
assert len(s) == 2, "not logged so only *required* statuses"
|
|
|
|
for e, status in zip(s, ['legal/cla', 'ci/runbot']):
|
|
|
|
assert set(e.classes) == {'dropdown-item', 'bg-success'}
|
|
|
|
assert e.text_content().strip() == f'{repo.name}: {status}'
|
2018-10-19 22:24:01 +07:00
|
|
|
|
2019-03-05 14:01:38 +07:00
|
|
|
assert st.state == 'success'
|
2020-11-17 21:21:21 +07:00
|
|
|
assert pr_id.state == 'merged'
|
|
|
|
assert pr_page(page, pr).cssselect('.alert-success')
|
2018-03-14 16:37:46 +07:00
|
|
|
|
|
|
|
master = repo.commit('heads/master')
|
2018-08-28 20:42:28 +07:00
|
|
|
# with default-rebase, only one parent is "known"
|
|
|
|
assert master.parents[0] == m
|
2018-06-05 15:10:32 +07:00
|
|
|
assert repo.read_tree(master) == {
|
2019-08-23 21:16:30 +07:00
|
|
|
'a': 'some other content',
|
|
|
|
'b': 'a second file',
|
[FIX] runbot_merge: tracking message author on PullRequest events
d4fa1fd35315d330566e37f515a937f722859ef7 added tracking to changes
from *comments* (as well as a few hacks around authorship transfer),
however it missed two things:
First, it set the `change-author` during comments handling only, so
changes from the `PullRequest` hook e.g. open, synchronise, close,
edit, don't get attributed to their actual source, and instead just
fall back to uid(1). This is easy enough to fix as the `sender` is
always provided, that can be resolved to a partner which is then set
as the author of whatever changes happen.
Second, I actually missed one of the message hooks: there's both
`_message_log` and `_message_log_batch` and they don't call one
another, so both have to be overridden in order for tracking to be
consistent. In this case, specifically, the *creation* of a tracked
object goes through `_message_log_batch` (since that's a very generic
message and so works on every tracked object created during the
transaction... even though batch has a message per record anyway...)
while *updates* go through `_message_log`.
Fixes #895
2024-06-21 21:33:44 +07:00
|
|
|
'whee': 'kjfdsh',
|
2018-03-14 16:37:46 +07:00
|
|
|
}
|
2018-11-22 00:43:05 +07:00
|
|
|
assert master.message == "gibberish\n\nblahblah\n\ncloses {repo.name}#1"\
|
|
|
|
"\n\nSigned-off-by: {reviewer.formatted_email}"\
|
|
|
|
.format(repo=repo, reviewer=get_partner(env, users['reviewer']))
|
2018-09-13 15:30:47 +07:00
|
|
|
|
2024-08-07 19:14:27 +07:00
|
|
|
def get_tracking_values(record):
|
[MERGE] bot from 16.0 to 17.0
Broken (can't run odoo at all):
- In Odoo 17.0, the `pre_init_hook` takes an env, not a cursor, update
`_check_citext`.
- Odoo 17.0 rejects `@attrs` and doesn't say where they are or how to
update them, fun, hunt down `attrs={'invisible': ...` and try to fix
them.
- Odoo 17.0 warns on non-multi creates, update them, most were very
reasonable, one very wasn't.
Test failures:
- Odoo 17.0 deprecates `name_get` and doesn't use it as a *source*
anymore, replace overrides by overrides to `_compute_display_name`.
- Multiple tracking changes:
- `_track_set_author` takes a `Partner` not an id.
- `_message_compute_author` still requires overriding in order to
handle record creation, which in standard doesn't support author
overriding.
- `mail.tracking.value.field_type` has been removed, the field type
now needs to be retrieved from the `field_id`.
- Some tracking ordering have changed and require adjusting a few
tests.
Also added a few flushes before SQL queries which are not (obviously
at least) at the start of a cron or controller, no test failure
observed but better safe than sorry (probably).
2024-08-12 18:13:03 +07:00
|
|
|
field_type = record.field_id.ttype
|
|
|
|
if not isinstance(field_type, str):
|
|
|
|
raise TypeError(f"{field_type!r} can't be a field type")
|
|
|
|
|
|
|
|
if field_type in ('integer', 'float', 'char', 'text', 'monetary', 'datetime'):
|
|
|
|
return record[f'old_value_{field_type}'], record[f'new_value_{field_type}']
|
|
|
|
elif field_type == 'date':
|
2024-08-07 19:14:27 +07:00
|
|
|
v1, v2 = record.old_value_datetime, record.new_value_datetime
|
|
|
|
return v1 and v1[:10], v2 and v2[:10]
|
[MERGE] bot from 16.0 to 17.0
Broken (can't run odoo at all):
- In Odoo 17.0, the `pre_init_hook` takes an env, not a cursor, update
`_check_citext`.
- Odoo 17.0 rejects `@attrs` and doesn't say where they are or how to
update them, fun, hunt down `attrs={'invisible': ...` and try to fix
them.
- Odoo 17.0 warns on non-multi creates, update them, most were very
reasonable, one very wasn't.
Test failures:
- Odoo 17.0 deprecates `name_get` and doesn't use it as a *source*
anymore, replace overrides by overrides to `_compute_display_name`.
- Multiple tracking changes:
- `_track_set_author` takes a `Partner` not an id.
- `_message_compute_author` still requires overriding in order to
handle record creation, which in standard doesn't support author
overriding.
- `mail.tracking.value.field_type` has been removed, the field type
now needs to be retrieved from the `field_id`.
- Some tracking ordering have changed and require adjusting a few
tests.
Also added a few flushes before SQL queries which are not (obviously
at least) at the start of a cron or controller, no test failure
observed but better safe than sorry (probably).
2024-08-12 18:13:03 +07:00
|
|
|
elif field_type == 'boolean':
|
2024-08-07 19:14:27 +07:00
|
|
|
return bool(record.old_value_integer), bool(record.new_value_integer)
|
|
|
|
else:
|
|
|
|
return record.old_value_char, record.new_value_char
|
|
|
|
|
[CHG] *: rewrite commands set, rework status management
This commit revisits the commands set in order to make it more
regular, and limit inconsistent command-sets, although it includes
pseudo-command aliases for common tasks now removed from the core set.
Hard Errors
===========
The previous iteration of the commands set would ignore any
non-command term in a command line. This has been changed to hard
error (and ignoring the entire thing) if any command is unknown or
invalid.
This fixes inconsistent / unexpected interpretations where a user
sends a command, then writes a novel on the same line some words of
which happen to *also* be commands, leading to merge states they did
not expect. They should now be told to fuck off.
Priority Restructuring
----------------------
The numerical priority system was pretty messy in that it confused
"staging priority" (in ways which were not entirely straightforward)
with overrides to other concerns.
This has now being split along all the axis, with separate command
subsets for:
- staging prioritisation, now separated between `default`, `priority`,
and `alone`,
- `default` means PRs are picked by an unspecified order when
creating a staging, if nothing better is available
- `priority` means PRs are picked first when staging, however if
`priority` PRs don't fill the staging the rest will be filled with
`default`, this mode did not previously exist
- `alone` means the PRs are picked first, before splits, and only
`alone` PRs can be part of the staging (which usually matches the
modename)
- `skipchecks` overrides both statuses and approval checks, for the
batch, something previously implied in `p=0`, but now
independent. Setting `skipchecks` basically makes the entire batch
`ready`.
For consistency this also sets the reviewer implicitly: since
skipchecks overrides both statuses *and approval*, whoever enables
this mode is essentially the reviewer.
- `cancel` cancels any ongoing staging when the marked PR becomes
ready again, previously this was also implied (in a more restricted
form) by setting `p=0`
FWBot removal
=============
While the "forwardport bot" still exists as an API level (to segregate
access rights between tokens) it has been removed as an interaction
point, as part of the modules merge plan. As a result,
fwbot stops responding
----------------------
Feedback messages are now always sent by the mergebot, the
forward-porting bot should not send any message or notification
anymore.
commands moved to the merge bot
-------------------------------
- `ignore`/`up to` simply changes bot
- `close` as well
- `skipci` is now a choice / flag of an `fw` command, which denotes
the forward-port policy,
- `fw=default` is the old `ci` and resets the policy to default,
that is wait for the PR to be merged to create forward ports, and
for the required statuses on each forward port to be received
before creating the next
- `fw=skipci` is the old `skipci`, it waits for the merge of the
base PR but then creates all the forward ports immediately (unless
it gets a conflict)
- `fw=skipmerge` immediately creates all the forward ports, without
even waiting for the PR to be merged
This is a completely new mode, and may be rather broken as until
now the 'bot has always assumed the source PR had been merged.
approval rework
---------------
Because of the previous section, there is no distinguishing feature
between `mergebot r+` = "merge this PR" and `forwardbot r+` = "merge
this PR and all its parent with different access rights".
As a result, the two have been merged under a single `mergebot r+`
with heuristics attempting to provide the best experience:
- if approving a non-forward port, the behavior does not change
- else, with review rights on the source, all ancestors are approved
- else, as author of the original, approves all ancestors which descend
from a merged PR
- else, approves all ancestors up to and including the oldest ancestor
to which we have review rights
Most notably, the source's author is not delegated on the source or
any of its descendants anymore. This might need to be revisited if it
provides too restrictive.
For the very specialized need of approving a forward-port *and none of
its ancestors*, `review=` can now take a comma (`,`) separated list of
pull request numbers (github numbers, not mergebot ids).
Computed State
==============
The `state` field of pull requests is now computed. Hopefully this
makes the status more consistent and predictable in the long run, and
importantly makes status management more reliable (because reference
datum get updated naturally flowing to the state).
For now however it makes things more complicated as some of the states
have to be separately signaled or updated:
- `closed` and `error` are now separate flags
- `merge_date` is pulled down from forwardport and becomes the
transition signal for ready -> merged
- `reviewed_by` becomes the transition signal for approval (might be a
good idea to rename it...)
- `status` is computed from the head's statuses and overrides, and
*that* becomes the validation state
Ideally, batch-level flags like `skipchecks` should be on, well, the
batch, and `state` should have a dependency on the batch. However
currently the batch is not a durable / permanent member of the system,
so it's a PR-level flag and a messy pile.
On notable change is that *forcing* the state to `ready` now does that
but also sets the reviewer, `skipchecks`, and overrides to ensure the
API-mediated readying does not get rolled back by e.g. the runbot
sending a status.
This is useful for a few types of automated / programmatic PRs
e.g. translation exports, where we set the state programmatically to
limit noise.
recursive dependency hack
-------------------------
Given a sequence of PRs with an override of the source, if one of the
PRs is updated its descendants should not have the override
anymore. However if the updated PR gets overridden, its descendants
should have *that* override.
This requires some unholy manipulations via an override of `modified`,
as the ORM supports recursive fields but not recursive
dependencies (on a different field).
unconditional followup scheduling
---------------------------------
Previously scheduling forward-port followup was contigent on the FW
policy, but it's not actually correct if the new PR is *immediately*
validated (which can happen now that the field is computed, if there
are no required statuses *or* all of the required statuses are
overridden by an ancestor) as nothing will trigger the state change
and thus scheduling of the fp followup.
The followup function checks all the properties of the batch to port,
so this should not result on incorrect ports. Although it's a bit more
expensive, and will lead to more spam.
Previously this would not happen because on creation of a PR the
validation task (commit -> PR) would still have to execute.
Misc Changes
============
- If a PR is marked as overriding / canceling stagings, it now does
so on retry not just when setting initially.
This was not handled at all previously, so a PR in P0 going into
error due to e.g. a non-deterministic bug would be retried and still
p=0, but a current staging would not get cancelled. Same when a PR
in p=0 goes into error because something was failed, then is updated
with a fix.
- Add tracking to a bunch of relevant PR fields.
Post-mortem analysis currently generally requires going through the
text logs to see what happened, which is annoying.
There is a nondeterminism / inconsistency in the tracking which
sometimes leads the admin user to trigger tracking before the bot
does, leading to the staging tracking being attributed to them
during tests, shove under the carpet by ignoring the user to whom
that tracking is attributed.
When multiple users update tracked fields in the same transaction
all the changes are attributed to the first one having triggered
tracking (?), I couldn't find why the admin sometimes takes over.
- added and leveraged support for enum-backed selection fields
- moved variuous fields from forwardport to runbot_merge
- fix a migration which had never worked and which never run (because
I forgot to bump the version on the module)
- remove some unnecessary intermediate de/serialisation
fixes #673, fixes #309, fixes #792, fixes #846 (probably)
2023-10-31 13:42:07 +07:00
|
|
|
# reverse because the messages are in newest-to-oldest by default
|
|
|
|
# (as that's how you want to read them)
|
2024-10-07 13:06:03 +07:00
|
|
|
messages = pr_id.message_ids[::-1].mapped(lambda m: (
|
|
|
|
m.author_id.display_name,
|
|
|
|
m.body,
|
|
|
|
list(map(read_tracking_value, m.tracking_value_ids)),
|
|
|
|
))
|
[CHG] *: rewrite commands set, rework status management
This commit revisits the commands set in order to make it more
regular, and limit inconsistent command-sets, although it includes
pseudo-command aliases for common tasks now removed from the core set.
Hard Errors
===========
The previous iteration of the commands set would ignore any
non-command term in a command line. This has been changed to hard
error (and ignoring the entire thing) if any command is unknown or
invalid.
This fixes inconsistent / unexpected interpretations where a user
sends a command, then writes a novel on the same line some words of
which happen to *also* be commands, leading to merge states they did
not expect. They should now be told to fuck off.
Priority Restructuring
----------------------
The numerical priority system was pretty messy in that it confused
"staging priority" (in ways which were not entirely straightforward)
with overrides to other concerns.
This has now being split along all the axis, with separate command
subsets for:
- staging prioritisation, now separated between `default`, `priority`,
and `alone`,
- `default` means PRs are picked by an unspecified order when
creating a staging, if nothing better is available
- `priority` means PRs are picked first when staging, however if
`priority` PRs don't fill the staging the rest will be filled with
`default`, this mode did not previously exist
- `alone` means the PRs are picked first, before splits, and only
`alone` PRs can be part of the staging (which usually matches the
modename)
- `skipchecks` overrides both statuses and approval checks, for the
batch, something previously implied in `p=0`, but now
independent. Setting `skipchecks` basically makes the entire batch
`ready`.
For consistency this also sets the reviewer implicitly: since
skipchecks overrides both statuses *and approval*, whoever enables
this mode is essentially the reviewer.
- `cancel` cancels any ongoing staging when the marked PR becomes
ready again, previously this was also implied (in a more restricted
form) by setting `p=0`
FWBot removal
=============
While the "forwardport bot" still exists as an API level (to segregate
access rights between tokens) it has been removed as an interaction
point, as part of the modules merge plan. As a result,
fwbot stops responding
----------------------
Feedback messages are now always sent by the mergebot, the
forward-porting bot should not send any message or notification
anymore.
commands moved to the merge bot
-------------------------------
- `ignore`/`up to` simply changes bot
- `close` as well
- `skipci` is now a choice / flag of an `fw` command, which denotes
the forward-port policy,
- `fw=default` is the old `ci` and resets the policy to default,
that is wait for the PR to be merged to create forward ports, and
for the required statuses on each forward port to be received
before creating the next
- `fw=skipci` is the old `skipci`, it waits for the merge of the
base PR but then creates all the forward ports immediately (unless
it gets a conflict)
- `fw=skipmerge` immediately creates all the forward ports, without
even waiting for the PR to be merged
This is a completely new mode, and may be rather broken as until
now the 'bot has always assumed the source PR had been merged.
approval rework
---------------
Because of the previous section, there is no distinguishing feature
between `mergebot r+` = "merge this PR" and `forwardbot r+` = "merge
this PR and all its parent with different access rights".
As a result, the two have been merged under a single `mergebot r+`
with heuristics attempting to provide the best experience:
- if approving a non-forward port, the behavior does not change
- else, with review rights on the source, all ancestors are approved
- else, as author of the original, approves all ancestors which descend
from a merged PR
- else, approves all ancestors up to and including the oldest ancestor
to which we have review rights
Most notably, the source's author is not delegated on the source or
any of its descendants anymore. This might need to be revisited if it
provides too restrictive.
For the very specialized need of approving a forward-port *and none of
its ancestors*, `review=` can now take a comma (`,`) separated list of
pull request numbers (github numbers, not mergebot ids).
Computed State
==============
The `state` field of pull requests is now computed. Hopefully this
makes the status more consistent and predictable in the long run, and
importantly makes status management more reliable (because reference
datum get updated naturally flowing to the state).
For now however it makes things more complicated as some of the states
have to be separately signaled or updated:
- `closed` and `error` are now separate flags
- `merge_date` is pulled down from forwardport and becomes the
transition signal for ready -> merged
- `reviewed_by` becomes the transition signal for approval (might be a
good idea to rename it...)
- `status` is computed from the head's statuses and overrides, and
*that* becomes the validation state
Ideally, batch-level flags like `skipchecks` should be on, well, the
batch, and `state` should have a dependency on the batch. However
currently the batch is not a durable / permanent member of the system,
so it's a PR-level flag and a messy pile.
On notable change is that *forcing* the state to `ready` now does that
but also sets the reviewer, `skipchecks`, and overrides to ensure the
API-mediated readying does not get rolled back by e.g. the runbot
sending a status.
This is useful for a few types of automated / programmatic PRs
e.g. translation exports, where we set the state programmatically to
limit noise.
recursive dependency hack
-------------------------
Given a sequence of PRs with an override of the source, if one of the
PRs is updated its descendants should not have the override
anymore. However if the updated PR gets overridden, its descendants
should have *that* override.
This requires some unholy manipulations via an override of `modified`,
as the ORM supports recursive fields but not recursive
dependencies (on a different field).
unconditional followup scheduling
---------------------------------
Previously scheduling forward-port followup was contigent on the FW
policy, but it's not actually correct if the new PR is *immediately*
validated (which can happen now that the field is computed, if there
are no required statuses *or* all of the required statuses are
overridden by an ancestor) as nothing will trigger the state change
and thus scheduling of the fp followup.
The followup function checks all the properties of the batch to port,
so this should not result on incorrect ports. Although it's a bit more
expensive, and will lead to more spam.
Previously this would not happen because on creation of a PR the
validation task (commit -> PR) would still have to execute.
Misc Changes
============
- If a PR is marked as overriding / canceling stagings, it now does
so on retry not just when setting initially.
This was not handled at all previously, so a PR in P0 going into
error due to e.g. a non-deterministic bug would be retried and still
p=0, but a current staging would not get cancelled. Same when a PR
in p=0 goes into error because something was failed, then is updated
with a fix.
- Add tracking to a bunch of relevant PR fields.
Post-mortem analysis currently generally requires going through the
text logs to see what happened, which is annoying.
There is a nondeterminism / inconsistency in the tracking which
sometimes leads the admin user to trigger tracking before the bot
does, leading to the staging tracking being attributed to them
during tests, shove under the carpet by ignoring the user to whom
that tracking is attributed.
When multiple users update tracked fields in the same transaction
all the changes are attributed to the first one having triggered
tracking (?), I couldn't find why the admin sometimes takes over.
- added and leveraged support for enum-backed selection fields
- moved variuous fields from forwardport to runbot_merge
- fix a migration which had never worked and which never run (because
I forgot to bump the version on the module)
- remove some unnecessary intermediate de/serialisation
fixes #673, fixes #309, fixes #792, fixes #846 (probably)
2023-10-31 13:42:07 +07:00
|
|
|
|
|
|
|
assert list(messages) == [
|
[FIX] runbot_merge: tracking message author on PullRequest events
d4fa1fd35315d330566e37f515a937f722859ef7 added tracking to changes
from *comments* (as well as a few hacks around authorship transfer),
however it missed two things:
First, it set the `change-author` during comments handling only, so
changes from the `PullRequest` hook e.g. open, synchronise, close,
edit, don't get attributed to their actual source, and instead just
fall back to uid(1). This is easy enough to fix as the `sender` is
always provided, that can be resolved to a partner which is then set
as the author of whatever changes happen.
Second, I actually missed one of the message hooks: there's both
`_message_log` and `_message_log_batch` and they don't call one
another, so both have to be overridden in order for tracking to be
consistent. In this case, specifically, the *creation* of a tracked
object goes through `_message_log_batch` (since that's a very generic
message and so works on every tracked object created during the
transaction... even though batch has a message per record anyway...)
while *updates* go through `_message_log`.
Fixes #895
2024-06-21 21:33:44 +07:00
|
|
|
(users['user'], '<p>Pull Request created</p>', []),
|
2024-10-07 13:06:03 +07:00
|
|
|
(users['user'], '', [('head', c1, c2)]),
|
|
|
|
('OdooBot', f'<p>statuses changed on {c2}</p>', [('state', 'Opened', 'Validated')]),
|
[CHG] *: rewrite commands set, rework status management
This commit revisits the commands set in order to make it more
regular, and limit inconsistent command-sets, although it includes
pseudo-command aliases for common tasks now removed from the core set.
Hard Errors
===========
The previous iteration of the commands set would ignore any
non-command term in a command line. This has been changed to hard
error (and ignoring the entire thing) if any command is unknown or
invalid.
This fixes inconsistent / unexpected interpretations where a user
sends a command, then writes a novel on the same line some words of
which happen to *also* be commands, leading to merge states they did
not expect. They should now be told to fuck off.
Priority Restructuring
----------------------
The numerical priority system was pretty messy in that it confused
"staging priority" (in ways which were not entirely straightforward)
with overrides to other concerns.
This has now being split along all the axis, with separate command
subsets for:
- staging prioritisation, now separated between `default`, `priority`,
and `alone`,
- `default` means PRs are picked by an unspecified order when
creating a staging, if nothing better is available
- `priority` means PRs are picked first when staging, however if
`priority` PRs don't fill the staging the rest will be filled with
`default`, this mode did not previously exist
- `alone` means the PRs are picked first, before splits, and only
`alone` PRs can be part of the staging (which usually matches the
modename)
- `skipchecks` overrides both statuses and approval checks, for the
batch, something previously implied in `p=0`, but now
independent. Setting `skipchecks` basically makes the entire batch
`ready`.
For consistency this also sets the reviewer implicitly: since
skipchecks overrides both statuses *and approval*, whoever enables
this mode is essentially the reviewer.
- `cancel` cancels any ongoing staging when the marked PR becomes
ready again, previously this was also implied (in a more restricted
form) by setting `p=0`
FWBot removal
=============
While the "forwardport bot" still exists as an API level (to segregate
access rights between tokens) it has been removed as an interaction
point, as part of the modules merge plan. As a result,
fwbot stops responding
----------------------
Feedback messages are now always sent by the mergebot, the
forward-porting bot should not send any message or notification
anymore.
commands moved to the merge bot
-------------------------------
- `ignore`/`up to` simply changes bot
- `close` as well
- `skipci` is now a choice / flag of an `fw` command, which denotes
the forward-port policy,
- `fw=default` is the old `ci` and resets the policy to default,
that is wait for the PR to be merged to create forward ports, and
for the required statuses on each forward port to be received
before creating the next
- `fw=skipci` is the old `skipci`, it waits for the merge of the
base PR but then creates all the forward ports immediately (unless
it gets a conflict)
- `fw=skipmerge` immediately creates all the forward ports, without
even waiting for the PR to be merged
This is a completely new mode, and may be rather broken as until
now the 'bot has always assumed the source PR had been merged.
approval rework
---------------
Because of the previous section, there is no distinguishing feature
between `mergebot r+` = "merge this PR" and `forwardbot r+` = "merge
this PR and all its parent with different access rights".
As a result, the two have been merged under a single `mergebot r+`
with heuristics attempting to provide the best experience:
- if approving a non-forward port, the behavior does not change
- else, with review rights on the source, all ancestors are approved
- else, as author of the original, approves all ancestors which descend
from a merged PR
- else, approves all ancestors up to and including the oldest ancestor
to which we have review rights
Most notably, the source's author is not delegated on the source or
any of its descendants anymore. This might need to be revisited if it
provides too restrictive.
For the very specialized need of approving a forward-port *and none of
its ancestors*, `review=` can now take a comma (`,`) separated list of
pull request numbers (github numbers, not mergebot ids).
Computed State
==============
The `state` field of pull requests is now computed. Hopefully this
makes the status more consistent and predictable in the long run, and
importantly makes status management more reliable (because reference
datum get updated naturally flowing to the state).
For now however it makes things more complicated as some of the states
have to be separately signaled or updated:
- `closed` and `error` are now separate flags
- `merge_date` is pulled down from forwardport and becomes the
transition signal for ready -> merged
- `reviewed_by` becomes the transition signal for approval (might be a
good idea to rename it...)
- `status` is computed from the head's statuses and overrides, and
*that* becomes the validation state
Ideally, batch-level flags like `skipchecks` should be on, well, the
batch, and `state` should have a dependency on the batch. However
currently the batch is not a durable / permanent member of the system,
so it's a PR-level flag and a messy pile.
On notable change is that *forcing* the state to `ready` now does that
but also sets the reviewer, `skipchecks`, and overrides to ensure the
API-mediated readying does not get rolled back by e.g. the runbot
sending a status.
This is useful for a few types of automated / programmatic PRs
e.g. translation exports, where we set the state programmatically to
limit noise.
recursive dependency hack
-------------------------
Given a sequence of PRs with an override of the source, if one of the
PRs is updated its descendants should not have the override
anymore. However if the updated PR gets overridden, its descendants
should have *that* override.
This requires some unholy manipulations via an override of `modified`,
as the ORM supports recursive fields but not recursive
dependencies (on a different field).
unconditional followup scheduling
---------------------------------
Previously scheduling forward-port followup was contigent on the FW
policy, but it's not actually correct if the new PR is *immediately*
validated (which can happen now that the field is computed, if there
are no required statuses *or* all of the required statuses are
overridden by an ancestor) as nothing will trigger the state change
and thus scheduling of the fp followup.
The followup function checks all the properties of the batch to port,
so this should not result on incorrect ports. Although it's a bit more
expensive, and will lead to more spam.
Previously this would not happen because on creation of a PR the
validation task (commit -> PR) would still have to execute.
Misc Changes
============
- If a PR is marked as overriding / canceling stagings, it now does
so on retry not just when setting initially.
This was not handled at all previously, so a PR in P0 going into
error due to e.g. a non-deterministic bug would be retried and still
p=0, but a current staging would not get cancelled. Same when a PR
in p=0 goes into error because something was failed, then is updated
with a fix.
- Add tracking to a bunch of relevant PR fields.
Post-mortem analysis currently generally requires going through the
text logs to see what happened, which is annoying.
There is a nondeterminism / inconsistency in the tracking which
sometimes leads the admin user to trigger tracking before the bot
does, leading to the staging tracking being attributed to them
during tests, shove under the carpet by ignoring the user to whom
that tracking is attributed.
When multiple users update tracked fields in the same transaction
all the changes are attributed to the first one having triggered
tracking (?), I couldn't find why the admin sometimes takes over.
- added and leveraged support for enum-backed selection fields
- moved variuous fields from forwardport to runbot_merge
- fix a migration which had never worked and which never run (because
I forgot to bump the version on the module)
- remove some unnecessary intermediate de/serialisation
fixes #673, fixes #309, fixes #792, fixes #846 (probably)
2023-10-31 13:42:07 +07:00
|
|
|
# reviewer approved changing the state and setting reviewer as reviewer
|
|
|
|
# plus set merge method
|
|
|
|
('Reviewer', '', [
|
2024-10-07 13:06:03 +07:00
|
|
|
('merge_method', '', 'rebase and merge, using the PR as merge commit message'),
|
|
|
|
('reviewed_by', '', 'Reviewer'),
|
2024-11-20 18:40:15 +07:00
|
|
|
('state', 'Validated', 'Ready'),
|
[CHG] *: rewrite commands set, rework status management
This commit revisits the commands set in order to make it more
regular, and limit inconsistent command-sets, although it includes
pseudo-command aliases for common tasks now removed from the core set.
Hard Errors
===========
The previous iteration of the commands set would ignore any
non-command term in a command line. This has been changed to hard
error (and ignoring the entire thing) if any command is unknown or
invalid.
This fixes inconsistent / unexpected interpretations where a user
sends a command, then writes a novel on the same line some words of
which happen to *also* be commands, leading to merge states they did
not expect. They should now be told to fuck off.
Priority Restructuring
----------------------
The numerical priority system was pretty messy in that it confused
"staging priority" (in ways which were not entirely straightforward)
with overrides to other concerns.
This has now being split along all the axis, with separate command
subsets for:
- staging prioritisation, now separated between `default`, `priority`,
and `alone`,
- `default` means PRs are picked by an unspecified order when
creating a staging, if nothing better is available
- `priority` means PRs are picked first when staging, however if
`priority` PRs don't fill the staging the rest will be filled with
`default`, this mode did not previously exist
- `alone` means the PRs are picked first, before splits, and only
`alone` PRs can be part of the staging (which usually matches the
modename)
- `skipchecks` overrides both statuses and approval checks, for the
batch, something previously implied in `p=0`, but now
independent. Setting `skipchecks` basically makes the entire batch
`ready`.
For consistency this also sets the reviewer implicitly: since
skipchecks overrides both statuses *and approval*, whoever enables
this mode is essentially the reviewer.
- `cancel` cancels any ongoing staging when the marked PR becomes
ready again, previously this was also implied (in a more restricted
form) by setting `p=0`
FWBot removal
=============
While the "forwardport bot" still exists as an API level (to segregate
access rights between tokens) it has been removed as an interaction
point, as part of the modules merge plan. As a result,
fwbot stops responding
----------------------
Feedback messages are now always sent by the mergebot, the
forward-porting bot should not send any message or notification
anymore.
commands moved to the merge bot
-------------------------------
- `ignore`/`up to` simply changes bot
- `close` as well
- `skipci` is now a choice / flag of an `fw` command, which denotes
the forward-port policy,
- `fw=default` is the old `ci` and resets the policy to default,
that is wait for the PR to be merged to create forward ports, and
for the required statuses on each forward port to be received
before creating the next
- `fw=skipci` is the old `skipci`, it waits for the merge of the
base PR but then creates all the forward ports immediately (unless
it gets a conflict)
- `fw=skipmerge` immediately creates all the forward ports, without
even waiting for the PR to be merged
This is a completely new mode, and may be rather broken as until
now the 'bot has always assumed the source PR had been merged.
approval rework
---------------
Because of the previous section, there is no distinguishing feature
between `mergebot r+` = "merge this PR" and `forwardbot r+` = "merge
this PR and all its parent with different access rights".
As a result, the two have been merged under a single `mergebot r+`
with heuristics attempting to provide the best experience:
- if approving a non-forward port, the behavior does not change
- else, with review rights on the source, all ancestors are approved
- else, as author of the original, approves all ancestors which descend
from a merged PR
- else, approves all ancestors up to and including the oldest ancestor
to which we have review rights
Most notably, the source's author is not delegated on the source or
any of its descendants anymore. This might need to be revisited if it
provides too restrictive.
For the very specialized need of approving a forward-port *and none of
its ancestors*, `review=` can now take a comma (`,`) separated list of
pull request numbers (github numbers, not mergebot ids).
Computed State
==============
The `state` field of pull requests is now computed. Hopefully this
makes the status more consistent and predictable in the long run, and
importantly makes status management more reliable (because reference
datum get updated naturally flowing to the state).
For now however it makes things more complicated as some of the states
have to be separately signaled or updated:
- `closed` and `error` are now separate flags
- `merge_date` is pulled down from forwardport and becomes the
transition signal for ready -> merged
- `reviewed_by` becomes the transition signal for approval (might be a
good idea to rename it...)
- `status` is computed from the head's statuses and overrides, and
*that* becomes the validation state
Ideally, batch-level flags like `skipchecks` should be on, well, the
batch, and `state` should have a dependency on the batch. However
currently the batch is not a durable / permanent member of the system,
so it's a PR-level flag and a messy pile.
On notable change is that *forcing* the state to `ready` now does that
but also sets the reviewer, `skipchecks`, and overrides to ensure the
API-mediated readying does not get rolled back by e.g. the runbot
sending a status.
This is useful for a few types of automated / programmatic PRs
e.g. translation exports, where we set the state programmatically to
limit noise.
recursive dependency hack
-------------------------
Given a sequence of PRs with an override of the source, if one of the
PRs is updated its descendants should not have the override
anymore. However if the updated PR gets overridden, its descendants
should have *that* override.
This requires some unholy manipulations via an override of `modified`,
as the ORM supports recursive fields but not recursive
dependencies (on a different field).
unconditional followup scheduling
---------------------------------
Previously scheduling forward-port followup was contigent on the FW
policy, but it's not actually correct if the new PR is *immediately*
validated (which can happen now that the field is computed, if there
are no required statuses *or* all of the required statuses are
overridden by an ancestor) as nothing will trigger the state change
and thus scheduling of the fp followup.
The followup function checks all the properties of the batch to port,
so this should not result on incorrect ports. Although it's a bit more
expensive, and will lead to more spam.
Previously this would not happen because on creation of a PR the
validation task (commit -> PR) would still have to execute.
Misc Changes
============
- If a PR is marked as overriding / canceling stagings, it now does
so on retry not just when setting initially.
This was not handled at all previously, so a PR in P0 going into
error due to e.g. a non-deterministic bug would be retried and still
p=0, but a current staging would not get cancelled. Same when a PR
in p=0 goes into error because something was failed, then is updated
with a fix.
- Add tracking to a bunch of relevant PR fields.
Post-mortem analysis currently generally requires going through the
text logs to see what happened, which is annoying.
There is a nondeterminism / inconsistency in the tracking which
sometimes leads the admin user to trigger tracking before the bot
does, leading to the staging tracking being attributed to them
during tests, shove under the carpet by ignoring the user to whom
that tracking is attributed.
When multiple users update tracked fields in the same transaction
all the changes are attributed to the first one having triggered
tracking (?), I couldn't find why the admin sometimes takes over.
- added and leveraged support for enum-backed selection fields
- moved variuous fields from forwardport to runbot_merge
- fix a migration which had never worked and which never run (because
I forgot to bump the version on the module)
- remove some unnecessary intermediate de/serialisation
fixes #673, fixes #309, fixes #792, fixes #846 (probably)
2023-10-31 13:42:07 +07:00
|
|
|
]),
|
|
|
|
# staging succeeded
|
2024-06-04 17:15:29 +07:00
|
|
|
(matches('$$'), f'<p>staging {st.id} succeeded</p>', [
|
[CHG] *: rewrite commands set, rework status management
This commit revisits the commands set in order to make it more
regular, and limit inconsistent command-sets, although it includes
pseudo-command aliases for common tasks now removed from the core set.
Hard Errors
===========
The previous iteration of the commands set would ignore any
non-command term in a command line. This has been changed to hard
error (and ignoring the entire thing) if any command is unknown or
invalid.
This fixes inconsistent / unexpected interpretations where a user
sends a command, then writes a novel on the same line some words of
which happen to *also* be commands, leading to merge states they did
not expect. They should now be told to fuck off.
Priority Restructuring
----------------------
The numerical priority system was pretty messy in that it confused
"staging priority" (in ways which were not entirely straightforward)
with overrides to other concerns.
This has now being split along all the axis, with separate command
subsets for:
- staging prioritisation, now separated between `default`, `priority`,
and `alone`,
- `default` means PRs are picked by an unspecified order when
creating a staging, if nothing better is available
- `priority` means PRs are picked first when staging, however if
`priority` PRs don't fill the staging the rest will be filled with
`default`, this mode did not previously exist
- `alone` means the PRs are picked first, before splits, and only
`alone` PRs can be part of the staging (which usually matches the
modename)
- `skipchecks` overrides both statuses and approval checks, for the
batch, something previously implied in `p=0`, but now
independent. Setting `skipchecks` basically makes the entire batch
`ready`.
For consistency this also sets the reviewer implicitly: since
skipchecks overrides both statuses *and approval*, whoever enables
this mode is essentially the reviewer.
- `cancel` cancels any ongoing staging when the marked PR becomes
ready again, previously this was also implied (in a more restricted
form) by setting `p=0`
FWBot removal
=============
While the "forwardport bot" still exists as an API level (to segregate
access rights between tokens) it has been removed as an interaction
point, as part of the modules merge plan. As a result,
fwbot stops responding
----------------------
Feedback messages are now always sent by the mergebot, the
forward-porting bot should not send any message or notification
anymore.
commands moved to the merge bot
-------------------------------
- `ignore`/`up to` simply changes bot
- `close` as well
- `skipci` is now a choice / flag of an `fw` command, which denotes
the forward-port policy,
- `fw=default` is the old `ci` and resets the policy to default,
that is wait for the PR to be merged to create forward ports, and
for the required statuses on each forward port to be received
before creating the next
- `fw=skipci` is the old `skipci`, it waits for the merge of the
base PR but then creates all the forward ports immediately (unless
it gets a conflict)
- `fw=skipmerge` immediately creates all the forward ports, without
even waiting for the PR to be merged
This is a completely new mode, and may be rather broken as until
now the 'bot has always assumed the source PR had been merged.
approval rework
---------------
Because of the previous section, there is no distinguishing feature
between `mergebot r+` = "merge this PR" and `forwardbot r+` = "merge
this PR and all its parent with different access rights".
As a result, the two have been merged under a single `mergebot r+`
with heuristics attempting to provide the best experience:
- if approving a non-forward port, the behavior does not change
- else, with review rights on the source, all ancestors are approved
- else, as author of the original, approves all ancestors which descend
from a merged PR
- else, approves all ancestors up to and including the oldest ancestor
to which we have review rights
Most notably, the source's author is not delegated on the source or
any of its descendants anymore. This might need to be revisited if it
provides too restrictive.
For the very specialized need of approving a forward-port *and none of
its ancestors*, `review=` can now take a comma (`,`) separated list of
pull request numbers (github numbers, not mergebot ids).
Computed State
==============
The `state` field of pull requests is now computed. Hopefully this
makes the status more consistent and predictable in the long run, and
importantly makes status management more reliable (because reference
datum get updated naturally flowing to the state).
For now however it makes things more complicated as some of the states
have to be separately signaled or updated:
- `closed` and `error` are now separate flags
- `merge_date` is pulled down from forwardport and becomes the
transition signal for ready -> merged
- `reviewed_by` becomes the transition signal for approval (might be a
good idea to rename it...)
- `status` is computed from the head's statuses and overrides, and
*that* becomes the validation state
Ideally, batch-level flags like `skipchecks` should be on, well, the
batch, and `state` should have a dependency on the batch. However
currently the batch is not a durable / permanent member of the system,
so it's a PR-level flag and a messy pile.
On notable change is that *forcing* the state to `ready` now does that
but also sets the reviewer, `skipchecks`, and overrides to ensure the
API-mediated readying does not get rolled back by e.g. the runbot
sending a status.
This is useful for a few types of automated / programmatic PRs
e.g. translation exports, where we set the state programmatically to
limit noise.
recursive dependency hack
-------------------------
Given a sequence of PRs with an override of the source, if one of the
PRs is updated its descendants should not have the override
anymore. However if the updated PR gets overridden, its descendants
should have *that* override.
This requires some unholy manipulations via an override of `modified`,
as the ORM supports recursive fields but not recursive
dependencies (on a different field).
unconditional followup scheduling
---------------------------------
Previously scheduling forward-port followup was contigent on the FW
policy, but it's not actually correct if the new PR is *immediately*
validated (which can happen now that the field is computed, if there
are no required statuses *or* all of the required statuses are
overridden by an ancestor) as nothing will trigger the state change
and thus scheduling of the fp followup.
The followup function checks all the properties of the batch to port,
so this should not result on incorrect ports. Although it's a bit more
expensive, and will lead to more spam.
Previously this would not happen because on creation of a PR the
validation task (commit -> PR) would still have to execute.
Misc Changes
============
- If a PR is marked as overriding / canceling stagings, it now does
so on retry not just when setting initially.
This was not handled at all previously, so a PR in P0 going into
error due to e.g. a non-deterministic bug would be retried and still
p=0, but a current staging would not get cancelled. Same when a PR
in p=0 goes into error because something was failed, then is updated
with a fix.
- Add tracking to a bunch of relevant PR fields.
Post-mortem analysis currently generally requires going through the
text logs to see what happened, which is annoying.
There is a nondeterminism / inconsistency in the tracking which
sometimes leads the admin user to trigger tracking before the bot
does, leading to the staging tracking being attributed to them
during tests, shove under the carpet by ignoring the user to whom
that tracking is attributed.
When multiple users update tracked fields in the same transaction
all the changes are attributed to the first one having triggered
tracking (?), I couldn't find why the admin sometimes takes over.
- added and leveraged support for enum-backed selection fields
- moved variuous fields from forwardport to runbot_merge
- fix a migration which had never worked and which never run (because
I forgot to bump the version on the module)
- remove some unnecessary intermediate de/serialisation
fixes #673, fixes #309, fixes #792, fixes #846 (probably)
2023-10-31 13:42:07 +07:00
|
|
|
# set merge date
|
2024-10-07 13:06:03 +07:00
|
|
|
('merge_date', False, pr_id.merge_date),
|
[CHG] *: rewrite commands set, rework status management
This commit revisits the commands set in order to make it more
regular, and limit inconsistent command-sets, although it includes
pseudo-command aliases for common tasks now removed from the core set.
Hard Errors
===========
The previous iteration of the commands set would ignore any
non-command term in a command line. This has been changed to hard
error (and ignoring the entire thing) if any command is unknown or
invalid.
This fixes inconsistent / unexpected interpretations where a user
sends a command, then writes a novel on the same line some words of
which happen to *also* be commands, leading to merge states they did
not expect. They should now be told to fuck off.
Priority Restructuring
----------------------
The numerical priority system was pretty messy in that it confused
"staging priority" (in ways which were not entirely straightforward)
with overrides to other concerns.
This has now being split along all the axis, with separate command
subsets for:
- staging prioritisation, now separated between `default`, `priority`,
and `alone`,
- `default` means PRs are picked by an unspecified order when
creating a staging, if nothing better is available
- `priority` means PRs are picked first when staging, however if
`priority` PRs don't fill the staging the rest will be filled with
`default`, this mode did not previously exist
- `alone` means the PRs are picked first, before splits, and only
`alone` PRs can be part of the staging (which usually matches the
modename)
- `skipchecks` overrides both statuses and approval checks, for the
batch, something previously implied in `p=0`, but now
independent. Setting `skipchecks` basically makes the entire batch
`ready`.
For consistency this also sets the reviewer implicitly: since
skipchecks overrides both statuses *and approval*, whoever enables
this mode is essentially the reviewer.
- `cancel` cancels any ongoing staging when the marked PR becomes
ready again, previously this was also implied (in a more restricted
form) by setting `p=0`
FWBot removal
=============
While the "forwardport bot" still exists as an API level (to segregate
access rights between tokens) it has been removed as an interaction
point, as part of the modules merge plan. As a result,
fwbot stops responding
----------------------
Feedback messages are now always sent by the mergebot, the
forward-porting bot should not send any message or notification
anymore.
commands moved to the merge bot
-------------------------------
- `ignore`/`up to` simply changes bot
- `close` as well
- `skipci` is now a choice / flag of an `fw` command, which denotes
the forward-port policy,
- `fw=default` is the old `ci` and resets the policy to default,
that is wait for the PR to be merged to create forward ports, and
for the required statuses on each forward port to be received
before creating the next
- `fw=skipci` is the old `skipci`, it waits for the merge of the
base PR but then creates all the forward ports immediately (unless
it gets a conflict)
- `fw=skipmerge` immediately creates all the forward ports, without
even waiting for the PR to be merged
This is a completely new mode, and may be rather broken as until
now the 'bot has always assumed the source PR had been merged.
approval rework
---------------
Because of the previous section, there is no distinguishing feature
between `mergebot r+` = "merge this PR" and `forwardbot r+` = "merge
this PR and all its parent with different access rights".
As a result, the two have been merged under a single `mergebot r+`
with heuristics attempting to provide the best experience:
- if approving a non-forward port, the behavior does not change
- else, with review rights on the source, all ancestors are approved
- else, as author of the original, approves all ancestors which descend
from a merged PR
- else, approves all ancestors up to and including the oldest ancestor
to which we have review rights
Most notably, the source's author is not delegated on the source or
any of its descendants anymore. This might need to be revisited if it
provides too restrictive.
For the very specialized need of approving a forward-port *and none of
its ancestors*, `review=` can now take a comma (`,`) separated list of
pull request numbers (github numbers, not mergebot ids).
Computed State
==============
The `state` field of pull requests is now computed. Hopefully this
makes the status more consistent and predictable in the long run, and
importantly makes status management more reliable (because reference
datum get updated naturally flowing to the state).
For now however it makes things more complicated as some of the states
have to be separately signaled or updated:
- `closed` and `error` are now separate flags
- `merge_date` is pulled down from forwardport and becomes the
transition signal for ready -> merged
- `reviewed_by` becomes the transition signal for approval (might be a
good idea to rename it...)
- `status` is computed from the head's statuses and overrides, and
*that* becomes the validation state
Ideally, batch-level flags like `skipchecks` should be on, well, the
batch, and `state` should have a dependency on the batch. However
currently the batch is not a durable / permanent member of the system,
so it's a PR-level flag and a messy pile.
On notable change is that *forcing* the state to `ready` now does that
but also sets the reviewer, `skipchecks`, and overrides to ensure the
API-mediated readying does not get rolled back by e.g. the runbot
sending a status.
This is useful for a few types of automated / programmatic PRs
e.g. translation exports, where we set the state programmatically to
limit noise.
recursive dependency hack
-------------------------
Given a sequence of PRs with an override of the source, if one of the
PRs is updated its descendants should not have the override
anymore. However if the updated PR gets overridden, its descendants
should have *that* override.
This requires some unholy manipulations via an override of `modified`,
as the ORM supports recursive fields but not recursive
dependencies (on a different field).
unconditional followup scheduling
---------------------------------
Previously scheduling forward-port followup was contigent on the FW
policy, but it's not actually correct if the new PR is *immediately*
validated (which can happen now that the field is computed, if there
are no required statuses *or* all of the required statuses are
overridden by an ancestor) as nothing will trigger the state change
and thus scheduling of the fp followup.
The followup function checks all the properties of the batch to port,
so this should not result on incorrect ports. Although it's a bit more
expensive, and will lead to more spam.
Previously this would not happen because on creation of a PR the
validation task (commit -> PR) would still have to execute.
Misc Changes
============
- If a PR is marked as overriding / canceling stagings, it now does
so on retry not just when setting initially.
This was not handled at all previously, so a PR in P0 going into
error due to e.g. a non-deterministic bug would be retried and still
p=0, but a current staging would not get cancelled. Same when a PR
in p=0 goes into error because something was failed, then is updated
with a fix.
- Add tracking to a bunch of relevant PR fields.
Post-mortem analysis currently generally requires going through the
text logs to see what happened, which is annoying.
There is a nondeterminism / inconsistency in the tracking which
sometimes leads the admin user to trigger tracking before the bot
does, leading to the staging tracking being attributed to them
during tests, shove under the carpet by ignoring the user to whom
that tracking is attributed.
When multiple users update tracked fields in the same transaction
all the changes are attributed to the first one having triggered
tracking (?), I couldn't find why the admin sometimes takes over.
- added and leveraged support for enum-backed selection fields
- moved variuous fields from forwardport to runbot_merge
- fix a migration which had never worked and which never run (because
I forgot to bump the version on the module)
- remove some unnecessary intermediate de/serialisation
fixes #673, fixes #309, fixes #792, fixes #846 (probably)
2023-10-31 13:42:07 +07:00
|
|
|
# updated state
|
2024-10-07 13:06:03 +07:00
|
|
|
('state', 'Ready', 'Merged'),
|
[CHG] *: rewrite commands set, rework status management
This commit revisits the commands set in order to make it more
regular, and limit inconsistent command-sets, although it includes
pseudo-command aliases for common tasks now removed from the core set.
Hard Errors
===========
The previous iteration of the commands set would ignore any
non-command term in a command line. This has been changed to hard
error (and ignoring the entire thing) if any command is unknown or
invalid.
This fixes inconsistent / unexpected interpretations where a user
sends a command, then writes a novel on the same line some words of
which happen to *also* be commands, leading to merge states they did
not expect. They should now be told to fuck off.
Priority Restructuring
----------------------
The numerical priority system was pretty messy in that it confused
"staging priority" (in ways which were not entirely straightforward)
with overrides to other concerns.
This has now being split along all the axis, with separate command
subsets for:
- staging prioritisation, now separated between `default`, `priority`,
and `alone`,
- `default` means PRs are picked by an unspecified order when
creating a staging, if nothing better is available
- `priority` means PRs are picked first when staging, however if
`priority` PRs don't fill the staging the rest will be filled with
`default`, this mode did not previously exist
- `alone` means the PRs are picked first, before splits, and only
`alone` PRs can be part of the staging (which usually matches the
modename)
- `skipchecks` overrides both statuses and approval checks, for the
batch, something previously implied in `p=0`, but now
independent. Setting `skipchecks` basically makes the entire batch
`ready`.
For consistency this also sets the reviewer implicitly: since
skipchecks overrides both statuses *and approval*, whoever enables
this mode is essentially the reviewer.
- `cancel` cancels any ongoing staging when the marked PR becomes
ready again, previously this was also implied (in a more restricted
form) by setting `p=0`
FWBot removal
=============
While the "forwardport bot" still exists as an API level (to segregate
access rights between tokens) it has been removed as an interaction
point, as part of the modules merge plan. As a result,
fwbot stops responding
----------------------
Feedback messages are now always sent by the mergebot, the
forward-porting bot should not send any message or notification
anymore.
commands moved to the merge bot
-------------------------------
- `ignore`/`up to` simply changes bot
- `close` as well
- `skipci` is now a choice / flag of an `fw` command, which denotes
the forward-port policy,
- `fw=default` is the old `ci` and resets the policy to default,
that is wait for the PR to be merged to create forward ports, and
for the required statuses on each forward port to be received
before creating the next
- `fw=skipci` is the old `skipci`, it waits for the merge of the
base PR but then creates all the forward ports immediately (unless
it gets a conflict)
- `fw=skipmerge` immediately creates all the forward ports, without
even waiting for the PR to be merged
This is a completely new mode, and may be rather broken as until
now the 'bot has always assumed the source PR had been merged.
approval rework
---------------
Because of the previous section, there is no distinguishing feature
between `mergebot r+` = "merge this PR" and `forwardbot r+` = "merge
this PR and all its parent with different access rights".
As a result, the two have been merged under a single `mergebot r+`
with heuristics attempting to provide the best experience:
- if approving a non-forward port, the behavior does not change
- else, with review rights on the source, all ancestors are approved
- else, as author of the original, approves all ancestors which descend
from a merged PR
- else, approves all ancestors up to and including the oldest ancestor
to which we have review rights
Most notably, the source's author is not delegated on the source or
any of its descendants anymore. This might need to be revisited if it
provides too restrictive.
For the very specialized need of approving a forward-port *and none of
its ancestors*, `review=` can now take a comma (`,`) separated list of
pull request numbers (github numbers, not mergebot ids).
Computed State
==============
The `state` field of pull requests is now computed. Hopefully this
makes the status more consistent and predictable in the long run, and
importantly makes status management more reliable (because reference
datum get updated naturally flowing to the state).
For now however it makes things more complicated as some of the states
have to be separately signaled or updated:
- `closed` and `error` are now separate flags
- `merge_date` is pulled down from forwardport and becomes the
transition signal for ready -> merged
- `reviewed_by` becomes the transition signal for approval (might be a
good idea to rename it...)
- `status` is computed from the head's statuses and overrides, and
*that* becomes the validation state
Ideally, batch-level flags like `skipchecks` should be on, well, the
batch, and `state` should have a dependency on the batch. However
currently the batch is not a durable / permanent member of the system,
so it's a PR-level flag and a messy pile.
On notable change is that *forcing* the state to `ready` now does that
but also sets the reviewer, `skipchecks`, and overrides to ensure the
API-mediated readying does not get rolled back by e.g. the runbot
sending a status.
This is useful for a few types of automated / programmatic PRs
e.g. translation exports, where we set the state programmatically to
limit noise.
recursive dependency hack
-------------------------
Given a sequence of PRs with an override of the source, if one of the
PRs is updated its descendants should not have the override
anymore. However if the updated PR gets overridden, its descendants
should have *that* override.
This requires some unholy manipulations via an override of `modified`,
as the ORM supports recursive fields but not recursive
dependencies (on a different field).
unconditional followup scheduling
---------------------------------
Previously scheduling forward-port followup was contigent on the FW
policy, but it's not actually correct if the new PR is *immediately*
validated (which can happen now that the field is computed, if there
are no required statuses *or* all of the required statuses are
overridden by an ancestor) as nothing will trigger the state change
and thus scheduling of the fp followup.
The followup function checks all the properties of the batch to port,
so this should not result on incorrect ports. Although it's a bit more
expensive, and will lead to more spam.
Previously this would not happen because on creation of a PR the
validation task (commit -> PR) would still have to execute.
Misc Changes
============
- If a PR is marked as overriding / canceling stagings, it now does
so on retry not just when setting initially.
This was not handled at all previously, so a PR in P0 going into
error due to e.g. a non-deterministic bug would be retried and still
p=0, but a current staging would not get cancelled. Same when a PR
in p=0 goes into error because something was failed, then is updated
with a fix.
- Add tracking to a bunch of relevant PR fields.
Post-mortem analysis currently generally requires going through the
text logs to see what happened, which is annoying.
There is a nondeterminism / inconsistency in the tracking which
sometimes leads the admin user to trigger tracking before the bot
does, leading to the staging tracking being attributed to them
during tests, shove under the carpet by ignoring the user to whom
that tracking is attributed.
When multiple users update tracked fields in the same transaction
all the changes are attributed to the first one having triggered
tracking (?), I couldn't find why the admin sometimes takes over.
- added and leveraged support for enum-backed selection fields
- moved variuous fields from forwardport to runbot_merge
- fix a migration which had never worked and which never run (because
I forgot to bump the version on the module)
- remove some unnecessary intermediate de/serialisation
fixes #673, fixes #309, fixes #792, fixes #846 (probably)
2023-10-31 13:42:07 +07:00
|
|
|
]),
|
|
|
|
]
|
|
|
|
|
2018-09-13 15:30:47 +07:00
|
|
|
class TestCommitMessage:
|
2019-10-10 14:22:12 +07:00
|
|
|
def test_commit_simple(self, env, repo, users, config):
|
2018-09-13 15:30:47 +07:00
|
|
|
""" verify 'closes ...' is correctly added in the commit message
|
|
|
|
"""
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
c1 = repo.make_commit(None, 'first!', None, tree={'f': 'm1'})
|
|
|
|
repo.make_ref('heads/master', c1)
|
|
|
|
c2 = repo.make_commit(c1, 'simple commit message', None, tree={'f': 'm2'})
|
|
|
|
|
|
|
|
prx = repo.make_pr(title='title', body='body', target='master', head=c2)
|
|
|
|
repo.post_status(prx.head, 'success', 'ci/runbot')
|
|
|
|
repo.post_status(prx.head, 'success', 'legal/cla')
|
|
|
|
prx.post_comment('hansen r+', config['role_reviewer']['token'])
|
|
|
|
env.run_crons()
|
|
|
|
|
|
|
|
with repo:
|
|
|
|
repo.post_status('heads/staging.master', 'success', 'ci/runbot')
|
|
|
|
repo.post_status('heads/staging.master', 'success', 'legal/cla')
|
|
|
|
env.run_crons()
|
2018-09-13 15:30:47 +07:00
|
|
|
|
|
|
|
master = repo.commit('heads/master')
|
2018-11-22 00:43:05 +07:00
|
|
|
assert master.message == "simple commit message\n\ncloses {repo.name}#1"\
|
|
|
|
"\n\nSigned-off-by: {reviewer.formatted_email}"\
|
|
|
|
.format(repo=repo, reviewer=get_partner(env, users['reviewer']))
|
2018-09-13 15:30:47 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
def test_commit_existing(self, env, repo, users, config):
|
2018-09-13 15:30:47 +07:00
|
|
|
""" verify do not duplicate 'closes' instruction
|
|
|
|
"""
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
c1 = repo.make_commit(None, 'first!', None, tree={'f': 'm1'})
|
|
|
|
repo.make_ref('heads/master', c1)
|
|
|
|
c2 = repo.make_commit(c1, 'simple commit message that closes #1', None, tree={'f': 'm2'})
|
|
|
|
|
|
|
|
prx = repo.make_pr(title='title', body='body', target='master', head=c2)
|
|
|
|
repo.post_status(prx.head, 'success', 'ci/runbot')
|
|
|
|
repo.post_status(prx.head, 'success', 'legal/cla')
|
|
|
|
prx.post_comment('hansen r+', config['role_reviewer']['token'])
|
|
|
|
env.run_crons()
|
|
|
|
|
|
|
|
with repo:
|
|
|
|
repo.post_status('heads/staging.master', 'success', 'ci/runbot')
|
|
|
|
repo.post_status('heads/staging.master', 'success', 'legal/cla')
|
|
|
|
env.run_crons()
|
2018-09-13 15:30:47 +07:00
|
|
|
|
|
|
|
master = repo.commit('heads/master')
|
|
|
|
# closes #1 is already present, should not modify message
|
2018-11-22 00:43:05 +07:00
|
|
|
assert master.message == "simple commit message that closes #1"\
|
|
|
|
"\n\nSigned-off-by: {reviewer.formatted_email}"\
|
|
|
|
.format(reviewer=get_partner(env, users['reviewer']))
|
2018-09-13 15:30:47 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
def test_commit_other(self, env, repo, users, config):
|
2018-09-13 15:30:47 +07:00
|
|
|
""" verify do not duplicate 'closes' instruction
|
|
|
|
"""
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
c1 = repo.make_commit(None, 'first!', None, tree={'f': 'm1'})
|
|
|
|
repo.make_ref('heads/master', c1)
|
|
|
|
c2 = repo.make_commit(c1, 'simple commit message that closes odoo/enterprise#1', None, tree={'f': 'm2'})
|
|
|
|
|
|
|
|
prx = repo.make_pr(title='title', body='body', target='master', head=c2)
|
|
|
|
repo.post_status(prx.head, 'success', 'ci/runbot')
|
|
|
|
repo.post_status(prx.head, 'success', 'legal/cla')
|
|
|
|
prx.post_comment('hansen r+', config['role_reviewer']['token'])
|
|
|
|
env.run_crons()
|
|
|
|
|
|
|
|
with repo:
|
|
|
|
repo.post_status('heads/staging.master', 'success', 'ci/runbot')
|
|
|
|
repo.post_status('heads/staging.master', 'success', 'legal/cla')
|
|
|
|
env.run_crons()
|
2018-09-13 15:30:47 +07:00
|
|
|
|
|
|
|
master = repo.commit('heads/master')
|
|
|
|
# closes on another repositoy, should modify the commit message
|
2018-11-22 00:43:05 +07:00
|
|
|
assert master.message == "simple commit message that closes odoo/enterprise#1\n\ncloses {repo.name}#1"\
|
|
|
|
"\n\nSigned-off-by: {reviewer.formatted_email}"\
|
|
|
|
.format(repo=repo, reviewer=get_partner(env, users['reviewer']))
|
2018-09-13 15:30:47 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
def test_commit_wrong_number(self, env, repo, users, config):
|
2018-09-13 15:30:47 +07:00
|
|
|
""" verify do not match on a wrong number
|
|
|
|
"""
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
c1 = repo.make_commit(None, 'first!', None, tree={'f': 'm1'})
|
|
|
|
repo.make_ref('heads/master', c1)
|
|
|
|
c2 = repo.make_commit(c1, 'simple commit message that closes #11', None, tree={'f': 'm2'})
|
|
|
|
|
|
|
|
prx = repo.make_pr(title='title', body='body', target='master', head=c2)
|
|
|
|
repo.post_status(prx.head, 'success', 'ci/runbot')
|
|
|
|
repo.post_status(prx.head, 'success', 'legal/cla')
|
|
|
|
prx.post_comment('hansen r+', config['role_reviewer']['token'])
|
|
|
|
env.run_crons()
|
|
|
|
|
|
|
|
with repo:
|
|
|
|
repo.post_status('heads/staging.master', 'success', 'ci/runbot')
|
|
|
|
repo.post_status('heads/staging.master', 'success', 'legal/cla')
|
|
|
|
env.run_crons()
|
2018-09-13 15:30:47 +07:00
|
|
|
|
|
|
|
master = repo.commit('heads/master')
|
|
|
|
# closes on another repositoy, should modify the commit message
|
2018-11-22 00:43:05 +07:00
|
|
|
assert master.message == "simple commit message that closes #11\n\ncloses {repo.name}#1"\
|
|
|
|
"\n\nSigned-off-by: {reviewer.formatted_email}"\
|
|
|
|
.format(repo=repo, reviewer=get_partner(env, users['reviewer']))
|
2018-09-13 15:30:47 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
def test_commit_delegate(self, env, repo, users, config):
|
2018-11-22 00:43:05 +07:00
|
|
|
""" verify 'signed-off-by ...' is correctly added in the commit message for delegated review
|
|
|
|
"""
|
2021-10-06 18:06:53 +07:00
|
|
|
env['res.partner'].create({
|
|
|
|
'name': users['other'],
|
|
|
|
'github_login': users['other'],
|
|
|
|
'email': users['other'] + '@example.org'
|
|
|
|
})
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
c1 = repo.make_commit(None, 'first!', None, tree={'f': 'm1'})
|
|
|
|
repo.make_ref('heads/master', c1)
|
|
|
|
c2 = repo.make_commit(c1, 'simple commit message', None, tree={'f': 'm2'})
|
|
|
|
|
|
|
|
prx = repo.make_pr(title='title', body='body', target='master', head=c2)
|
|
|
|
repo.post_status(prx.head, 'success', 'ci/runbot')
|
|
|
|
repo.post_status(prx.head, 'success', 'legal/cla')
|
|
|
|
prx.post_comment('hansen delegate=%s' % users['other'], config["role_reviewer"]["token"])
|
|
|
|
prx.post_comment('hansen r+', config['role_other']['token'])
|
|
|
|
env.run_crons()
|
|
|
|
|
|
|
|
with repo:
|
|
|
|
repo.post_status('heads/staging.master', 'success', 'ci/runbot')
|
|
|
|
repo.post_status('heads/staging.master', 'success', 'legal/cla')
|
|
|
|
env.run_crons()
|
2018-11-22 00:43:05 +07:00
|
|
|
|
|
|
|
master = repo.commit('heads/master')
|
|
|
|
assert master.message == "simple commit message\n\ncloses {repo.name}#1"\
|
|
|
|
"\n\nSigned-off-by: {reviewer.formatted_email}"\
|
|
|
|
.format(repo=repo, reviewer=get_partner(env, users['other']))
|
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
def test_commit_coauthored(self, env, repo, users, config):
|
2018-11-22 00:43:05 +07:00
|
|
|
""" verify 'closes ...' and 'Signed-off-by' are added before co-authored-by tags.
|
2019-05-07 18:22:13 +07:00
|
|
|
|
|
|
|
Also checks that all co-authored-by are moved at the end of the
|
|
|
|
message
|
2018-11-22 00:43:05 +07:00
|
|
|
"""
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
c1 = repo.make_commit(None, 'first!', None, tree={'f': 'm1'})
|
|
|
|
repo.make_ref('heads/master', c1)
|
|
|
|
c2 = repo.make_commit(c1, '''simple commit message
|
2019-08-23 21:16:30 +07:00
|
|
|
|
|
|
|
|
|
|
|
Co-authored-by: Bob <bob@example.com>
|
|
|
|
|
|
|
|
Fixes a thing''', None, tree={'f': 'm2'})
|
2018-11-22 00:43:05 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
prx = repo.make_pr(title='title', body='body', target='master', head=c2)
|
|
|
|
repo.post_status(prx.head, 'success', 'ci/runbot')
|
|
|
|
repo.post_status(prx.head, 'success', 'legal/cla')
|
|
|
|
prx.post_comment('hansen r+', config['role_reviewer']['token'])
|
|
|
|
env.run_crons()
|
2018-11-22 00:43:05 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
repo.post_status('heads/staging.master', 'success', 'ci/runbot')
|
|
|
|
repo.post_status('heads/staging.master', 'success', 'legal/cla')
|
|
|
|
env.run_crons()
|
2018-11-22 00:43:05 +07:00
|
|
|
|
|
|
|
master = repo.commit('heads/master')
|
2019-08-23 21:16:30 +07:00
|
|
|
assert master.message == """simple commit message
|
|
|
|
|
|
|
|
Fixes a thing
|
|
|
|
|
|
|
|
closes {repo.name}#1
|
|
|
|
|
|
|
|
Signed-off-by: {reviewer.formatted_email}
|
|
|
|
Co-authored-by: Bob <bob@example.com>""".format(
|
|
|
|
repo=repo,
|
|
|
|
reviewer=get_partner(env, users['reviewer'])
|
|
|
|
)
|
2018-03-14 16:37:46 +07:00
|
|
|
|
2018-06-13 17:35:22 +07:00
|
|
|
class TestWebhookSecurity:
|
[ADD] *: per-repository webhook secret
Currently webhook secrets are configured per *project* which is an
issue both because different repositories may have different
administrators and thus creates safety concerns, and because multiple
repositories can feed into different projects (e.g. on mergebot,
odoo-dev/odoo is both an ancillary repository to the main RD project,
and the main repository to the minor / legacy master-wowl
project). This means it can be necessary to have multiple projects
share the same secret as well, this then mandates the secret for more
repositories per (1).
This is a pain in the ass, so just detach secrets from projects and
link them *only* to repositories, it's cleaner and easier to manage
and set up progressively.
This requires a lot of changes to the tests, as they all need to
correctly configure the signaling.
For `runbot_merge` there was *some* setup sharing already via the
module-level `repo` fixtures`, those were merged into a conftest-level
fixture which could handle the signaling setup. A few tests which
unnecessarily set up repositories ad-hoc were also moved to the
fixture. But for most of the ad-hoc setup in `runbot_merge`, as well
as `forwardport` where it's all ad-hoc, events sources setup was just
appended as is. This should probably be cleaned up at one point, with
the various requirements collected and organised into a small set of
fixtures doing the job more uniformly.
Fixes #887
2024-06-06 16:07:57 +07:00
|
|
|
@pytest.fixture(autouse=True)
|
|
|
|
def add_secret_to_source(self, env, repo):
|
|
|
|
env['runbot_merge.events_sources'].search([
|
|
|
|
('repository', '=', repo.name),
|
|
|
|
]).secret = "a secret"
|
|
|
|
|
2018-06-13 17:35:22 +07:00
|
|
|
def test_no_secret(self, env, project, repo):
|
|
|
|
""" Test 1: didn't add a secret to the repo, should be ignored
|
|
|
|
"""
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
m = repo.make_commit(None, "initial", None, tree={'a': 'some content'})
|
|
|
|
repo.make_ref('heads/master', m)
|
2018-06-13 17:35:22 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
c0 = repo.make_commit(m, 'replace file contents', None, tree={'a': 'some other content'})
|
|
|
|
pr0 = repo.make_pr(title="gibberish", body="blahblah", target='master', head=c0)
|
2018-06-13 17:35:22 +07:00
|
|
|
|
2024-12-02 20:17:28 +07:00
|
|
|
with pytest.raises(TimeoutError):
|
|
|
|
to_pr(env, pr0)
|
2018-06-13 17:35:22 +07:00
|
|
|
|
|
|
|
def test_wrong_secret(self, env, project, repo):
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
repo.set_secret("wrong secret")
|
2018-06-13 17:35:22 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
m = repo.make_commit(None, "initial", None, tree={'a': 'some content'})
|
|
|
|
repo.make_ref('heads/master', m)
|
2018-06-13 17:35:22 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
c0 = repo.make_commit(m, 'replace file contents', None, tree={'a': 'some other content'})
|
|
|
|
pr0 = repo.make_pr(title="gibberish", body="blahblah", target='master', head=c0)
|
2018-06-13 17:35:22 +07:00
|
|
|
|
2024-12-02 20:17:28 +07:00
|
|
|
with pytest.raises(TimeoutError):
|
|
|
|
to_pr(env, pr0)
|
2018-06-13 17:35:22 +07:00
|
|
|
|
|
|
|
def test_correct_secret(self, env, project, repo):
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
repo.set_secret("a secret")
|
2018-06-13 17:35:22 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
m = repo.make_commit(None, "initial", None, tree={'a': 'some content'})
|
|
|
|
repo.make_ref('heads/master', m)
|
2018-06-13 17:35:22 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
c0 = repo.make_commit(m, 'replace file contents', None, tree={'a': 'some other content'})
|
|
|
|
pr0 = repo.make_pr(title="gibberish", body="blahblah", target='master', head=c0)
|
2018-06-13 17:35:22 +07:00
|
|
|
|
2024-12-02 20:17:28 +07:00
|
|
|
assert to_pr(env, pr0)
|
2018-06-13 17:35:22 +07:00
|
|
|
|
2020-10-02 20:24:54 +07:00
|
|
|
def test_staging_ongoing(env, repo, config):
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
# create base branch
|
|
|
|
m = repo.make_commit(None, 'initial', None, tree={'a': 'some content'})
|
|
|
|
repo.make_ref('heads/master', m)
|
|
|
|
|
|
|
|
# create PR
|
|
|
|
c0 = repo.make_commit(m, 'replace file contents', None, tree={'a': 'some other content'})
|
|
|
|
c1 = repo.make_commit(c0, 'add file', None, tree={'a': 'some other content', 'b': 'a second file'})
|
|
|
|
pr1 = repo.make_pr(title="gibberish", body="blahblah", target='master', head=c1)
|
|
|
|
repo.post_status(c1, 'success', 'legal/cla')
|
|
|
|
repo.post_status(c1, 'success', 'ci/runbot')
|
|
|
|
pr1.post_comment("hansen r+ rebase-merge", config['role_reviewer']['token'])
|
|
|
|
env.run_crons()
|
2024-12-02 20:17:28 +07:00
|
|
|
pr1 = to_pr(env, pr1)
|
2018-03-14 16:37:46 +07:00
|
|
|
assert pr1.staging_id
|
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
# create second PR and make ready for staging
|
|
|
|
c2 = repo.make_commit(m, 'other', None, tree={'a': 'some content', 'c': 'ccc'})
|
|
|
|
c3 = repo.make_commit(c2, 'other', None, tree={'a': 'some content', 'c': 'ccc', 'd': 'ddd'})
|
|
|
|
pr2 = repo.make_pr(title='gibberish', body='blahblah', target='master', head=c3)
|
|
|
|
repo.post_status(c3, 'success', 'legal/cla')
|
|
|
|
repo.post_status(c3, 'success', 'ci/runbot')
|
|
|
|
pr2.post_comment('hansen r+ rebase-merge', config['role_reviewer']['token'])
|
|
|
|
env.run_crons()
|
2024-12-02 20:17:28 +07:00
|
|
|
p_2 = to_pr(env, pr2)
|
2018-03-28 21:43:48 +07:00
|
|
|
assert p_2.state == 'ready', "PR2 should not have been staged since there is a pending staging for master"
|
2018-03-14 16:37:46 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
2024-06-03 17:59:24 +07:00
|
|
|
repo.post_status('staging.master', 'success', 'ci/runbot')
|
|
|
|
repo.post_status('staging.master', 'success', 'legal/cla')
|
2019-10-10 14:22:12 +07:00
|
|
|
env.run_crons()
|
2018-03-14 16:37:46 +07:00
|
|
|
assert pr1.state == 'merged'
|
2018-03-28 21:43:48 +07:00
|
|
|
assert p_2.staging_id
|
2018-03-14 16:37:46 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
2024-06-03 17:59:24 +07:00
|
|
|
repo.post_status('staging.master', 'success', 'ci/runbot')
|
|
|
|
repo.post_status('staging.master', 'success', 'legal/cla')
|
2019-10-10 14:22:12 +07:00
|
|
|
env.run_crons()
|
2018-03-28 21:43:48 +07:00
|
|
|
assert p_2.state == 'merged'
|
2018-03-14 16:37:46 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
def test_staging_concurrent(env, repo, config):
|
2018-03-14 16:37:46 +07:00
|
|
|
""" test staging to different targets, should be picked up together """
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
m = repo.make_commit(None, 'initial', None, tree={'m': 'm'})
|
|
|
|
repo.make_ref('heads/1.0', m)
|
|
|
|
repo.make_ref('heads/2.0', m)
|
2018-03-14 16:37:46 +07:00
|
|
|
|
|
|
|
env['runbot_merge.project'].search([]).write({
|
|
|
|
'branch_ids': [(0, 0, {'name': '1.0'}), (0, 0, {'name': '2.0'})],
|
|
|
|
})
|
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
c10 = repo.make_commit(m, 'AAA', None, tree={'m': 'm', 'a': 'a'})
|
|
|
|
c11 = repo.make_commit(c10, 'BBB', None, tree={'m': 'm', 'a': 'a', 'b': 'b'})
|
|
|
|
pr1 = repo.make_pr(title='t1', body='b1', target='1.0', head=c11)
|
|
|
|
repo.post_status(pr1.head, 'success', 'ci/runbot')
|
|
|
|
repo.post_status(pr1.head, 'success', 'legal/cla')
|
|
|
|
pr1.post_comment('hansen r+ rebase-merge', config['role_reviewer']['token'])
|
|
|
|
|
|
|
|
c20 = repo.make_commit(m, 'CCC', None, tree={'m': 'm', 'c': 'c'})
|
|
|
|
c21 = repo.make_commit(c20, 'DDD', None, tree={'m': 'm', 'c': 'c', 'd': 'd'})
|
|
|
|
pr2 = repo.make_pr(title='t2', body='b2', target='2.0', head=c21)
|
|
|
|
repo.post_status(pr2.head, 'success', 'ci/runbot')
|
|
|
|
repo.post_status(pr2.head, 'success', 'legal/cla')
|
|
|
|
pr2.post_comment('hansen r+ rebase-merge', config['role_reviewer']['token'])
|
|
|
|
env.run_crons()
|
|
|
|
|
2024-12-02 20:17:28 +07:00
|
|
|
pr1 = to_pr(env, pr1)
|
2018-03-14 16:37:46 +07:00
|
|
|
assert pr1.staging_id
|
2024-12-02 20:17:28 +07:00
|
|
|
pr2 = to_pr(env, pr2)
|
2018-03-14 16:37:46 +07:00
|
|
|
assert pr2.staging_id
|
|
|
|
|
2024-06-11 20:41:03 +07:00
|
|
|
|
2021-08-30 19:40:38 +07:00
|
|
|
def test_staging_conflict_first(env, repo, users, config, page):
|
2020-10-02 20:24:54 +07:00
|
|
|
""" If the first batch of a staging triggers a conflict, the PR should be
|
|
|
|
marked as in error
|
2018-03-14 16:37:46 +07:00
|
|
|
"""
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
m1 = repo.make_commit(None, 'initial', None, tree={'f': 'm1'})
|
|
|
|
m2 = repo.make_commit(m1, 'second', None, tree={'f': 'm2'})
|
|
|
|
repo.make_ref('heads/master', m2)
|
|
|
|
|
|
|
|
c1 = repo.make_commit(m1, 'other second', None, tree={'f': 'c1'})
|
|
|
|
c2 = repo.make_commit(c1, 'third', None, tree={'f': 'c2'})
|
2021-08-30 19:40:38 +07:00
|
|
|
pr = repo.make_pr(title='title', body='body', target='master', head=c2)
|
|
|
|
repo.post_status(pr.head, 'success', 'ci/runbot')
|
|
|
|
repo.post_status(pr.head, 'success', 'legal/cla')
|
|
|
|
pr.post_comment('hansen r+ rebase-merge', config['role_reviewer']['token'])
|
2019-10-10 14:22:12 +07:00
|
|
|
env.run_crons()
|
|
|
|
|
2021-08-30 19:40:38 +07:00
|
|
|
pr_id = to_pr(env, pr)
|
|
|
|
assert pr_id.state == 'error'
|
|
|
|
assert pr.comments == [
|
2018-11-26 16:28:13 +07:00
|
|
|
(users['reviewer'], 'hansen r+ rebase-merge'),
|
2021-08-30 19:40:38 +07:00
|
|
|
seen(env, pr, users),
|
2022-06-23 19:25:07 +07:00
|
|
|
(users['user'], 'Merge method set to rebase and merge, using the PR as merge commit message.'),
|
|
|
|
(users['user'], '@%(user)s @%(reviewer)s unable to stage: merge conflict' % users),
|
2018-03-14 16:37:46 +07:00
|
|
|
]
|
|
|
|
|
2021-08-30 19:40:38 +07:00
|
|
|
dangerbox = pr_page(page, pr).cssselect('.alert-danger span')
|
|
|
|
assert dangerbox
|
|
|
|
assert dangerbox[0].text.strip() == 'Unable to stage PR'
|
|
|
|
|
2024-06-11 20:41:03 +07:00
|
|
|
|
2020-10-02 20:24:54 +07:00
|
|
|
def test_staging_conflict_second(env, repo, users, config):
|
|
|
|
""" If the non-first batch of a staging triggers a conflict, the PR should
|
|
|
|
just be skipped: it might be a conflict with an other PR which could fail
|
|
|
|
the staging
|
|
|
|
"""
|
|
|
|
with repo:
|
|
|
|
[m] = repo.make_commits(None, Commit('initial', tree={'a': '1'}), ref='heads/master')
|
|
|
|
|
|
|
|
with repo:
|
|
|
|
repo.make_commits(m, Commit('first pr', tree={'a': '2'}), ref='heads/pr0')
|
|
|
|
pr0 = repo.make_pr(target='master', head='pr0')
|
|
|
|
repo.post_status(pr0.head, 'success', 'ci/runbot')
|
|
|
|
repo.post_status(pr0.head, 'success', 'legal/cla')
|
|
|
|
pr0.post_comment('hansen r+', config['role_reviewer']['token'])
|
|
|
|
|
|
|
|
with repo:
|
|
|
|
repo.make_commits(m, Commit('second pr', tree={'a': '3'}), ref='heads/pr1')
|
|
|
|
pr1 = repo.make_pr(target='master', head='pr1')
|
|
|
|
repo.post_status(pr1.head, 'success', 'ci/runbot')
|
|
|
|
repo.post_status(pr1.head, 'success', 'legal/cla')
|
|
|
|
pr1.post_comment('hansen r+', config['role_reviewer']['token'])
|
|
|
|
env.run_crons()
|
2021-08-30 19:40:38 +07:00
|
|
|
|
|
|
|
pr0_id = to_pr(env, pr0)
|
|
|
|
pr1_id = to_pr(env, pr1)
|
2020-10-02 20:24:54 +07:00
|
|
|
assert pr0_id.staging_id, "pr0 should have been staged"
|
|
|
|
assert not pr1_id.staging_id, "pr1 should not have been staged (due to conflict)"
|
|
|
|
assert pr1_id.state == 'ready', "pr1 should not be in error yet"
|
|
|
|
|
|
|
|
# merge the staging, this should try to stage pr1, fail, and put it in error
|
|
|
|
# as it now conflicts with the master proper
|
|
|
|
with repo:
|
|
|
|
repo.post_status('staging.master', 'success', 'ci/runbot')
|
|
|
|
repo.post_status('staging.master', 'success', 'legal/cla')
|
|
|
|
env.run_crons()
|
|
|
|
|
|
|
|
assert pr1_id.state == 'error', "now pr1 should be in error"
|
|
|
|
|
|
|
|
|
2024-07-31 14:40:53 +07:00
|
|
|
@pytest.mark.defaultstatuses
|
|
|
|
@pytest.mark.parametrize('update_op', [
|
|
|
|
pytest.param(
|
|
|
|
lambda _: {'timeout_limit': datetime.datetime.now().isoformat(" ", "seconds")},
|
|
|
|
id="set-timeout-limit",
|
|
|
|
),
|
|
|
|
pytest.param(
|
|
|
|
lambda timeout: {'staged_at': (datetime.datetime.now() - datetime.timedelta(minutes=2*timeout)).isoformat(" ", "seconds")},
|
|
|
|
id="set-staged-at",
|
|
|
|
),
|
|
|
|
])
|
|
|
|
def test_staging_ci_timeout(env, repo, config, page, update_op: Callable[[int], dict]):
|
2018-03-14 16:37:46 +07:00
|
|
|
"""If a staging timeouts (~ delay since staged greater than
|
|
|
|
configured)... requeue?
|
|
|
|
"""
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
2024-07-31 14:40:53 +07:00
|
|
|
m, _, c2 = repo.make_commits(
|
|
|
|
None,
|
|
|
|
Commit('initial', tree={'f': 'm'}),
|
|
|
|
Commit('first', tree={'f': 'c1'}),
|
|
|
|
Commit('second', tree={'f': 'c2'}),
|
|
|
|
)
|
2019-10-10 14:22:12 +07:00
|
|
|
repo.make_ref('heads/master', m)
|
2018-03-14 16:37:46 +07:00
|
|
|
|
2021-08-30 19:40:38 +07:00
|
|
|
pr = repo.make_pr(title='title', body='body', target='master', head=c2)
|
2024-07-31 14:40:53 +07:00
|
|
|
repo.post_status(pr.head, 'success')
|
2021-08-30 19:40:38 +07:00
|
|
|
pr.post_comment('hansen r+ rebase-merge', config['role_reviewer']['token'])
|
2019-10-10 14:22:12 +07:00
|
|
|
env.run_crons()
|
2018-03-14 16:37:46 +07:00
|
|
|
|
2021-08-30 19:40:38 +07:00
|
|
|
pr_id = to_pr(env, pr)
|
|
|
|
assert pr_id.staging_id
|
2018-03-14 16:37:46 +07:00
|
|
|
timeout = env['runbot_merge.project'].search([]).ci_timeout
|
|
|
|
|
2024-07-31 14:40:53 +07:00
|
|
|
pr_id.staging_id.write(update_op(timeout))
|
2024-08-01 15:15:32 +07:00
|
|
|
env.run_crons(None)
|
2021-08-30 19:40:38 +07:00
|
|
|
assert pr_id.state == 'error', "timeout should fail the PR"
|
|
|
|
|
|
|
|
dangerbox = pr_page(page, pr).cssselect('.alert-danger span')
|
|
|
|
assert dangerbox
|
|
|
|
assert dangerbox[0].text == 'timed out (>60 minutes)'
|
2018-03-14 16:37:46 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
def test_timeout_bump_on_pending(env, repo, config):
|
|
|
|
with repo:
|
2024-07-31 14:40:53 +07:00
|
|
|
[m, c] = repo.make_commits(
|
|
|
|
None,
|
|
|
|
Commit('initial', tree={'f': '0'}),
|
|
|
|
Commit('c', tree={'f': '1'}),
|
|
|
|
)
|
2019-10-10 14:22:12 +07:00
|
|
|
repo.make_ref('heads/master', m)
|
2019-09-23 20:42:18 +07:00
|
|
|
|
2024-09-20 17:17:17 +07:00
|
|
|
prx = repo.make_pr(target='master', head=c)
|
|
|
|
repo.post_status(prx.head, 'success', 'ci/runbot')
|
|
|
|
repo.post_status(prx.head, 'success', 'legal/cla')
|
2019-10-10 14:22:12 +07:00
|
|
|
prx.post_comment('hansen r+', config['role_reviewer']['token'])
|
|
|
|
env.run_crons()
|
2019-09-23 20:42:18 +07:00
|
|
|
|
|
|
|
st = env['runbot_merge.stagings'].search([])
|
|
|
|
old_timeout = odoo.fields.Datetime.to_string(datetime.datetime.now() - datetime.timedelta(days=15))
|
|
|
|
st.timeout_limit = old_timeout
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
2024-09-20 17:17:17 +07:00
|
|
|
repo.post_status('staging.master', 'pending', 'ci/runbot')
|
|
|
|
env.run_crons(None)
|
|
|
|
assert st.timeout_limit > old_timeout, "receiving a pending status should bump the timeout"
|
|
|
|
|
|
|
|
st.timeout_limit = old_timeout
|
|
|
|
# clear the statuses cache to remove the memoized status times
|
|
|
|
st.statuses_cache = "{}"
|
|
|
|
st.commit_ids.statuses = "{}"
|
|
|
|
with repo:
|
|
|
|
repo.post_status('staging.master', 'success', 'legal/cla')
|
2024-07-30 18:42:00 +07:00
|
|
|
env.run_crons(None)
|
2024-09-20 17:17:17 +07:00
|
|
|
assert st.timeout_limit == old_timeout, "receiving a success status should *not* bump the timeout"
|
2019-09-23 20:42:18 +07:00
|
|
|
|
2021-08-30 19:40:38 +07:00
|
|
|
def test_staging_ci_failure_single(env, repo, users, config, page):
|
2018-03-14 16:37:46 +07:00
|
|
|
""" on failure of single-PR staging, mark & notify failure
|
|
|
|
"""
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
m = repo.make_commit(None, 'initial', None, tree={'m': 'm'})
|
|
|
|
repo.make_ref('heads/master', m)
|
|
|
|
|
|
|
|
c1 = repo.make_commit(m, 'first', None, tree={'m': 'c1'})
|
|
|
|
c2 = repo.make_commit(c1, 'second', None, tree={'m': 'c2'})
|
2021-08-30 19:40:38 +07:00
|
|
|
pr = repo.make_pr(title='title', body='body', target='master', head=c2)
|
|
|
|
repo.post_status(pr.head, 'success', 'ci/runbot')
|
|
|
|
repo.post_status(pr.head, 'success', 'legal/cla')
|
|
|
|
pr.post_comment('hansen r+ rebase-merge', config['role_reviewer']['token'])
|
2019-10-10 14:22:12 +07:00
|
|
|
env.run_crons()
|
2021-08-30 19:40:38 +07:00
|
|
|
pr_id = to_pr(env, pr)
|
|
|
|
assert pr_id.staging_id
|
2018-03-14 16:37:46 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
2024-06-03 17:59:24 +07:00
|
|
|
repo.post_status('staging.master', 'failure', 'a/b')
|
|
|
|
repo.post_status('staging.master', 'success', 'legal/cla')
|
|
|
|
repo.post_status('staging.master', 'failure', 'ci/runbot') # stable genius
|
2019-10-10 14:22:12 +07:00
|
|
|
env.run_crons()
|
2021-08-30 19:40:38 +07:00
|
|
|
assert pr_id.state == 'error'
|
2018-03-14 16:37:46 +07:00
|
|
|
|
2021-08-30 19:40:38 +07:00
|
|
|
assert pr.comments == [
|
2018-11-26 16:28:13 +07:00
|
|
|
(users['reviewer'], 'hansen r+ rebase-merge'),
|
2021-08-30 19:40:38 +07:00
|
|
|
seen(env, pr, users),
|
2022-06-23 19:25:07 +07:00
|
|
|
(users['user'], "Merge method set to rebase and merge, using the PR as merge commit message."),
|
|
|
|
(users['user'], '@%(user)s @%(reviewer)s staging failed: ci/runbot' % users)
|
2018-03-14 16:37:46 +07:00
|
|
|
]
|
|
|
|
|
2021-08-30 19:40:38 +07:00
|
|
|
dangerbox = pr_page(page, pr).cssselect('.alert-danger span')
|
|
|
|
assert dangerbox
|
|
|
|
assert dangerbox[0].text == 'ci/runbot'
|
|
|
|
|
2024-06-11 20:41:03 +07:00
|
|
|
|
2022-06-08 19:45:32 +07:00
|
|
|
def test_ff_failure(env, repo, config, page):
|
2018-03-14 16:37:46 +07:00
|
|
|
""" target updated while the PR is being staged => redo staging """
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
m = repo.make_commit(None, 'initial', None, tree={'m': 'm'})
|
|
|
|
repo.make_ref('heads/master', m)
|
|
|
|
|
|
|
|
c1 = repo.make_commit(m, 'first', None, tree={'m': 'c1'})
|
|
|
|
c2 = repo.make_commit(c1, 'second', None, tree={'m': 'c2'})
|
|
|
|
prx = repo.make_pr(title='title', body='body', target='master', head=c2)
|
|
|
|
repo.post_status(prx.head, 'success', 'legal/cla')
|
|
|
|
repo.post_status(prx.head, 'success', 'ci/runbot')
|
|
|
|
prx.post_comment('hansen r+ rebase-merge', config['role_reviewer']['token'])
|
|
|
|
env.run_crons()
|
2024-12-02 20:17:28 +07:00
|
|
|
st = to_pr(env, prx).staging_id
|
2022-06-08 19:45:32 +07:00
|
|
|
assert st
|
2018-03-14 16:37:46 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
m2 = repo.make_commit('heads/master', 'cockblock', None, tree={'m': 'm', 'm2': 'm2'})
|
2018-03-14 16:37:46 +07:00
|
|
|
assert repo.commit('heads/master').id == m2
|
|
|
|
|
|
|
|
# report staging success & run cron to merge
|
|
|
|
staging = repo.commit('heads/staging.master')
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
2024-06-03 17:59:24 +07:00
|
|
|
repo.post_status('staging.master', 'success', 'legal/cla')
|
|
|
|
repo.post_status('staging.master', 'success', 'ci/runbot')
|
2019-10-10 14:22:12 +07:00
|
|
|
env.run_crons()
|
2018-03-14 16:37:46 +07:00
|
|
|
|
2022-06-08 19:45:32 +07:00
|
|
|
assert st.reason == 'update is not a fast forward'
|
|
|
|
# check that it's added as title on the staging
|
|
|
|
doc = html.fromstring(page('/runbot_merge'))
|
|
|
|
_new, prev = doc.cssselect('li.staging')
|
|
|
|
|
|
|
|
assert 'bg-gray-lighter' in prev.classes, "ff failure is ~ cancelling"
|
2024-06-04 17:15:29 +07:00
|
|
|
assert 'fast forward failed (update is not a fast forward)' in prev.get('title')
|
2022-06-08 19:45:32 +07:00
|
|
|
|
2024-12-02 20:17:28 +07:00
|
|
|
assert to_pr(env, prx).staging_id, "merge should not have succeeded"
|
2018-03-14 16:37:46 +07:00
|
|
|
assert repo.commit('heads/staging.master').id != staging.id,\
|
|
|
|
"PR should be staged to a new commit"
|
|
|
|
|
2024-06-11 20:41:03 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
def test_ff_failure_batch(env, repo, users, config):
|
|
|
|
with repo:
|
|
|
|
m = repo.make_commit(None, 'initial', None, tree={'m': 'm'})
|
|
|
|
repo.make_ref('heads/master', m)
|
|
|
|
|
|
|
|
a1 = repo.make_commit(m, 'a1', None, tree={'m': 'm', 'a': '1'})
|
|
|
|
a2 = repo.make_commit(a1, 'a2', None, tree={'m': 'm', 'a': '2'})
|
|
|
|
repo.make_ref('heads/A', a2)
|
|
|
|
A = repo.make_pr(title='A', body=None, target='master', head='A')
|
|
|
|
repo.post_status(A.head, 'success', 'legal/cla')
|
|
|
|
repo.post_status(A.head, 'success', 'ci/runbot')
|
|
|
|
A.post_comment('hansen r+ rebase-merge', config['role_reviewer']['token'])
|
|
|
|
|
|
|
|
b1 = repo.make_commit(m, 'b1', None, tree={'m': 'm', 'b': '1'})
|
|
|
|
b2 = repo.make_commit(b1, 'b2', None, tree={'m': 'm', 'b': '2'})
|
|
|
|
repo.make_ref('heads/B', b2)
|
|
|
|
B = repo.make_pr(title='B', body=None, target='master', head='B')
|
|
|
|
repo.post_status(B.head, 'success', 'legal/cla')
|
|
|
|
repo.post_status(B.head, 'success', 'ci/runbot')
|
|
|
|
B.post_comment('hansen r+ rebase-merge', config['role_reviewer']['token'])
|
|
|
|
|
|
|
|
c1 = repo.make_commit(m, 'c1', None, tree={'m': 'm', 'c': '1'})
|
|
|
|
c2 = repo.make_commit(c1, 'c2', None, tree={'m': 'm', 'c': '2'})
|
|
|
|
repo.make_ref('heads/C', c2)
|
|
|
|
C = repo.make_pr(title='C', body=None, target='master', head='C')
|
|
|
|
repo.post_status(C.head, 'success', 'legal/cla')
|
|
|
|
repo.post_status(C.head, 'success', 'ci/runbot')
|
|
|
|
C.post_comment('hansen r+ rebase-merge', config['role_reviewer']['token'])
|
|
|
|
env.run_crons()
|
|
|
|
|
2021-08-09 12:55:38 +07:00
|
|
|
pr_a = to_pr(env, A)
|
|
|
|
pr_b = to_pr(env, B)
|
|
|
|
pr_c = to_pr(env, C)
|
|
|
|
|
2018-09-19 23:49:52 +07:00
|
|
|
messages = [
|
|
|
|
c['commit']['message']
|
|
|
|
for c in repo.log('heads/staging.master')
|
|
|
|
]
|
2021-08-09 12:55:38 +07:00
|
|
|
assert part_of('a2', pr_a) in messages
|
|
|
|
assert part_of('b2', pr_b) in messages
|
|
|
|
assert part_of('c2', pr_c) in messages
|
2018-09-19 23:49:52 +07:00
|
|
|
|
|
|
|
# block FF
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
2021-01-13 14:18:17 +07:00
|
|
|
repo.make_commit('heads/master', 'NO!', None, tree={'m': 'm2'})
|
2018-09-19 23:49:52 +07:00
|
|
|
|
|
|
|
old_staging = repo.commit('heads/staging.master')
|
|
|
|
# confirm staging
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
repo.post_status('heads/staging.master', 'success', 'legal/cla')
|
|
|
|
repo.post_status('heads/staging.master', 'success', 'ci/runbot')
|
|
|
|
env.run_crons()
|
2018-09-19 23:49:52 +07:00
|
|
|
new_staging = repo.commit('heads/staging.master')
|
|
|
|
|
|
|
|
assert new_staging.id != old_staging.id
|
|
|
|
|
|
|
|
# confirm again
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
repo.post_status('heads/staging.master', 'success', 'legal/cla')
|
|
|
|
repo.post_status('heads/staging.master', 'success', 'ci/runbot')
|
|
|
|
env.run_crons()
|
2018-09-19 23:49:52 +07:00
|
|
|
messages = {
|
|
|
|
c['commit']['message']
|
|
|
|
for c in repo.log('heads/master')
|
|
|
|
}
|
2018-11-22 00:43:05 +07:00
|
|
|
reviewer = get_partner(env, users["reviewer"]).formatted_email
|
2018-09-19 23:49:52 +07:00
|
|
|
assert messages == {
|
|
|
|
'initial', 'NO!',
|
2021-08-09 12:55:38 +07:00
|
|
|
part_of('a1', pr_a), part_of('a2', pr_a), f'A\n\ncloses {pr_a.display_name}\n\nSigned-off-by: {reviewer}',
|
|
|
|
part_of('b1', pr_b), part_of('b2', pr_b), f'B\n\ncloses {pr_b.display_name}\n\nSigned-off-by: {reviewer}',
|
|
|
|
part_of('c1', pr_c), part_of('c2', pr_c), f'C\n\ncloses {pr_c.display_name}\n\nSigned-off-by: {reviewer}',
|
2018-09-19 23:49:52 +07:00
|
|
|
}
|
|
|
|
|
2019-08-27 16:23:34 +07:00
|
|
|
class TestPREdition:
|
2024-07-15 20:52:08 +07:00
|
|
|
def test_edit(self, env, project, repo, config):
|
2019-08-27 16:23:34 +07:00
|
|
|
""" Editing PR:
|
2018-03-14 16:37:46 +07:00
|
|
|
|
2019-08-27 16:23:34 +07:00
|
|
|
* title (-> message)
|
|
|
|
* body (-> message)
|
|
|
|
* base.ref (-> target)
|
|
|
|
"""
|
|
|
|
branch_1 = env['runbot_merge.branch'].create({
|
|
|
|
'name': '1.0',
|
|
|
|
'project_id': env['runbot_merge.project'].search([]).id,
|
|
|
|
})
|
2018-03-14 16:37:46 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
m = repo.make_commit(None, 'initial', None, tree={'m': 'm'})
|
|
|
|
repo.make_ref('heads/master', m)
|
|
|
|
repo.make_ref('heads/1.0', m)
|
|
|
|
repo.make_ref('heads/2.0', m)
|
2018-03-14 16:37:46 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
c1 = repo.make_commit(m, 'first', None, tree={'m': 'c1'})
|
|
|
|
c2 = repo.make_commit(c1, 'second', None, tree={'m': 'c2'})
|
|
|
|
prx = repo.make_pr(title='title', body='body', target='master', head=c2)
|
2022-06-09 13:55:34 +07:00
|
|
|
repo.post_status(prx.head, 'success', 'legal/cla')
|
|
|
|
repo.post_status(prx.head, 'success', 'ci/runbot')
|
|
|
|
prx.post_comment('hansen rebase-ff r+', config['role_reviewer']['token'])
|
|
|
|
env.run_crons()
|
|
|
|
|
2024-12-02 20:17:28 +07:00
|
|
|
pr = to_pr(env, prx)
|
2022-06-09 13:55:34 +07:00
|
|
|
assert pr.state == 'ready'
|
|
|
|
st = pr.staging_id
|
|
|
|
assert st
|
2019-08-27 16:23:34 +07:00
|
|
|
assert pr.message == 'title\n\nbody'
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo: prx.title = "title 2"
|
2019-08-27 16:23:34 +07:00
|
|
|
assert pr.message == 'title 2\n\nbody'
|
2022-07-11 19:00:35 +07:00
|
|
|
with repo: prx.body = None
|
|
|
|
assert pr.message == "title 2"
|
2022-06-09 13:55:34 +07:00
|
|
|
assert pr.staging_id, \
|
|
|
|
"message edition does not affect staging of rebased PRs"
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo: prx.base = '1.0'
|
2019-08-27 16:23:34 +07:00
|
|
|
assert pr.target == branch_1
|
2022-06-09 13:55:34 +07:00
|
|
|
assert not pr.staging_id, "updated the base of a staged PR should have unstaged it"
|
2024-02-05 22:10:15 +07:00
|
|
|
assert st.state == 'cancelled', f"expected cancellation, got {st.state}"
|
2024-07-15 20:52:08 +07:00
|
|
|
assert st.reason == f"{pr.display_name} target (base) branch was changed from '{project.name}:master' to '{project.name}:1.0'"
|
2019-08-27 16:23:34 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo: prx.base = '2.0'
|
2019-08-27 16:23:34 +07:00
|
|
|
assert not pr.exists()
|
2019-10-10 14:22:12 +07:00
|
|
|
env.run_crons()
|
2019-03-04 15:52:21 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo: prx.base = '1.0'
|
2024-12-02 20:17:28 +07:00
|
|
|
assert to_pr(env, prx).target == branch_1
|
2019-03-04 15:52:21 +07:00
|
|
|
|
[CHG] *: rewrite commands set, rework status management
This commit revisits the commands set in order to make it more
regular, and limit inconsistent command-sets, although it includes
pseudo-command aliases for common tasks now removed from the core set.
Hard Errors
===========
The previous iteration of the commands set would ignore any
non-command term in a command line. This has been changed to hard
error (and ignoring the entire thing) if any command is unknown or
invalid.
This fixes inconsistent / unexpected interpretations where a user
sends a command, then writes a novel on the same line some words of
which happen to *also* be commands, leading to merge states they did
not expect. They should now be told to fuck off.
Priority Restructuring
----------------------
The numerical priority system was pretty messy in that it confused
"staging priority" (in ways which were not entirely straightforward)
with overrides to other concerns.
This has now being split along all the axis, with separate command
subsets for:
- staging prioritisation, now separated between `default`, `priority`,
and `alone`,
- `default` means PRs are picked by an unspecified order when
creating a staging, if nothing better is available
- `priority` means PRs are picked first when staging, however if
`priority` PRs don't fill the staging the rest will be filled with
`default`, this mode did not previously exist
- `alone` means the PRs are picked first, before splits, and only
`alone` PRs can be part of the staging (which usually matches the
modename)
- `skipchecks` overrides both statuses and approval checks, for the
batch, something previously implied in `p=0`, but now
independent. Setting `skipchecks` basically makes the entire batch
`ready`.
For consistency this also sets the reviewer implicitly: since
skipchecks overrides both statuses *and approval*, whoever enables
this mode is essentially the reviewer.
- `cancel` cancels any ongoing staging when the marked PR becomes
ready again, previously this was also implied (in a more restricted
form) by setting `p=0`
FWBot removal
=============
While the "forwardport bot" still exists as an API level (to segregate
access rights between tokens) it has been removed as an interaction
point, as part of the modules merge plan. As a result,
fwbot stops responding
----------------------
Feedback messages are now always sent by the mergebot, the
forward-porting bot should not send any message or notification
anymore.
commands moved to the merge bot
-------------------------------
- `ignore`/`up to` simply changes bot
- `close` as well
- `skipci` is now a choice / flag of an `fw` command, which denotes
the forward-port policy,
- `fw=default` is the old `ci` and resets the policy to default,
that is wait for the PR to be merged to create forward ports, and
for the required statuses on each forward port to be received
before creating the next
- `fw=skipci` is the old `skipci`, it waits for the merge of the
base PR but then creates all the forward ports immediately (unless
it gets a conflict)
- `fw=skipmerge` immediately creates all the forward ports, without
even waiting for the PR to be merged
This is a completely new mode, and may be rather broken as until
now the 'bot has always assumed the source PR had been merged.
approval rework
---------------
Because of the previous section, there is no distinguishing feature
between `mergebot r+` = "merge this PR" and `forwardbot r+` = "merge
this PR and all its parent with different access rights".
As a result, the two have been merged under a single `mergebot r+`
with heuristics attempting to provide the best experience:
- if approving a non-forward port, the behavior does not change
- else, with review rights on the source, all ancestors are approved
- else, as author of the original, approves all ancestors which descend
from a merged PR
- else, approves all ancestors up to and including the oldest ancestor
to which we have review rights
Most notably, the source's author is not delegated on the source or
any of its descendants anymore. This might need to be revisited if it
provides too restrictive.
For the very specialized need of approving a forward-port *and none of
its ancestors*, `review=` can now take a comma (`,`) separated list of
pull request numbers (github numbers, not mergebot ids).
Computed State
==============
The `state` field of pull requests is now computed. Hopefully this
makes the status more consistent and predictable in the long run, and
importantly makes status management more reliable (because reference
datum get updated naturally flowing to the state).
For now however it makes things more complicated as some of the states
have to be separately signaled or updated:
- `closed` and `error` are now separate flags
- `merge_date` is pulled down from forwardport and becomes the
transition signal for ready -> merged
- `reviewed_by` becomes the transition signal for approval (might be a
good idea to rename it...)
- `status` is computed from the head's statuses and overrides, and
*that* becomes the validation state
Ideally, batch-level flags like `skipchecks` should be on, well, the
batch, and `state` should have a dependency on the batch. However
currently the batch is not a durable / permanent member of the system,
so it's a PR-level flag and a messy pile.
On notable change is that *forcing* the state to `ready` now does that
but also sets the reviewer, `skipchecks`, and overrides to ensure the
API-mediated readying does not get rolled back by e.g. the runbot
sending a status.
This is useful for a few types of automated / programmatic PRs
e.g. translation exports, where we set the state programmatically to
limit noise.
recursive dependency hack
-------------------------
Given a sequence of PRs with an override of the source, if one of the
PRs is updated its descendants should not have the override
anymore. However if the updated PR gets overridden, its descendants
should have *that* override.
This requires some unholy manipulations via an override of `modified`,
as the ORM supports recursive fields but not recursive
dependencies (on a different field).
unconditional followup scheduling
---------------------------------
Previously scheduling forward-port followup was contigent on the FW
policy, but it's not actually correct if the new PR is *immediately*
validated (which can happen now that the field is computed, if there
are no required statuses *or* all of the required statuses are
overridden by an ancestor) as nothing will trigger the state change
and thus scheduling of the fp followup.
The followup function checks all the properties of the batch to port,
so this should not result on incorrect ports. Although it's a bit more
expensive, and will lead to more spam.
Previously this would not happen because on creation of a PR the
validation task (commit -> PR) would still have to execute.
Misc Changes
============
- If a PR is marked as overriding / canceling stagings, it now does
so on retry not just when setting initially.
This was not handled at all previously, so a PR in P0 going into
error due to e.g. a non-deterministic bug would be retried and still
p=0, but a current staging would not get cancelled. Same when a PR
in p=0 goes into error because something was failed, then is updated
with a fix.
- Add tracking to a bunch of relevant PR fields.
Post-mortem analysis currently generally requires going through the
text logs to see what happened, which is annoying.
There is a nondeterminism / inconsistency in the tracking which
sometimes leads the admin user to trigger tracking before the bot
does, leading to the staging tracking being attributed to them
during tests, shove under the carpet by ignoring the user to whom
that tracking is attributed.
When multiple users update tracked fields in the same transaction
all the changes are attributed to the first one having triggered
tracking (?), I couldn't find why the admin sometimes takes over.
- added and leveraged support for enum-backed selection fields
- moved variuous fields from forwardport to runbot_merge
- fix a migration which had never worked and which never run (because
I forgot to bump the version on the module)
- remove some unnecessary intermediate de/serialisation
fixes #673, fixes #309, fixes #792, fixes #846 (probably)
2023-10-31 13:42:07 +07:00
|
|
|
def test_retarget_update_commits(self, env, project, repo):
|
|
|
|
""" Retargeting a PR should update its commits count, as well as follow
|
|
|
|
the new target's requirements
|
2019-08-27 16:23:34 +07:00
|
|
|
"""
|
[CHG] *: rewrite commands set, rework status management
This commit revisits the commands set in order to make it more
regular, and limit inconsistent command-sets, although it includes
pseudo-command aliases for common tasks now removed from the core set.
Hard Errors
===========
The previous iteration of the commands set would ignore any
non-command term in a command line. This has been changed to hard
error (and ignoring the entire thing) if any command is unknown or
invalid.
This fixes inconsistent / unexpected interpretations where a user
sends a command, then writes a novel on the same line some words of
which happen to *also* be commands, leading to merge states they did
not expect. They should now be told to fuck off.
Priority Restructuring
----------------------
The numerical priority system was pretty messy in that it confused
"staging priority" (in ways which were not entirely straightforward)
with overrides to other concerns.
This has now being split along all the axis, with separate command
subsets for:
- staging prioritisation, now separated between `default`, `priority`,
and `alone`,
- `default` means PRs are picked by an unspecified order when
creating a staging, if nothing better is available
- `priority` means PRs are picked first when staging, however if
`priority` PRs don't fill the staging the rest will be filled with
`default`, this mode did not previously exist
- `alone` means the PRs are picked first, before splits, and only
`alone` PRs can be part of the staging (which usually matches the
modename)
- `skipchecks` overrides both statuses and approval checks, for the
batch, something previously implied in `p=0`, but now
independent. Setting `skipchecks` basically makes the entire batch
`ready`.
For consistency this also sets the reviewer implicitly: since
skipchecks overrides both statuses *and approval*, whoever enables
this mode is essentially the reviewer.
- `cancel` cancels any ongoing staging when the marked PR becomes
ready again, previously this was also implied (in a more restricted
form) by setting `p=0`
FWBot removal
=============
While the "forwardport bot" still exists as an API level (to segregate
access rights between tokens) it has been removed as an interaction
point, as part of the modules merge plan. As a result,
fwbot stops responding
----------------------
Feedback messages are now always sent by the mergebot, the
forward-porting bot should not send any message or notification
anymore.
commands moved to the merge bot
-------------------------------
- `ignore`/`up to` simply changes bot
- `close` as well
- `skipci` is now a choice / flag of an `fw` command, which denotes
the forward-port policy,
- `fw=default` is the old `ci` and resets the policy to default,
that is wait for the PR to be merged to create forward ports, and
for the required statuses on each forward port to be received
before creating the next
- `fw=skipci` is the old `skipci`, it waits for the merge of the
base PR but then creates all the forward ports immediately (unless
it gets a conflict)
- `fw=skipmerge` immediately creates all the forward ports, without
even waiting for the PR to be merged
This is a completely new mode, and may be rather broken as until
now the 'bot has always assumed the source PR had been merged.
approval rework
---------------
Because of the previous section, there is no distinguishing feature
between `mergebot r+` = "merge this PR" and `forwardbot r+` = "merge
this PR and all its parent with different access rights".
As a result, the two have been merged under a single `mergebot r+`
with heuristics attempting to provide the best experience:
- if approving a non-forward port, the behavior does not change
- else, with review rights on the source, all ancestors are approved
- else, as author of the original, approves all ancestors which descend
from a merged PR
- else, approves all ancestors up to and including the oldest ancestor
to which we have review rights
Most notably, the source's author is not delegated on the source or
any of its descendants anymore. This might need to be revisited if it
provides too restrictive.
For the very specialized need of approving a forward-port *and none of
its ancestors*, `review=` can now take a comma (`,`) separated list of
pull request numbers (github numbers, not mergebot ids).
Computed State
==============
The `state` field of pull requests is now computed. Hopefully this
makes the status more consistent and predictable in the long run, and
importantly makes status management more reliable (because reference
datum get updated naturally flowing to the state).
For now however it makes things more complicated as some of the states
have to be separately signaled or updated:
- `closed` and `error` are now separate flags
- `merge_date` is pulled down from forwardport and becomes the
transition signal for ready -> merged
- `reviewed_by` becomes the transition signal for approval (might be a
good idea to rename it...)
- `status` is computed from the head's statuses and overrides, and
*that* becomes the validation state
Ideally, batch-level flags like `skipchecks` should be on, well, the
batch, and `state` should have a dependency on the batch. However
currently the batch is not a durable / permanent member of the system,
so it's a PR-level flag and a messy pile.
On notable change is that *forcing* the state to `ready` now does that
but also sets the reviewer, `skipchecks`, and overrides to ensure the
API-mediated readying does not get rolled back by e.g. the runbot
sending a status.
This is useful for a few types of automated / programmatic PRs
e.g. translation exports, where we set the state programmatically to
limit noise.
recursive dependency hack
-------------------------
Given a sequence of PRs with an override of the source, if one of the
PRs is updated its descendants should not have the override
anymore. However if the updated PR gets overridden, its descendants
should have *that* override.
This requires some unholy manipulations via an override of `modified`,
as the ORM supports recursive fields but not recursive
dependencies (on a different field).
unconditional followup scheduling
---------------------------------
Previously scheduling forward-port followup was contigent on the FW
policy, but it's not actually correct if the new PR is *immediately*
validated (which can happen now that the field is computed, if there
are no required statuses *or* all of the required statuses are
overridden by an ancestor) as nothing will trigger the state change
and thus scheduling of the fp followup.
The followup function checks all the properties of the batch to port,
so this should not result on incorrect ports. Although it's a bit more
expensive, and will lead to more spam.
Previously this would not happen because on creation of a PR the
validation task (commit -> PR) would still have to execute.
Misc Changes
============
- If a PR is marked as overriding / canceling stagings, it now does
so on retry not just when setting initially.
This was not handled at all previously, so a PR in P0 going into
error due to e.g. a non-deterministic bug would be retried and still
p=0, but a current staging would not get cancelled. Same when a PR
in p=0 goes into error because something was failed, then is updated
with a fix.
- Add tracking to a bunch of relevant PR fields.
Post-mortem analysis currently generally requires going through the
text logs to see what happened, which is annoying.
There is a nondeterminism / inconsistency in the tracking which
sometimes leads the admin user to trigger tracking before the bot
does, leading to the staging tracking being attributed to them
during tests, shove under the carpet by ignoring the user to whom
that tracking is attributed.
When multiple users update tracked fields in the same transaction
all the changes are attributed to the first one having triggered
tracking (?), I couldn't find why the admin sometimes takes over.
- added and leveraged support for enum-backed selection fields
- moved variuous fields from forwardport to runbot_merge
- fix a migration which had never worked and which never run (because
I forgot to bump the version on the module)
- remove some unnecessary intermediate de/serialisation
fixes #673, fixes #309, fixes #792, fixes #846 (probably)
2023-10-31 13:42:07 +07:00
|
|
|
project.repo_ids.write({
|
|
|
|
'status_ids': [
|
|
|
|
(5, 0, 0),
|
|
|
|
(0, 0, {'context': 'a', 'branch_filter': [('name', '=', 'master')]}),
|
|
|
|
(0, 0, {'context': 'b', 'branch_filter': [('name', '!=', 'master')]}),
|
|
|
|
]
|
|
|
|
})
|
2019-08-27 16:23:34 +07:00
|
|
|
branch_1 = env['runbot_merge.branch'].create({
|
|
|
|
'name': '1.0',
|
|
|
|
'project_id': env['runbot_merge.project'].search([]).id,
|
|
|
|
})
|
|
|
|
master = env['runbot_merge.branch'].search([('name', '=', 'master')])
|
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
2019-11-18 20:21:16 +07:00
|
|
|
# master is 1 commit ahead of 1.0
|
[CHG] *: rewrite commands set, rework status management
This commit revisits the commands set in order to make it more
regular, and limit inconsistent command-sets, although it includes
pseudo-command aliases for common tasks now removed from the core set.
Hard Errors
===========
The previous iteration of the commands set would ignore any
non-command term in a command line. This has been changed to hard
error (and ignoring the entire thing) if any command is unknown or
invalid.
This fixes inconsistent / unexpected interpretations where a user
sends a command, then writes a novel on the same line some words of
which happen to *also* be commands, leading to merge states they did
not expect. They should now be told to fuck off.
Priority Restructuring
----------------------
The numerical priority system was pretty messy in that it confused
"staging priority" (in ways which were not entirely straightforward)
with overrides to other concerns.
This has now being split along all the axis, with separate command
subsets for:
- staging prioritisation, now separated between `default`, `priority`,
and `alone`,
- `default` means PRs are picked by an unspecified order when
creating a staging, if nothing better is available
- `priority` means PRs are picked first when staging, however if
`priority` PRs don't fill the staging the rest will be filled with
`default`, this mode did not previously exist
- `alone` means the PRs are picked first, before splits, and only
`alone` PRs can be part of the staging (which usually matches the
modename)
- `skipchecks` overrides both statuses and approval checks, for the
batch, something previously implied in `p=0`, but now
independent. Setting `skipchecks` basically makes the entire batch
`ready`.
For consistency this also sets the reviewer implicitly: since
skipchecks overrides both statuses *and approval*, whoever enables
this mode is essentially the reviewer.
- `cancel` cancels any ongoing staging when the marked PR becomes
ready again, previously this was also implied (in a more restricted
form) by setting `p=0`
FWBot removal
=============
While the "forwardport bot" still exists as an API level (to segregate
access rights between tokens) it has been removed as an interaction
point, as part of the modules merge plan. As a result,
fwbot stops responding
----------------------
Feedback messages are now always sent by the mergebot, the
forward-porting bot should not send any message or notification
anymore.
commands moved to the merge bot
-------------------------------
- `ignore`/`up to` simply changes bot
- `close` as well
- `skipci` is now a choice / flag of an `fw` command, which denotes
the forward-port policy,
- `fw=default` is the old `ci` and resets the policy to default,
that is wait for the PR to be merged to create forward ports, and
for the required statuses on each forward port to be received
before creating the next
- `fw=skipci` is the old `skipci`, it waits for the merge of the
base PR but then creates all the forward ports immediately (unless
it gets a conflict)
- `fw=skipmerge` immediately creates all the forward ports, without
even waiting for the PR to be merged
This is a completely new mode, and may be rather broken as until
now the 'bot has always assumed the source PR had been merged.
approval rework
---------------
Because of the previous section, there is no distinguishing feature
between `mergebot r+` = "merge this PR" and `forwardbot r+` = "merge
this PR and all its parent with different access rights".
As a result, the two have been merged under a single `mergebot r+`
with heuristics attempting to provide the best experience:
- if approving a non-forward port, the behavior does not change
- else, with review rights on the source, all ancestors are approved
- else, as author of the original, approves all ancestors which descend
from a merged PR
- else, approves all ancestors up to and including the oldest ancestor
to which we have review rights
Most notably, the source's author is not delegated on the source or
any of its descendants anymore. This might need to be revisited if it
provides too restrictive.
For the very specialized need of approving a forward-port *and none of
its ancestors*, `review=` can now take a comma (`,`) separated list of
pull request numbers (github numbers, not mergebot ids).
Computed State
==============
The `state` field of pull requests is now computed. Hopefully this
makes the status more consistent and predictable in the long run, and
importantly makes status management more reliable (because reference
datum get updated naturally flowing to the state).
For now however it makes things more complicated as some of the states
have to be separately signaled or updated:
- `closed` and `error` are now separate flags
- `merge_date` is pulled down from forwardport and becomes the
transition signal for ready -> merged
- `reviewed_by` becomes the transition signal for approval (might be a
good idea to rename it...)
- `status` is computed from the head's statuses and overrides, and
*that* becomes the validation state
Ideally, batch-level flags like `skipchecks` should be on, well, the
batch, and `state` should have a dependency on the batch. However
currently the batch is not a durable / permanent member of the system,
so it's a PR-level flag and a messy pile.
On notable change is that *forcing* the state to `ready` now does that
but also sets the reviewer, `skipchecks`, and overrides to ensure the
API-mediated readying does not get rolled back by e.g. the runbot
sending a status.
This is useful for a few types of automated / programmatic PRs
e.g. translation exports, where we set the state programmatically to
limit noise.
recursive dependency hack
-------------------------
Given a sequence of PRs with an override of the source, if one of the
PRs is updated its descendants should not have the override
anymore. However if the updated PR gets overridden, its descendants
should have *that* override.
This requires some unholy manipulations via an override of `modified`,
as the ORM supports recursive fields but not recursive
dependencies (on a different field).
unconditional followup scheduling
---------------------------------
Previously scheduling forward-port followup was contigent on the FW
policy, but it's not actually correct if the new PR is *immediately*
validated (which can happen now that the field is computed, if there
are no required statuses *or* all of the required statuses are
overridden by an ancestor) as nothing will trigger the state change
and thus scheduling of the fp followup.
The followup function checks all the properties of the batch to port,
so this should not result on incorrect ports. Although it's a bit more
expensive, and will lead to more spam.
Previously this would not happen because on creation of a PR the
validation task (commit -> PR) would still have to execute.
Misc Changes
============
- If a PR is marked as overriding / canceling stagings, it now does
so on retry not just when setting initially.
This was not handled at all previously, so a PR in P0 going into
error due to e.g. a non-deterministic bug would be retried and still
p=0, but a current staging would not get cancelled. Same when a PR
in p=0 goes into error because something was failed, then is updated
with a fix.
- Add tracking to a bunch of relevant PR fields.
Post-mortem analysis currently generally requires going through the
text logs to see what happened, which is annoying.
There is a nondeterminism / inconsistency in the tracking which
sometimes leads the admin user to trigger tracking before the bot
does, leading to the staging tracking being attributed to them
during tests, shove under the carpet by ignoring the user to whom
that tracking is attributed.
When multiple users update tracked fields in the same transaction
all the changes are attributed to the first one having triggered
tracking (?), I couldn't find why the admin sometimes takes over.
- added and leveraged support for enum-backed selection fields
- moved variuous fields from forwardport to runbot_merge
- fix a migration which had never worked and which never run (because
I forgot to bump the version on the module)
- remove some unnecessary intermediate de/serialisation
fixes #673, fixes #309, fixes #792, fixes #846 (probably)
2023-10-31 13:42:07 +07:00
|
|
|
[m] = repo.make_commits(None, Commit('initial', tree={'m': 'm'}), ref='heads/1.0')
|
|
|
|
[m2] = repo.make_commits(m, Commit('second', tree={'m': 'm2'}), ref='heads/master')
|
2019-08-27 16:23:34 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
# the PR builds on master, but is errorneously targeted to 1.0
|
[CHG] *: rewrite commands set, rework status management
This commit revisits the commands set in order to make it more
regular, and limit inconsistent command-sets, although it includes
pseudo-command aliases for common tasks now removed from the core set.
Hard Errors
===========
The previous iteration of the commands set would ignore any
non-command term in a command line. This has been changed to hard
error (and ignoring the entire thing) if any command is unknown or
invalid.
This fixes inconsistent / unexpected interpretations where a user
sends a command, then writes a novel on the same line some words of
which happen to *also* be commands, leading to merge states they did
not expect. They should now be told to fuck off.
Priority Restructuring
----------------------
The numerical priority system was pretty messy in that it confused
"staging priority" (in ways which were not entirely straightforward)
with overrides to other concerns.
This has now being split along all the axis, with separate command
subsets for:
- staging prioritisation, now separated between `default`, `priority`,
and `alone`,
- `default` means PRs are picked by an unspecified order when
creating a staging, if nothing better is available
- `priority` means PRs are picked first when staging, however if
`priority` PRs don't fill the staging the rest will be filled with
`default`, this mode did not previously exist
- `alone` means the PRs are picked first, before splits, and only
`alone` PRs can be part of the staging (which usually matches the
modename)
- `skipchecks` overrides both statuses and approval checks, for the
batch, something previously implied in `p=0`, but now
independent. Setting `skipchecks` basically makes the entire batch
`ready`.
For consistency this also sets the reviewer implicitly: since
skipchecks overrides both statuses *and approval*, whoever enables
this mode is essentially the reviewer.
- `cancel` cancels any ongoing staging when the marked PR becomes
ready again, previously this was also implied (in a more restricted
form) by setting `p=0`
FWBot removal
=============
While the "forwardport bot" still exists as an API level (to segregate
access rights between tokens) it has been removed as an interaction
point, as part of the modules merge plan. As a result,
fwbot stops responding
----------------------
Feedback messages are now always sent by the mergebot, the
forward-porting bot should not send any message or notification
anymore.
commands moved to the merge bot
-------------------------------
- `ignore`/`up to` simply changes bot
- `close` as well
- `skipci` is now a choice / flag of an `fw` command, which denotes
the forward-port policy,
- `fw=default` is the old `ci` and resets the policy to default,
that is wait for the PR to be merged to create forward ports, and
for the required statuses on each forward port to be received
before creating the next
- `fw=skipci` is the old `skipci`, it waits for the merge of the
base PR but then creates all the forward ports immediately (unless
it gets a conflict)
- `fw=skipmerge` immediately creates all the forward ports, without
even waiting for the PR to be merged
This is a completely new mode, and may be rather broken as until
now the 'bot has always assumed the source PR had been merged.
approval rework
---------------
Because of the previous section, there is no distinguishing feature
between `mergebot r+` = "merge this PR" and `forwardbot r+` = "merge
this PR and all its parent with different access rights".
As a result, the two have been merged under a single `mergebot r+`
with heuristics attempting to provide the best experience:
- if approving a non-forward port, the behavior does not change
- else, with review rights on the source, all ancestors are approved
- else, as author of the original, approves all ancestors which descend
from a merged PR
- else, approves all ancestors up to and including the oldest ancestor
to which we have review rights
Most notably, the source's author is not delegated on the source or
any of its descendants anymore. This might need to be revisited if it
provides too restrictive.
For the very specialized need of approving a forward-port *and none of
its ancestors*, `review=` can now take a comma (`,`) separated list of
pull request numbers (github numbers, not mergebot ids).
Computed State
==============
The `state` field of pull requests is now computed. Hopefully this
makes the status more consistent and predictable in the long run, and
importantly makes status management more reliable (because reference
datum get updated naturally flowing to the state).
For now however it makes things more complicated as some of the states
have to be separately signaled or updated:
- `closed` and `error` are now separate flags
- `merge_date` is pulled down from forwardport and becomes the
transition signal for ready -> merged
- `reviewed_by` becomes the transition signal for approval (might be a
good idea to rename it...)
- `status` is computed from the head's statuses and overrides, and
*that* becomes the validation state
Ideally, batch-level flags like `skipchecks` should be on, well, the
batch, and `state` should have a dependency on the batch. However
currently the batch is not a durable / permanent member of the system,
so it's a PR-level flag and a messy pile.
On notable change is that *forcing* the state to `ready` now does that
but also sets the reviewer, `skipchecks`, and overrides to ensure the
API-mediated readying does not get rolled back by e.g. the runbot
sending a status.
This is useful for a few types of automated / programmatic PRs
e.g. translation exports, where we set the state programmatically to
limit noise.
recursive dependency hack
-------------------------
Given a sequence of PRs with an override of the source, if one of the
PRs is updated its descendants should not have the override
anymore. However if the updated PR gets overridden, its descendants
should have *that* override.
This requires some unholy manipulations via an override of `modified`,
as the ORM supports recursive fields but not recursive
dependencies (on a different field).
unconditional followup scheduling
---------------------------------
Previously scheduling forward-port followup was contigent on the FW
policy, but it's not actually correct if the new PR is *immediately*
validated (which can happen now that the field is computed, if there
are no required statuses *or* all of the required statuses are
overridden by an ancestor) as nothing will trigger the state change
and thus scheduling of the fp followup.
The followup function checks all the properties of the batch to port,
so this should not result on incorrect ports. Although it's a bit more
expensive, and will lead to more spam.
Previously this would not happen because on creation of a PR the
validation task (commit -> PR) would still have to execute.
Misc Changes
============
- If a PR is marked as overriding / canceling stagings, it now does
so on retry not just when setting initially.
This was not handled at all previously, so a PR in P0 going into
error due to e.g. a non-deterministic bug would be retried and still
p=0, but a current staging would not get cancelled. Same when a PR
in p=0 goes into error because something was failed, then is updated
with a fix.
- Add tracking to a bunch of relevant PR fields.
Post-mortem analysis currently generally requires going through the
text logs to see what happened, which is annoying.
There is a nondeterminism / inconsistency in the tracking which
sometimes leads the admin user to trigger tracking before the bot
does, leading to the staging tracking being attributed to them
during tests, shove under the carpet by ignoring the user to whom
that tracking is attributed.
When multiple users update tracked fields in the same transaction
all the changes are attributed to the first one having triggered
tracking (?), I couldn't find why the admin sometimes takes over.
- added and leveraged support for enum-backed selection fields
- moved variuous fields from forwardport to runbot_merge
- fix a migration which had never worked and which never run (because
I forgot to bump the version on the module)
- remove some unnecessary intermediate de/serialisation
fixes #673, fixes #309, fixes #792, fixes #846 (probably)
2023-10-31 13:42:07 +07:00
|
|
|
repo.make_commits(m2, Commit('first', tree={'m': 'm3'}), ref='heads/abranch')
|
|
|
|
prx = repo.make_pr(title='title', body='body', target='1.0', head='abranch')
|
|
|
|
repo.post_status('heads/abranch', 'success', 'a')
|
|
|
|
env.run_crons()
|
2024-12-02 20:17:28 +07:00
|
|
|
pr = to_pr(env, prx)
|
2019-08-27 16:23:34 +07:00
|
|
|
assert not pr.squash
|
[CHG] *: rewrite commands set, rework status management
This commit revisits the commands set in order to make it more
regular, and limit inconsistent command-sets, although it includes
pseudo-command aliases for common tasks now removed from the core set.
Hard Errors
===========
The previous iteration of the commands set would ignore any
non-command term in a command line. This has been changed to hard
error (and ignoring the entire thing) if any command is unknown or
invalid.
This fixes inconsistent / unexpected interpretations where a user
sends a command, then writes a novel on the same line some words of
which happen to *also* be commands, leading to merge states they did
not expect. They should now be told to fuck off.
Priority Restructuring
----------------------
The numerical priority system was pretty messy in that it confused
"staging priority" (in ways which were not entirely straightforward)
with overrides to other concerns.
This has now being split along all the axis, with separate command
subsets for:
- staging prioritisation, now separated between `default`, `priority`,
and `alone`,
- `default` means PRs are picked by an unspecified order when
creating a staging, if nothing better is available
- `priority` means PRs are picked first when staging, however if
`priority` PRs don't fill the staging the rest will be filled with
`default`, this mode did not previously exist
- `alone` means the PRs are picked first, before splits, and only
`alone` PRs can be part of the staging (which usually matches the
modename)
- `skipchecks` overrides both statuses and approval checks, for the
batch, something previously implied in `p=0`, but now
independent. Setting `skipchecks` basically makes the entire batch
`ready`.
For consistency this also sets the reviewer implicitly: since
skipchecks overrides both statuses *and approval*, whoever enables
this mode is essentially the reviewer.
- `cancel` cancels any ongoing staging when the marked PR becomes
ready again, previously this was also implied (in a more restricted
form) by setting `p=0`
FWBot removal
=============
While the "forwardport bot" still exists as an API level (to segregate
access rights between tokens) it has been removed as an interaction
point, as part of the modules merge plan. As a result,
fwbot stops responding
----------------------
Feedback messages are now always sent by the mergebot, the
forward-porting bot should not send any message or notification
anymore.
commands moved to the merge bot
-------------------------------
- `ignore`/`up to` simply changes bot
- `close` as well
- `skipci` is now a choice / flag of an `fw` command, which denotes
the forward-port policy,
- `fw=default` is the old `ci` and resets the policy to default,
that is wait for the PR to be merged to create forward ports, and
for the required statuses on each forward port to be received
before creating the next
- `fw=skipci` is the old `skipci`, it waits for the merge of the
base PR but then creates all the forward ports immediately (unless
it gets a conflict)
- `fw=skipmerge` immediately creates all the forward ports, without
even waiting for the PR to be merged
This is a completely new mode, and may be rather broken as until
now the 'bot has always assumed the source PR had been merged.
approval rework
---------------
Because of the previous section, there is no distinguishing feature
between `mergebot r+` = "merge this PR" and `forwardbot r+` = "merge
this PR and all its parent with different access rights".
As a result, the two have been merged under a single `mergebot r+`
with heuristics attempting to provide the best experience:
- if approving a non-forward port, the behavior does not change
- else, with review rights on the source, all ancestors are approved
- else, as author of the original, approves all ancestors which descend
from a merged PR
- else, approves all ancestors up to and including the oldest ancestor
to which we have review rights
Most notably, the source's author is not delegated on the source or
any of its descendants anymore. This might need to be revisited if it
provides too restrictive.
For the very specialized need of approving a forward-port *and none of
its ancestors*, `review=` can now take a comma (`,`) separated list of
pull request numbers (github numbers, not mergebot ids).
Computed State
==============
The `state` field of pull requests is now computed. Hopefully this
makes the status more consistent and predictable in the long run, and
importantly makes status management more reliable (because reference
datum get updated naturally flowing to the state).
For now however it makes things more complicated as some of the states
have to be separately signaled or updated:
- `closed` and `error` are now separate flags
- `merge_date` is pulled down from forwardport and becomes the
transition signal for ready -> merged
- `reviewed_by` becomes the transition signal for approval (might be a
good idea to rename it...)
- `status` is computed from the head's statuses and overrides, and
*that* becomes the validation state
Ideally, batch-level flags like `skipchecks` should be on, well, the
batch, and `state` should have a dependency on the batch. However
currently the batch is not a durable / permanent member of the system,
so it's a PR-level flag and a messy pile.
On notable change is that *forcing* the state to `ready` now does that
but also sets the reviewer, `skipchecks`, and overrides to ensure the
API-mediated readying does not get rolled back by e.g. the runbot
sending a status.
This is useful for a few types of automated / programmatic PRs
e.g. translation exports, where we set the state programmatically to
limit noise.
recursive dependency hack
-------------------------
Given a sequence of PRs with an override of the source, if one of the
PRs is updated its descendants should not have the override
anymore. However if the updated PR gets overridden, its descendants
should have *that* override.
This requires some unholy manipulations via an override of `modified`,
as the ORM supports recursive fields but not recursive
dependencies (on a different field).
unconditional followup scheduling
---------------------------------
Previously scheduling forward-port followup was contigent on the FW
policy, but it's not actually correct if the new PR is *immediately*
validated (which can happen now that the field is computed, if there
are no required statuses *or* all of the required statuses are
overridden by an ancestor) as nothing will trigger the state change
and thus scheduling of the fp followup.
The followup function checks all the properties of the batch to port,
so this should not result on incorrect ports. Although it's a bit more
expensive, and will lead to more spam.
Previously this would not happen because on creation of a PR the
validation task (commit -> PR) would still have to execute.
Misc Changes
============
- If a PR is marked as overriding / canceling stagings, it now does
so on retry not just when setting initially.
This was not handled at all previously, so a PR in P0 going into
error due to e.g. a non-deterministic bug would be retried and still
p=0, but a current staging would not get cancelled. Same when a PR
in p=0 goes into error because something was failed, then is updated
with a fix.
- Add tracking to a bunch of relevant PR fields.
Post-mortem analysis currently generally requires going through the
text logs to see what happened, which is annoying.
There is a nondeterminism / inconsistency in the tracking which
sometimes leads the admin user to trigger tracking before the bot
does, leading to the staging tracking being attributed to them
during tests, shove under the carpet by ignoring the user to whom
that tracking is attributed.
When multiple users update tracked fields in the same transaction
all the changes are attributed to the first one having triggered
tracking (?), I couldn't find why the admin sometimes takes over.
- added and leveraged support for enum-backed selection fields
- moved variuous fields from forwardport to runbot_merge
- fix a migration which had never worked and which never run (because
I forgot to bump the version on the module)
- remove some unnecessary intermediate de/serialisation
fixes #673, fixes #309, fixes #792, fixes #846 (probably)
2023-10-31 13:42:07 +07:00
|
|
|
assert pr.status == 'pending'
|
|
|
|
assert pr.state == 'opened'
|
2019-03-04 15:52:21 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
prx.base = 'master'
|
2019-08-27 16:23:34 +07:00
|
|
|
assert pr.target == master
|
|
|
|
assert pr.squash
|
[CHG] *: rewrite commands set, rework status management
This commit revisits the commands set in order to make it more
regular, and limit inconsistent command-sets, although it includes
pseudo-command aliases for common tasks now removed from the core set.
Hard Errors
===========
The previous iteration of the commands set would ignore any
non-command term in a command line. This has been changed to hard
error (and ignoring the entire thing) if any command is unknown or
invalid.
This fixes inconsistent / unexpected interpretations where a user
sends a command, then writes a novel on the same line some words of
which happen to *also* be commands, leading to merge states they did
not expect. They should now be told to fuck off.
Priority Restructuring
----------------------
The numerical priority system was pretty messy in that it confused
"staging priority" (in ways which were not entirely straightforward)
with overrides to other concerns.
This has now being split along all the axis, with separate command
subsets for:
- staging prioritisation, now separated between `default`, `priority`,
and `alone`,
- `default` means PRs are picked by an unspecified order when
creating a staging, if nothing better is available
- `priority` means PRs are picked first when staging, however if
`priority` PRs don't fill the staging the rest will be filled with
`default`, this mode did not previously exist
- `alone` means the PRs are picked first, before splits, and only
`alone` PRs can be part of the staging (which usually matches the
modename)
- `skipchecks` overrides both statuses and approval checks, for the
batch, something previously implied in `p=0`, but now
independent. Setting `skipchecks` basically makes the entire batch
`ready`.
For consistency this also sets the reviewer implicitly: since
skipchecks overrides both statuses *and approval*, whoever enables
this mode is essentially the reviewer.
- `cancel` cancels any ongoing staging when the marked PR becomes
ready again, previously this was also implied (in a more restricted
form) by setting `p=0`
FWBot removal
=============
While the "forwardport bot" still exists as an API level (to segregate
access rights between tokens) it has been removed as an interaction
point, as part of the modules merge plan. As a result,
fwbot stops responding
----------------------
Feedback messages are now always sent by the mergebot, the
forward-porting bot should not send any message or notification
anymore.
commands moved to the merge bot
-------------------------------
- `ignore`/`up to` simply changes bot
- `close` as well
- `skipci` is now a choice / flag of an `fw` command, which denotes
the forward-port policy,
- `fw=default` is the old `ci` and resets the policy to default,
that is wait for the PR to be merged to create forward ports, and
for the required statuses on each forward port to be received
before creating the next
- `fw=skipci` is the old `skipci`, it waits for the merge of the
base PR but then creates all the forward ports immediately (unless
it gets a conflict)
- `fw=skipmerge` immediately creates all the forward ports, without
even waiting for the PR to be merged
This is a completely new mode, and may be rather broken as until
now the 'bot has always assumed the source PR had been merged.
approval rework
---------------
Because of the previous section, there is no distinguishing feature
between `mergebot r+` = "merge this PR" and `forwardbot r+` = "merge
this PR and all its parent with different access rights".
As a result, the two have been merged under a single `mergebot r+`
with heuristics attempting to provide the best experience:
- if approving a non-forward port, the behavior does not change
- else, with review rights on the source, all ancestors are approved
- else, as author of the original, approves all ancestors which descend
from a merged PR
- else, approves all ancestors up to and including the oldest ancestor
to which we have review rights
Most notably, the source's author is not delegated on the source or
any of its descendants anymore. This might need to be revisited if it
provides too restrictive.
For the very specialized need of approving a forward-port *and none of
its ancestors*, `review=` can now take a comma (`,`) separated list of
pull request numbers (github numbers, not mergebot ids).
Computed State
==============
The `state` field of pull requests is now computed. Hopefully this
makes the status more consistent and predictable in the long run, and
importantly makes status management more reliable (because reference
datum get updated naturally flowing to the state).
For now however it makes things more complicated as some of the states
have to be separately signaled or updated:
- `closed` and `error` are now separate flags
- `merge_date` is pulled down from forwardport and becomes the
transition signal for ready -> merged
- `reviewed_by` becomes the transition signal for approval (might be a
good idea to rename it...)
- `status` is computed from the head's statuses and overrides, and
*that* becomes the validation state
Ideally, batch-level flags like `skipchecks` should be on, well, the
batch, and `state` should have a dependency on the batch. However
currently the batch is not a durable / permanent member of the system,
so it's a PR-level flag and a messy pile.
On notable change is that *forcing* the state to `ready` now does that
but also sets the reviewer, `skipchecks`, and overrides to ensure the
API-mediated readying does not get rolled back by e.g. the runbot
sending a status.
This is useful for a few types of automated / programmatic PRs
e.g. translation exports, where we set the state programmatically to
limit noise.
recursive dependency hack
-------------------------
Given a sequence of PRs with an override of the source, if one of the
PRs is updated its descendants should not have the override
anymore. However if the updated PR gets overridden, its descendants
should have *that* override.
This requires some unholy manipulations via an override of `modified`,
as the ORM supports recursive fields but not recursive
dependencies (on a different field).
unconditional followup scheduling
---------------------------------
Previously scheduling forward-port followup was contigent on the FW
policy, but it's not actually correct if the new PR is *immediately*
validated (which can happen now that the field is computed, if there
are no required statuses *or* all of the required statuses are
overridden by an ancestor) as nothing will trigger the state change
and thus scheduling of the fp followup.
The followup function checks all the properties of the batch to port,
so this should not result on incorrect ports. Although it's a bit more
expensive, and will lead to more spam.
Previously this would not happen because on creation of a PR the
validation task (commit -> PR) would still have to execute.
Misc Changes
============
- If a PR is marked as overriding / canceling stagings, it now does
so on retry not just when setting initially.
This was not handled at all previously, so a PR in P0 going into
error due to e.g. a non-deterministic bug would be retried and still
p=0, but a current staging would not get cancelled. Same when a PR
in p=0 goes into error because something was failed, then is updated
with a fix.
- Add tracking to a bunch of relevant PR fields.
Post-mortem analysis currently generally requires going through the
text logs to see what happened, which is annoying.
There is a nondeterminism / inconsistency in the tracking which
sometimes leads the admin user to trigger tracking before the bot
does, leading to the staging tracking being attributed to them
during tests, shove under the carpet by ignoring the user to whom
that tracking is attributed.
When multiple users update tracked fields in the same transaction
all the changes are attributed to the first one having triggered
tracking (?), I couldn't find why the admin sometimes takes over.
- added and leveraged support for enum-backed selection fields
- moved variuous fields from forwardport to runbot_merge
- fix a migration which had never worked and which never run (because
I forgot to bump the version on the module)
- remove some unnecessary intermediate de/serialisation
fixes #673, fixes #309, fixes #792, fixes #846 (probably)
2023-10-31 13:42:07 +07:00
|
|
|
assert pr.status == 'success'
|
|
|
|
assert pr.state == 'validated'
|
2019-03-04 15:52:21 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
prx.base = '1.0'
|
2019-08-27 16:23:34 +07:00
|
|
|
assert pr.target == branch_1
|
|
|
|
assert not pr.squash
|
[CHG] *: rewrite commands set, rework status management
This commit revisits the commands set in order to make it more
regular, and limit inconsistent command-sets, although it includes
pseudo-command aliases for common tasks now removed from the core set.
Hard Errors
===========
The previous iteration of the commands set would ignore any
non-command term in a command line. This has been changed to hard
error (and ignoring the entire thing) if any command is unknown or
invalid.
This fixes inconsistent / unexpected interpretations where a user
sends a command, then writes a novel on the same line some words of
which happen to *also* be commands, leading to merge states they did
not expect. They should now be told to fuck off.
Priority Restructuring
----------------------
The numerical priority system was pretty messy in that it confused
"staging priority" (in ways which were not entirely straightforward)
with overrides to other concerns.
This has now being split along all the axis, with separate command
subsets for:
- staging prioritisation, now separated between `default`, `priority`,
and `alone`,
- `default` means PRs are picked by an unspecified order when
creating a staging, if nothing better is available
- `priority` means PRs are picked first when staging, however if
`priority` PRs don't fill the staging the rest will be filled with
`default`, this mode did not previously exist
- `alone` means the PRs are picked first, before splits, and only
`alone` PRs can be part of the staging (which usually matches the
modename)
- `skipchecks` overrides both statuses and approval checks, for the
batch, something previously implied in `p=0`, but now
independent. Setting `skipchecks` basically makes the entire batch
`ready`.
For consistency this also sets the reviewer implicitly: since
skipchecks overrides both statuses *and approval*, whoever enables
this mode is essentially the reviewer.
- `cancel` cancels any ongoing staging when the marked PR becomes
ready again, previously this was also implied (in a more restricted
form) by setting `p=0`
FWBot removal
=============
While the "forwardport bot" still exists as an API level (to segregate
access rights between tokens) it has been removed as an interaction
point, as part of the modules merge plan. As a result,
fwbot stops responding
----------------------
Feedback messages are now always sent by the mergebot, the
forward-porting bot should not send any message or notification
anymore.
commands moved to the merge bot
-------------------------------
- `ignore`/`up to` simply changes bot
- `close` as well
- `skipci` is now a choice / flag of an `fw` command, which denotes
the forward-port policy,
- `fw=default` is the old `ci` and resets the policy to default,
that is wait for the PR to be merged to create forward ports, and
for the required statuses on each forward port to be received
before creating the next
- `fw=skipci` is the old `skipci`, it waits for the merge of the
base PR but then creates all the forward ports immediately (unless
it gets a conflict)
- `fw=skipmerge` immediately creates all the forward ports, without
even waiting for the PR to be merged
This is a completely new mode, and may be rather broken as until
now the 'bot has always assumed the source PR had been merged.
approval rework
---------------
Because of the previous section, there is no distinguishing feature
between `mergebot r+` = "merge this PR" and `forwardbot r+` = "merge
this PR and all its parent with different access rights".
As a result, the two have been merged under a single `mergebot r+`
with heuristics attempting to provide the best experience:
- if approving a non-forward port, the behavior does not change
- else, with review rights on the source, all ancestors are approved
- else, as author of the original, approves all ancestors which descend
from a merged PR
- else, approves all ancestors up to and including the oldest ancestor
to which we have review rights
Most notably, the source's author is not delegated on the source or
any of its descendants anymore. This might need to be revisited if it
provides too restrictive.
For the very specialized need of approving a forward-port *and none of
its ancestors*, `review=` can now take a comma (`,`) separated list of
pull request numbers (github numbers, not mergebot ids).
Computed State
==============
The `state` field of pull requests is now computed. Hopefully this
makes the status more consistent and predictable in the long run, and
importantly makes status management more reliable (because reference
datum get updated naturally flowing to the state).
For now however it makes things more complicated as some of the states
have to be separately signaled or updated:
- `closed` and `error` are now separate flags
- `merge_date` is pulled down from forwardport and becomes the
transition signal for ready -> merged
- `reviewed_by` becomes the transition signal for approval (might be a
good idea to rename it...)
- `status` is computed from the head's statuses and overrides, and
*that* becomes the validation state
Ideally, batch-level flags like `skipchecks` should be on, well, the
batch, and `state` should have a dependency on the batch. However
currently the batch is not a durable / permanent member of the system,
so it's a PR-level flag and a messy pile.
On notable change is that *forcing* the state to `ready` now does that
but also sets the reviewer, `skipchecks`, and overrides to ensure the
API-mediated readying does not get rolled back by e.g. the runbot
sending a status.
This is useful for a few types of automated / programmatic PRs
e.g. translation exports, where we set the state programmatically to
limit noise.
recursive dependency hack
-------------------------
Given a sequence of PRs with an override of the source, if one of the
PRs is updated its descendants should not have the override
anymore. However if the updated PR gets overridden, its descendants
should have *that* override.
This requires some unholy manipulations via an override of `modified`,
as the ORM supports recursive fields but not recursive
dependencies (on a different field).
unconditional followup scheduling
---------------------------------
Previously scheduling forward-port followup was contigent on the FW
policy, but it's not actually correct if the new PR is *immediately*
validated (which can happen now that the field is computed, if there
are no required statuses *or* all of the required statuses are
overridden by an ancestor) as nothing will trigger the state change
and thus scheduling of the fp followup.
The followup function checks all the properties of the batch to port,
so this should not result on incorrect ports. Although it's a bit more
expensive, and will lead to more spam.
Previously this would not happen because on creation of a PR the
validation task (commit -> PR) would still have to execute.
Misc Changes
============
- If a PR is marked as overriding / canceling stagings, it now does
so on retry not just when setting initially.
This was not handled at all previously, so a PR in P0 going into
error due to e.g. a non-deterministic bug would be retried and still
p=0, but a current staging would not get cancelled. Same when a PR
in p=0 goes into error because something was failed, then is updated
with a fix.
- Add tracking to a bunch of relevant PR fields.
Post-mortem analysis currently generally requires going through the
text logs to see what happened, which is annoying.
There is a nondeterminism / inconsistency in the tracking which
sometimes leads the admin user to trigger tracking before the bot
does, leading to the staging tracking being attributed to them
during tests, shove under the carpet by ignoring the user to whom
that tracking is attributed.
When multiple users update tracked fields in the same transaction
all the changes are attributed to the first one having triggered
tracking (?), I couldn't find why the admin sometimes takes over.
- added and leveraged support for enum-backed selection fields
- moved variuous fields from forwardport to runbot_merge
- fix a migration which had never worked and which never run (because
I forgot to bump the version on the module)
- remove some unnecessary intermediate de/serialisation
fixes #673, fixes #309, fixes #792, fixes #846 (probably)
2023-10-31 13:42:07 +07:00
|
|
|
assert pr.status == 'pending'
|
|
|
|
assert pr.state == 'opened'
|
2019-03-04 15:52:21 +07:00
|
|
|
|
2019-11-18 20:21:16 +07:00
|
|
|
# check if things also work right when modifying the PR then
|
|
|
|
# retargeting (don't see why not but...)
|
|
|
|
with repo:
|
|
|
|
c2 = repo.make_commit(m2, 'xxx', None, tree={'m': 'm4'})
|
|
|
|
repo.update_ref(prx.ref, c2, force=True)
|
|
|
|
assert pr.head == c2
|
|
|
|
assert not pr.squash
|
|
|
|
with repo:
|
|
|
|
prx.base = 'master'
|
|
|
|
assert pr.squash
|
|
|
|
|
2020-02-07 22:15:26 +07:00
|
|
|
@pytest.mark.xfail(reason="github doesn't allow retargeting closed PRs", strict=True)
|
|
|
|
def test_retarget_closed(self, env, repo):
|
|
|
|
branch_1 = env['runbot_merge.branch'].create({
|
|
|
|
'name': '1.0',
|
|
|
|
'project_id': env['runbot_merge.project'].search([]).id,
|
|
|
|
})
|
|
|
|
|
|
|
|
with repo:
|
|
|
|
# master is 1 commit ahead of 1.0
|
|
|
|
[m] = repo.make_commits(None, repo.Commit('initial', tree={'1': '1'}), ref='heads/1.0')
|
|
|
|
repo.make_commits(m, repo.Commit('second', tree={'m': 'm'}), ref='heads/master')
|
|
|
|
|
|
|
|
[c] = repo.make_commits(m, repo.Commit('first', tree={'m': 'm3'}), ref='heads/abranch')
|
|
|
|
prx = repo.make_pr(title='title', body='body', target='1.0', head=c)
|
|
|
|
env.run_crons()
|
2024-12-02 20:17:28 +07:00
|
|
|
pr = to_pr(env, prx)
|
2020-02-07 22:15:26 +07:00
|
|
|
assert pr.target == branch_1
|
|
|
|
with repo:
|
|
|
|
prx.close()
|
|
|
|
with repo:
|
|
|
|
prx.base = 'master'
|
|
|
|
|
2020-11-17 21:21:21 +07:00
|
|
|
def test_close_staged(env, repo, config, page):
|
2018-05-31 21:50:36 +07:00
|
|
|
"""
|
|
|
|
When closing a staged PR, cancel the staging
|
|
|
|
"""
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
m = repo.make_commit(None, 'initial', None, tree={'m': 'm'})
|
|
|
|
repo.make_ref('heads/master', m)
|
|
|
|
|
|
|
|
c = repo.make_commit(m, 'fist', None, tree={'m': 'c1'})
|
|
|
|
prx = repo.make_pr(title='title', body='body', target='master', head=c)
|
|
|
|
repo.post_status(prx.head, 'success', 'legal/cla')
|
|
|
|
repo.post_status(prx.head, 'success', 'ci/runbot')
|
|
|
|
prx.post_comment('hansen r+', config['role_reviewer']['token'])
|
2024-12-02 20:17:28 +07:00
|
|
|
pr = to_pr(env, prx)
|
2019-10-10 14:22:12 +07:00
|
|
|
env.run_crons()
|
[CHG] *: rewrite commands set, rework status management
This commit revisits the commands set in order to make it more
regular, and limit inconsistent command-sets, although it includes
pseudo-command aliases for common tasks now removed from the core set.
Hard Errors
===========
The previous iteration of the commands set would ignore any
non-command term in a command line. This has been changed to hard
error (and ignoring the entire thing) if any command is unknown or
invalid.
This fixes inconsistent / unexpected interpretations where a user
sends a command, then writes a novel on the same line some words of
which happen to *also* be commands, leading to merge states they did
not expect. They should now be told to fuck off.
Priority Restructuring
----------------------
The numerical priority system was pretty messy in that it confused
"staging priority" (in ways which were not entirely straightforward)
with overrides to other concerns.
This has now being split along all the axis, with separate command
subsets for:
- staging prioritisation, now separated between `default`, `priority`,
and `alone`,
- `default` means PRs are picked by an unspecified order when
creating a staging, if nothing better is available
- `priority` means PRs are picked first when staging, however if
`priority` PRs don't fill the staging the rest will be filled with
`default`, this mode did not previously exist
- `alone` means the PRs are picked first, before splits, and only
`alone` PRs can be part of the staging (which usually matches the
modename)
- `skipchecks` overrides both statuses and approval checks, for the
batch, something previously implied in `p=0`, but now
independent. Setting `skipchecks` basically makes the entire batch
`ready`.
For consistency this also sets the reviewer implicitly: since
skipchecks overrides both statuses *and approval*, whoever enables
this mode is essentially the reviewer.
- `cancel` cancels any ongoing staging when the marked PR becomes
ready again, previously this was also implied (in a more restricted
form) by setting `p=0`
FWBot removal
=============
While the "forwardport bot" still exists as an API level (to segregate
access rights between tokens) it has been removed as an interaction
point, as part of the modules merge plan. As a result,
fwbot stops responding
----------------------
Feedback messages are now always sent by the mergebot, the
forward-porting bot should not send any message or notification
anymore.
commands moved to the merge bot
-------------------------------
- `ignore`/`up to` simply changes bot
- `close` as well
- `skipci` is now a choice / flag of an `fw` command, which denotes
the forward-port policy,
- `fw=default` is the old `ci` and resets the policy to default,
that is wait for the PR to be merged to create forward ports, and
for the required statuses on each forward port to be received
before creating the next
- `fw=skipci` is the old `skipci`, it waits for the merge of the
base PR but then creates all the forward ports immediately (unless
it gets a conflict)
- `fw=skipmerge` immediately creates all the forward ports, without
even waiting for the PR to be merged
This is a completely new mode, and may be rather broken as until
now the 'bot has always assumed the source PR had been merged.
approval rework
---------------
Because of the previous section, there is no distinguishing feature
between `mergebot r+` = "merge this PR" and `forwardbot r+` = "merge
this PR and all its parent with different access rights".
As a result, the two have been merged under a single `mergebot r+`
with heuristics attempting to provide the best experience:
- if approving a non-forward port, the behavior does not change
- else, with review rights on the source, all ancestors are approved
- else, as author of the original, approves all ancestors which descend
from a merged PR
- else, approves all ancestors up to and including the oldest ancestor
to which we have review rights
Most notably, the source's author is not delegated on the source or
any of its descendants anymore. This might need to be revisited if it
provides too restrictive.
For the very specialized need of approving a forward-port *and none of
its ancestors*, `review=` can now take a comma (`,`) separated list of
pull request numbers (github numbers, not mergebot ids).
Computed State
==============
The `state` field of pull requests is now computed. Hopefully this
makes the status more consistent and predictable in the long run, and
importantly makes status management more reliable (because reference
datum get updated naturally flowing to the state).
For now however it makes things more complicated as some of the states
have to be separately signaled or updated:
- `closed` and `error` are now separate flags
- `merge_date` is pulled down from forwardport and becomes the
transition signal for ready -> merged
- `reviewed_by` becomes the transition signal for approval (might be a
good idea to rename it...)
- `status` is computed from the head's statuses and overrides, and
*that* becomes the validation state
Ideally, batch-level flags like `skipchecks` should be on, well, the
batch, and `state` should have a dependency on the batch. However
currently the batch is not a durable / permanent member of the system,
so it's a PR-level flag and a messy pile.
On notable change is that *forcing* the state to `ready` now does that
but also sets the reviewer, `skipchecks`, and overrides to ensure the
API-mediated readying does not get rolled back by e.g. the runbot
sending a status.
This is useful for a few types of automated / programmatic PRs
e.g. translation exports, where we set the state programmatically to
limit noise.
recursive dependency hack
-------------------------
Given a sequence of PRs with an override of the source, if one of the
PRs is updated its descendants should not have the override
anymore. However if the updated PR gets overridden, its descendants
should have *that* override.
This requires some unholy manipulations via an override of `modified`,
as the ORM supports recursive fields but not recursive
dependencies (on a different field).
unconditional followup scheduling
---------------------------------
Previously scheduling forward-port followup was contigent on the FW
policy, but it's not actually correct if the new PR is *immediately*
validated (which can happen now that the field is computed, if there
are no required statuses *or* all of the required statuses are
overridden by an ancestor) as nothing will trigger the state change
and thus scheduling of the fp followup.
The followup function checks all the properties of the batch to port,
so this should not result on incorrect ports. Although it's a bit more
expensive, and will lead to more spam.
Previously this would not happen because on creation of a PR the
validation task (commit -> PR) would still have to execute.
Misc Changes
============
- If a PR is marked as overriding / canceling stagings, it now does
so on retry not just when setting initially.
This was not handled at all previously, so a PR in P0 going into
error due to e.g. a non-deterministic bug would be retried and still
p=0, but a current staging would not get cancelled. Same when a PR
in p=0 goes into error because something was failed, then is updated
with a fix.
- Add tracking to a bunch of relevant PR fields.
Post-mortem analysis currently generally requires going through the
text logs to see what happened, which is annoying.
There is a nondeterminism / inconsistency in the tracking which
sometimes leads the admin user to trigger tracking before the bot
does, leading to the staging tracking being attributed to them
during tests, shove under the carpet by ignoring the user to whom
that tracking is attributed.
When multiple users update tracked fields in the same transaction
all the changes are attributed to the first one having triggered
tracking (?), I couldn't find why the admin sometimes takes over.
- added and leveraged support for enum-backed selection fields
- moved variuous fields from forwardport to runbot_merge
- fix a migration which had never worked and which never run (because
I forgot to bump the version on the module)
- remove some unnecessary intermediate de/serialisation
fixes #673, fixes #309, fixes #792, fixes #846 (probably)
2023-10-31 13:42:07 +07:00
|
|
|
assert pr.reviewed_by
|
2018-05-31 21:50:36 +07:00
|
|
|
assert pr.state == 'ready'
|
|
|
|
assert pr.staging_id
|
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
prx.close()
|
|
|
|
env.run_crons()
|
2018-05-31 21:50:36 +07:00
|
|
|
|
|
|
|
assert not pr.staging_id
|
|
|
|
assert not env['runbot_merge.stagings'].search([])
|
2018-11-22 22:01:44 +07:00
|
|
|
assert pr.state == 'closed'
|
2020-11-17 21:21:21 +07:00
|
|
|
assert pr_page(page, prx).cssselect('.alert-light')
|
[CHG] *: rewrite commands set, rework status management
This commit revisits the commands set in order to make it more
regular, and limit inconsistent command-sets, although it includes
pseudo-command aliases for common tasks now removed from the core set.
Hard Errors
===========
The previous iteration of the commands set would ignore any
non-command term in a command line. This has been changed to hard
error (and ignoring the entire thing) if any command is unknown or
invalid.
This fixes inconsistent / unexpected interpretations where a user
sends a command, then writes a novel on the same line some words of
which happen to *also* be commands, leading to merge states they did
not expect. They should now be told to fuck off.
Priority Restructuring
----------------------
The numerical priority system was pretty messy in that it confused
"staging priority" (in ways which were not entirely straightforward)
with overrides to other concerns.
This has now being split along all the axis, with separate command
subsets for:
- staging prioritisation, now separated between `default`, `priority`,
and `alone`,
- `default` means PRs are picked by an unspecified order when
creating a staging, if nothing better is available
- `priority` means PRs are picked first when staging, however if
`priority` PRs don't fill the staging the rest will be filled with
`default`, this mode did not previously exist
- `alone` means the PRs are picked first, before splits, and only
`alone` PRs can be part of the staging (which usually matches the
modename)
- `skipchecks` overrides both statuses and approval checks, for the
batch, something previously implied in `p=0`, but now
independent. Setting `skipchecks` basically makes the entire batch
`ready`.
For consistency this also sets the reviewer implicitly: since
skipchecks overrides both statuses *and approval*, whoever enables
this mode is essentially the reviewer.
- `cancel` cancels any ongoing staging when the marked PR becomes
ready again, previously this was also implied (in a more restricted
form) by setting `p=0`
FWBot removal
=============
While the "forwardport bot" still exists as an API level (to segregate
access rights between tokens) it has been removed as an interaction
point, as part of the modules merge plan. As a result,
fwbot stops responding
----------------------
Feedback messages are now always sent by the mergebot, the
forward-porting bot should not send any message or notification
anymore.
commands moved to the merge bot
-------------------------------
- `ignore`/`up to` simply changes bot
- `close` as well
- `skipci` is now a choice / flag of an `fw` command, which denotes
the forward-port policy,
- `fw=default` is the old `ci` and resets the policy to default,
that is wait for the PR to be merged to create forward ports, and
for the required statuses on each forward port to be received
before creating the next
- `fw=skipci` is the old `skipci`, it waits for the merge of the
base PR but then creates all the forward ports immediately (unless
it gets a conflict)
- `fw=skipmerge` immediately creates all the forward ports, without
even waiting for the PR to be merged
This is a completely new mode, and may be rather broken as until
now the 'bot has always assumed the source PR had been merged.
approval rework
---------------
Because of the previous section, there is no distinguishing feature
between `mergebot r+` = "merge this PR" and `forwardbot r+` = "merge
this PR and all its parent with different access rights".
As a result, the two have been merged under a single `mergebot r+`
with heuristics attempting to provide the best experience:
- if approving a non-forward port, the behavior does not change
- else, with review rights on the source, all ancestors are approved
- else, as author of the original, approves all ancestors which descend
from a merged PR
- else, approves all ancestors up to and including the oldest ancestor
to which we have review rights
Most notably, the source's author is not delegated on the source or
any of its descendants anymore. This might need to be revisited if it
provides too restrictive.
For the very specialized need of approving a forward-port *and none of
its ancestors*, `review=` can now take a comma (`,`) separated list of
pull request numbers (github numbers, not mergebot ids).
Computed State
==============
The `state` field of pull requests is now computed. Hopefully this
makes the status more consistent and predictable in the long run, and
importantly makes status management more reliable (because reference
datum get updated naturally flowing to the state).
For now however it makes things more complicated as some of the states
have to be separately signaled or updated:
- `closed` and `error` are now separate flags
- `merge_date` is pulled down from forwardport and becomes the
transition signal for ready -> merged
- `reviewed_by` becomes the transition signal for approval (might be a
good idea to rename it...)
- `status` is computed from the head's statuses and overrides, and
*that* becomes the validation state
Ideally, batch-level flags like `skipchecks` should be on, well, the
batch, and `state` should have a dependency on the batch. However
currently the batch is not a durable / permanent member of the system,
so it's a PR-level flag and a messy pile.
On notable change is that *forcing* the state to `ready` now does that
but also sets the reviewer, `skipchecks`, and overrides to ensure the
API-mediated readying does not get rolled back by e.g. the runbot
sending a status.
This is useful for a few types of automated / programmatic PRs
e.g. translation exports, where we set the state programmatically to
limit noise.
recursive dependency hack
-------------------------
Given a sequence of PRs with an override of the source, if one of the
PRs is updated its descendants should not have the override
anymore. However if the updated PR gets overridden, its descendants
should have *that* override.
This requires some unholy manipulations via an override of `modified`,
as the ORM supports recursive fields but not recursive
dependencies (on a different field).
unconditional followup scheduling
---------------------------------
Previously scheduling forward-port followup was contigent on the FW
policy, but it's not actually correct if the new PR is *immediately*
validated (which can happen now that the field is computed, if there
are no required statuses *or* all of the required statuses are
overridden by an ancestor) as nothing will trigger the state change
and thus scheduling of the fp followup.
The followup function checks all the properties of the batch to port,
so this should not result on incorrect ports. Although it's a bit more
expensive, and will lead to more spam.
Previously this would not happen because on creation of a PR the
validation task (commit -> PR) would still have to execute.
Misc Changes
============
- If a PR is marked as overriding / canceling stagings, it now does
so on retry not just when setting initially.
This was not handled at all previously, so a PR in P0 going into
error due to e.g. a non-deterministic bug would be retried and still
p=0, but a current staging would not get cancelled. Same when a PR
in p=0 goes into error because something was failed, then is updated
with a fix.
- Add tracking to a bunch of relevant PR fields.
Post-mortem analysis currently generally requires going through the
text logs to see what happened, which is annoying.
There is a nondeterminism / inconsistency in the tracking which
sometimes leads the admin user to trigger tracking before the bot
does, leading to the staging tracking being attributed to them
during tests, shove under the carpet by ignoring the user to whom
that tracking is attributed.
When multiple users update tracked fields in the same transaction
all the changes are attributed to the first one having triggered
tracking (?), I couldn't find why the admin sometimes takes over.
- added and leveraged support for enum-backed selection fields
- moved variuous fields from forwardport to runbot_merge
- fix a migration which had never worked and which never run (because
I forgot to bump the version on the module)
- remove some unnecessary intermediate de/serialisation
fixes #673, fixes #309, fixes #792, fixes #846 (probably)
2023-10-31 13:42:07 +07:00
|
|
|
assert not pr.reviewed_by
|
|
|
|
|
|
|
|
with repo:
|
|
|
|
prx.open()
|
|
|
|
assert pr.state == 'validated'
|
|
|
|
assert not pr.reviewed_by
|
|
|
|
|
|
|
|
with repo:
|
|
|
|
prx.post_comment('hansen r+', config['role_reviewer']['token'])
|
|
|
|
assert pr.reviewed_by
|
|
|
|
pr.write({'closed': True})
|
|
|
|
assert not pr.reviewed_by
|
2018-03-14 16:37:46 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
def test_forward_port(env, repo, config):
|
|
|
|
with repo:
|
|
|
|
m = repo.make_commit(None, 'initial', None, tree={'m': 'm'})
|
|
|
|
repo.make_ref('heads/master', m)
|
|
|
|
|
|
|
|
head = m
|
|
|
|
for i in range(110):
|
|
|
|
head = repo.make_commit(head, 'c_%03d' % i, None, tree={'m': 'm', 'f': str(i)})
|
|
|
|
# not sure why we wanted to wait here
|
|
|
|
|
|
|
|
with repo:
|
|
|
|
pr = repo.make_pr(title='PR', body=None, target='master', head=head)
|
|
|
|
repo.post_status(pr.head, 'success', 'legal/cla')
|
|
|
|
repo.post_status(pr.head, 'success', 'ci/runbot')
|
|
|
|
pr.post_comment('hansen r+ merge', config['role_reviewer']['token'])
|
|
|
|
env.run_crons()
|
2018-09-20 20:02:18 +07:00
|
|
|
|
2021-08-09 18:21:24 +07:00
|
|
|
st = repo.commit('staging.master')
|
2018-09-20 20:02:18 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
2024-06-03 17:59:24 +07:00
|
|
|
repo.post_status('staging.master', 'success', 'legal/cla')
|
|
|
|
repo.post_status('staging.master', 'success', 'ci/runbot')
|
2019-10-10 14:22:12 +07:00
|
|
|
env.run_crons()
|
2018-09-20 20:02:18 +07:00
|
|
|
|
2021-08-09 18:21:24 +07:00
|
|
|
h = repo.commit('master')
|
|
|
|
assert st.id == h.id
|
2018-09-20 20:02:18 +07:00
|
|
|
assert set(h.parents) == {m, pr.head}
|
2021-08-09 18:21:24 +07:00
|
|
|
commits = {c['sha'] for c in repo.log('master')}
|
2018-09-20 20:02:18 +07:00
|
|
|
assert len(commits) == 112
|
|
|
|
|
2019-10-14 14:33:21 +07:00
|
|
|
@pytest.mark.skip("Needs to find a way to make set_ref fail on *second* call.")
|
2019-10-10 14:22:12 +07:00
|
|
|
def test_rebase_failure(env, repo, users, config):
|
2018-10-17 19:18:49 +07:00
|
|
|
""" It looks like gh.rebase() can fail in the final ref-setting after
|
|
|
|
the merging & commits creation has been performed. At this point, the
|
|
|
|
staging will fail (yay) but the target branch (tmp) would not get reset,
|
|
|
|
leading to the next PR being staged *on top* of the one being staged
|
|
|
|
right there, and pretty much integrating it, leading to very, very
|
|
|
|
strange results if the entire thing passes staging.
|
|
|
|
|
|
|
|
Seen: https://github.com/odoo/odoo/pull/27835#issuecomment-430505429
|
|
|
|
PR 27835 was merged to tmp at df0ae6c00e085dbaabcfec821208c9ace2f4b02d
|
|
|
|
then the set_ref failed, following which PR 27840 is merged to tmp at
|
|
|
|
819b5414c27a92031a9ce3f159a8f466a4fd698c note that the first (left)
|
|
|
|
parent is the merge commit from PR 27835. The set_ref of PR 27840
|
|
|
|
succeeded resulting in PR 27835 being integrated into the squashing of
|
|
|
|
27840 (without any renaming or anything, just the content), following
|
|
|
|
which PR 27835 was merged and squashed as a "no-content" commit.
|
|
|
|
|
|
|
|
Problem: I need to make try_staging > stage > rebase > set_ref fail
|
|
|
|
but only the first time, and not the set_ref in try_staging itself, and
|
|
|
|
that call is performed *in a subprocess* when running <remote> tests.
|
|
|
|
"""
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
m = repo.make_commit(None, 'initial', None, tree={'m': 'm'})
|
|
|
|
repo.make_ref('heads/master', m)
|
2018-10-17 19:18:49 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
commit_a = repo.make_commit(m, 'A', None, tree={'m': 'm', 'a': 'a'})
|
|
|
|
repo.make_ref('heads/a', commit_a)
|
|
|
|
pr_a = repo.make_pr(title='A', body=None, target='master', head='a')
|
|
|
|
repo.post_status(pr_a.head, 'success', 'ci/runbot')
|
|
|
|
repo.post_status(pr_a.head, 'success', 'legal/cla')
|
|
|
|
pr_a.post_comment('hansen r+', config['role_reviewer']['token'])
|
2018-10-17 19:18:49 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
commit_b = repo.make_commit(m, 'B', None, tree={'m': 'm', 'b': 'b'})
|
|
|
|
repo.make_ref('heads/b', commit_b)
|
|
|
|
pr_b = repo.make_pr(title='B', body=None, target='master', head='b')
|
|
|
|
repo.post_status(pr_b.head, 'success', 'ci/runbot')
|
|
|
|
repo.post_status(pr_b.head, 'success', 'legal/cla')
|
|
|
|
pr_b.post_comment('hansen r+', config['role_reviewer']['token'])
|
2018-10-17 19:18:49 +07:00
|
|
|
|
|
|
|
from odoo.addons.runbot_merge.github import GH
|
|
|
|
original = GH.set_ref
|
|
|
|
counter = itertools.count(start=1)
|
|
|
|
def wrapper(*args):
|
|
|
|
assert next(counter) != 2, "make it seem like updating the branch post-rebase fails"
|
|
|
|
return original(*args)
|
|
|
|
|
2019-03-05 14:01:38 +07:00
|
|
|
env['runbot_merge.commit']._notify()
|
2021-01-13 14:18:17 +07:00
|
|
|
with mock.patch.object(GH, 'set_ref', autospec=True, side_effect=wrapper):
|
2018-10-17 19:18:49 +07:00
|
|
|
env['runbot_merge.project']._check_progress()
|
|
|
|
|
2021-11-10 19:13:34 +07:00
|
|
|
env['runbot_merge.pull_requests.feedback']._send()
|
2018-11-27 17:53:10 +07:00
|
|
|
|
2018-10-17 19:18:49 +07:00
|
|
|
assert pr_a.comments == [
|
|
|
|
(users['reviewer'], 'hansen r+'),
|
2020-11-17 21:21:21 +07:00
|
|
|
seen(env, pr_a, users),
|
2024-06-04 17:15:29 +07:00
|
|
|
(users['user'], matches('Unable to stage PR')),
|
2018-10-17 19:18:49 +07:00
|
|
|
]
|
|
|
|
assert pr_b.comments == [
|
|
|
|
(users['reviewer'], 'hansen r+'),
|
2020-11-17 21:21:21 +07:00
|
|
|
seen(env, pr_b, users),
|
2018-10-17 19:18:49 +07:00
|
|
|
]
|
|
|
|
assert repo.read_tree(repo.commit('heads/staging.master')) == {
|
2019-08-23 21:16:30 +07:00
|
|
|
'm': 'm',
|
|
|
|
'b': 'b',
|
2018-10-17 19:18:49 +07:00
|
|
|
}
|
|
|
|
|
2020-02-10 15:48:03 +07:00
|
|
|
def test_reopen_merged_pr(env, repo, config, users):
|
|
|
|
""" Reopening a *merged* PR should cause us to immediately close it again,
|
|
|
|
and insult whoever did it
|
|
|
|
"""
|
|
|
|
with repo:
|
|
|
|
[m] = repo.make_commits(
|
|
|
|
None,
|
|
|
|
repo.Commit('initial', tree={'0': '0'}),
|
|
|
|
ref = 'heads/master'
|
|
|
|
)
|
|
|
|
|
|
|
|
[c] = repo.make_commits(
|
|
|
|
m, repo.Commit('second', tree={'0': '1'}),
|
|
|
|
ref='heads/abranch'
|
|
|
|
)
|
|
|
|
prx = repo.make_pr(target='master', head='abranch')
|
|
|
|
repo.post_status(c, 'success', 'legal/cla')
|
|
|
|
repo.post_status(c, 'success', 'ci/runbot')
|
|
|
|
prx.post_comment('hansen r+', config['role_reviewer']['token'])
|
|
|
|
env.run_crons()
|
|
|
|
|
|
|
|
with repo:
|
|
|
|
repo.post_status('staging.master', 'success', 'legal/cla')
|
|
|
|
repo.post_status('staging.master', 'success', 'ci/runbot')
|
|
|
|
env.run_crons()
|
2024-12-02 20:17:28 +07:00
|
|
|
pr = to_pr(env, prx)
|
2020-02-10 15:48:03 +07:00
|
|
|
assert prx.state == 'closed'
|
|
|
|
assert pr.state == 'merged'
|
|
|
|
|
|
|
|
repo.add_collaborator(users['other'], config['role_other']['token'])
|
|
|
|
with repo:
|
|
|
|
prx.open(config['role_other']['token'])
|
|
|
|
env.run_crons()
|
|
|
|
assert prx.state == 'closed'
|
|
|
|
assert pr.state == 'merged'
|
|
|
|
assert prx.comments == [
|
|
|
|
(users['reviewer'], 'hansen r+'),
|
2020-11-17 21:21:21 +07:00
|
|
|
seen(env, prx, users),
|
2022-06-23 19:25:07 +07:00
|
|
|
(users['user'], "@%s ya silly goose you can't reopen a merged PR." % users['other'])
|
2020-02-10 15:48:03 +07:00
|
|
|
]
|
|
|
|
|
2020-02-07 22:11:12 +07:00
|
|
|
class TestNoRequiredStatus:
|
2024-07-31 14:40:53 +07:00
|
|
|
@pytest.mark.defaultstatuses
|
2020-02-07 22:11:12 +07:00
|
|
|
def test_basic(self, env, repo, config):
|
|
|
|
""" check that mergebot can work on a repo with no CI at all
|
|
|
|
"""
|
2020-07-10 15:21:43 +07:00
|
|
|
env['runbot_merge.repository'].search([('name', '=', repo.name)]).status_ids = False
|
2020-02-07 22:11:12 +07:00
|
|
|
with repo:
|
2024-07-31 14:40:53 +07:00
|
|
|
[m, c] = repo.make_commits(
|
|
|
|
None,
|
|
|
|
Commit('initial', tree={'0': '0'}),
|
|
|
|
Commit('first', tree={'0': '1'}),
|
|
|
|
)
|
2020-02-07 22:11:12 +07:00
|
|
|
repo.make_ref('heads/master', m)
|
2019-10-03 21:04:30 +07:00
|
|
|
|
2024-07-31 14:40:53 +07:00
|
|
|
pr = repo.make_pr(title='title', body='body', target='master', head=c)
|
|
|
|
pr.post_comment('hansen r+', config['role_reviewer']['token'])
|
2020-02-07 22:11:12 +07:00
|
|
|
env.run_crons()
|
2019-10-03 21:04:30 +07:00
|
|
|
|
2024-07-31 14:40:53 +07:00
|
|
|
pr_id = to_pr(env, pr)
|
|
|
|
|
|
|
|
st = env['runbot_merge.stagings'].search([], context={'active_test': False})
|
2020-02-07 22:11:12 +07:00
|
|
|
assert st.state == 'success'
|
2024-07-31 14:40:53 +07:00
|
|
|
assert pr_id.state == 'merged'
|
2020-02-07 22:11:12 +07:00
|
|
|
|
2024-07-31 14:40:53 +07:00
|
|
|
@pytest.mark.defaultstatuses
|
2020-02-07 22:11:12 +07:00
|
|
|
def test_updated(self, env, repo, config):
|
2020-07-10 15:21:43 +07:00
|
|
|
env['runbot_merge.repository'].search([('name', '=', repo.name)]).status_ids = False
|
2020-02-07 22:11:12 +07:00
|
|
|
with repo:
|
2024-07-31 14:40:53 +07:00
|
|
|
[m, c] = repo.make_commits(
|
|
|
|
None,
|
|
|
|
Commit('initial', tree={'0': '0'}),
|
|
|
|
Commit('first', tree={'0': '1'}),
|
|
|
|
)
|
2020-02-07 22:11:12 +07:00
|
|
|
repo.make_ref('heads/master', m)
|
|
|
|
|
2024-07-31 14:40:53 +07:00
|
|
|
pr = repo.make_pr(title='title', body='body', target='master', head=c)
|
2020-02-07 22:11:12 +07:00
|
|
|
env.run_crons()
|
|
|
|
|
2024-07-31 14:40:53 +07:00
|
|
|
pr_id = to_pr(env, pr)
|
|
|
|
assert pr_id.state == 'validated'
|
2020-02-07 22:11:12 +07:00
|
|
|
|
|
|
|
# normal push
|
|
|
|
with repo:
|
2024-07-31 14:40:53 +07:00
|
|
|
repo.make_commits(c, repo.Commit('second', tree={'0': '2'}), ref=pr.ref)
|
2020-02-07 22:11:12 +07:00
|
|
|
env.run_crons()
|
2024-07-31 14:40:53 +07:00
|
|
|
assert pr_id.state == 'validated'
|
2020-02-07 22:11:12 +07:00
|
|
|
with repo:
|
2024-07-31 14:40:53 +07:00
|
|
|
pr.post_comment('hansen r+', config['role_reviewer']['token'])
|
|
|
|
assert pr_id.state == 'ready'
|
2020-02-07 22:11:12 +07:00
|
|
|
|
|
|
|
# force push
|
|
|
|
with repo:
|
2024-07-31 14:40:53 +07:00
|
|
|
repo.make_commits(m, repo.Commit('xxx', tree={'0': 'm'}), ref=pr.ref)
|
2020-02-07 22:11:12 +07:00
|
|
|
env.run_crons()
|
2024-07-31 14:40:53 +07:00
|
|
|
assert pr_id.state == 'validated'
|
2020-02-07 22:11:12 +07:00
|
|
|
with repo:
|
2024-07-31 14:40:53 +07:00
|
|
|
pr.post_comment('hansen r+', config['role_reviewer']['token'])
|
|
|
|
assert pr_id.state == 'ready'
|
2019-10-03 21:04:30 +07:00
|
|
|
|
2018-03-14 16:37:46 +07:00
|
|
|
class TestRetry:
|
|
|
|
@pytest.mark.xfail(reason="This may not be a good idea as it could lead to tons of rebuild spam")
|
2019-10-10 14:22:12 +07:00
|
|
|
def test_auto_retry_push(self, env, repo, config):
|
2019-03-05 15:03:26 +07:00
|
|
|
prx = _simple_init(repo)
|
2018-03-14 16:37:46 +07:00
|
|
|
repo.post_status(prx.head, 'success', 'ci/runbot')
|
|
|
|
repo.post_status(prx.head, 'success', 'legal/cla')
|
2019-10-10 14:22:12 +07:00
|
|
|
prx.post_comment('hansen r+', config['role_reviewer']['token'])
|
|
|
|
env.run_crons()
|
2024-12-02 20:17:28 +07:00
|
|
|
assert to_pr(env, prx).staging_id
|
2018-03-14 16:37:46 +07:00
|
|
|
|
|
|
|
staging_head = repo.commit('heads/staging.master')
|
2024-06-03 17:59:24 +07:00
|
|
|
repo.post_status('staging.master', 'success', 'legal/cla')
|
|
|
|
repo.post_status('staging.master', 'failure', 'ci/runbot')
|
2019-10-10 14:22:12 +07:00
|
|
|
env.run_crons()
|
2024-12-02 20:17:28 +07:00
|
|
|
pr = to_pr(env, prx)
|
2018-03-14 16:37:46 +07:00
|
|
|
assert pr.state == 'error'
|
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
repo.update_ref(prx.ref, repo.make_commit(prx.head, 'third', None, tree={'m': 'c3'}), force=True)
|
2018-03-14 16:37:46 +07:00
|
|
|
assert pr.state == 'approved'
|
|
|
|
env['runbot_merge.project']._check_progress()
|
|
|
|
assert pr.state == 'approved'
|
|
|
|
repo.post_status(prx.head, 'success', 'ci/runbot')
|
|
|
|
repo.post_status(prx.head, 'success', 'legal/cla')
|
2019-10-10 14:22:12 +07:00
|
|
|
env.run_crons()
|
2018-03-14 16:37:46 +07:00
|
|
|
assert pr.state == 'ready'
|
|
|
|
|
|
|
|
staging_head2 = repo.commit('heads/staging.master')
|
|
|
|
assert staging_head2 != staging_head
|
2024-06-03 17:59:24 +07:00
|
|
|
repo.post_status('staging.master', 'success', 'legal/cla')
|
|
|
|
repo.post_status('staging.master', 'success', 'ci/runbot')
|
2019-10-10 14:22:12 +07:00
|
|
|
env.run_crons()
|
2018-03-14 16:37:46 +07:00
|
|
|
assert pr.state == 'merged'
|
|
|
|
|
2018-03-26 22:29:49 +07:00
|
|
|
@pytest.mark.parametrize('retrier', ['user', 'other', 'reviewer'])
|
2019-10-10 14:22:12 +07:00
|
|
|
def test_retry_comment(self, env, repo, retrier, users, config):
|
2018-03-14 16:37:46 +07:00
|
|
|
""" An accepted but failed PR should be re-tried when the author or a
|
|
|
|
reviewer asks for it
|
|
|
|
"""
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
2024-07-09 19:06:19 +07:00
|
|
|
pr = _simple_init(repo)
|
|
|
|
repo.post_status(pr.head, 'success', 'ci/runbot')
|
|
|
|
repo.post_status(pr.head, 'success', 'legal/cla')
|
|
|
|
pr.post_comment(f'hansen r+ delegate={users["other"]} rebase-merge',
|
|
|
|
config["role_reviewer"]['token'])
|
2019-10-10 14:22:12 +07:00
|
|
|
env.run_crons()
|
2024-07-09 19:06:19 +07:00
|
|
|
pr_id = to_pr(env, pr)
|
|
|
|
assert pr_id.staging_id
|
2018-03-14 16:37:46 +07:00
|
|
|
|
|
|
|
staging_head = repo.commit('heads/staging.master')
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
2024-06-03 17:59:24 +07:00
|
|
|
repo.post_status('staging.master', 'success', 'legal/cla')
|
|
|
|
repo.post_status('staging.master', 'failure', 'ci/runbot')
|
2019-10-10 14:22:12 +07:00
|
|
|
env.run_crons()
|
2024-07-09 19:06:19 +07:00
|
|
|
assert pr_id.state == 'error'
|
2018-03-14 16:37:46 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
2024-07-09 19:06:19 +07:00
|
|
|
pr.post_comment('hansen r+ rebase-ff', config["role_reviewer"]['token'])
|
|
|
|
env.run_crons()
|
|
|
|
assert pr_id.state == 'error'
|
|
|
|
assert pr.comments == [
|
|
|
|
(users['reviewer'], f'hansen r+ delegate={users["other"]} rebase-merge'),
|
|
|
|
seen(env, pr, users),
|
|
|
|
(users['user'], 'Merge method set to rebase and merge, using the PR as merge commit message.'),
|
|
|
|
(users['user'], '@{user} @{reviewer} staging failed: ci/runbot'.format_map(users)),
|
|
|
|
(users['reviewer'], 'hansen r+ rebase-ff'),
|
|
|
|
(users['user'], "This PR is already reviewed, it's in error, you might want to `retry` it instead "
|
|
|
|
"(if you have already confirmed the error is not legitimate)."),
|
|
|
|
(users['user'], 'Merge method set to rebase and fast-forward.'),
|
|
|
|
]
|
|
|
|
assert pr_id.merge_method == 'rebase-ff'
|
|
|
|
|
|
|
|
with repo:
|
|
|
|
pr.post_comment('hansen retry', config['role_' + retrier]['token'])
|
|
|
|
assert pr_id.state == 'ready'
|
2024-08-01 15:15:32 +07:00
|
|
|
env.run_crons(None)
|
2018-03-14 16:37:46 +07:00
|
|
|
|
|
|
|
staging_head2 = repo.commit('heads/staging.master')
|
|
|
|
assert staging_head2 != staging_head
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
2024-06-03 17:59:24 +07:00
|
|
|
repo.post_status('staging.master', 'success', 'legal/cla')
|
|
|
|
repo.post_status('staging.master', 'success', 'ci/runbot')
|
2019-10-10 14:22:12 +07:00
|
|
|
env.run_crons()
|
2024-07-09 19:06:19 +07:00
|
|
|
assert pr_id.state == 'merged'
|
2018-03-14 16:37:46 +07:00
|
|
|
|
2021-08-30 19:40:38 +07:00
|
|
|
def test_retry_again_message(self, env, repo, users, config, page):
|
|
|
|
""" For a retried PR, the error message on the PR's page should be the
|
|
|
|
later staging
|
|
|
|
"""
|
|
|
|
with repo:
|
|
|
|
pr = _simple_init(repo)
|
|
|
|
repo.post_status(pr.head, 'success', 'ci/runbot')
|
|
|
|
repo.post_status(pr.head, 'success', 'legal/cla')
|
|
|
|
pr.post_comment('hansen r+ delegate=%s rebase-merge' % users['other'],
|
|
|
|
config["role_reviewer"]['token'])
|
|
|
|
env.run_crons()
|
|
|
|
pr_id = to_pr(env, pr)
|
|
|
|
assert pr_id.staging_id
|
|
|
|
|
|
|
|
with repo:
|
|
|
|
repo.post_status('staging.master', 'success', 'legal/cla')
|
|
|
|
repo.post_status('staging.master', 'failure', 'ci/runbot',
|
|
|
|
target_url='https://example.com/whocares')
|
|
|
|
env.run_crons()
|
|
|
|
assert pr_id.state == 'error'
|
|
|
|
|
|
|
|
with repo:
|
|
|
|
pr.post_comment('hansen retry', config['role_reviewer']['token'])
|
2024-08-01 15:15:32 +07:00
|
|
|
env.run_crons(None)
|
2021-08-30 19:40:38 +07:00
|
|
|
|
|
|
|
with repo:
|
|
|
|
repo.post_status('staging.master', 'success', 'legal/cla')
|
|
|
|
repo.post_status('staging.master', 'failure', 'ci/runbot',
|
|
|
|
target_url='https://example.com/ohno')
|
|
|
|
env.run_crons()
|
|
|
|
assert pr_id.state == 'error'
|
|
|
|
|
|
|
|
dangerbox = pr_page(page, pr).cssselect('.alert-danger span')
|
|
|
|
assert dangerbox
|
|
|
|
assert dangerbox[0].text == 'ci/runbot (view more at https://example.com/ohno)'
|
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
def test_retry_ignored(self, env, repo, users, config):
|
2018-10-16 17:40:45 +07:00
|
|
|
""" Check feedback in case of ignored retry command on a non-error PR.
|
|
|
|
"""
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
prx = _simple_init(repo)
|
2024-06-13 18:30:07 +07:00
|
|
|
prx.post_comment('hansen r+ rebase-ff', config['role_reviewer']['token'])
|
2019-10-10 14:22:12 +07:00
|
|
|
prx.post_comment('hansen retry', config['role_reviewer']['token'])
|
|
|
|
env.run_crons()
|
2018-10-16 17:40:45 +07:00
|
|
|
|
|
|
|
assert prx.comments == [
|
2024-06-13 18:30:07 +07:00
|
|
|
(users['reviewer'], 'hansen r+ rebase-ff'),
|
2018-10-16 17:40:45 +07:00
|
|
|
(users['reviewer'], 'hansen retry'),
|
2020-11-17 21:21:21 +07:00
|
|
|
seen(env, prx, users),
|
2024-06-13 18:30:07 +07:00
|
|
|
(users['user'], "Merge method set to rebase and fast-forward."),
|
2023-10-16 15:46:29 +07:00
|
|
|
(users['user'], "@{reviewer} retry makes no sense when the PR is not in error.".format_map(users)),
|
2018-10-16 17:40:45 +07:00
|
|
|
]
|
|
|
|
|
2024-07-31 14:40:53 +07:00
|
|
|
@pytest.mark.defaultstatuses
|
2018-03-26 22:29:49 +07:00
|
|
|
@pytest.mark.parametrize('disabler', ['user', 'other', 'reviewer'])
|
2019-10-10 14:22:12 +07:00
|
|
|
def test_retry_disable(self, env, repo, disabler, users, config):
|
|
|
|
with repo:
|
|
|
|
prx = _simple_init(repo)
|
2024-07-31 14:40:53 +07:00
|
|
|
repo.post_status(prx.head, 'success')
|
2019-10-10 14:22:12 +07:00
|
|
|
prx.post_comment('hansen r+ delegate=%s rebase-merge' % users['other'],
|
|
|
|
config["role_reviewer"]['token'])
|
|
|
|
env.run_crons()
|
2024-07-31 14:40:53 +07:00
|
|
|
pr_id = to_pr(env, prx)
|
|
|
|
staging_id = pr_id.staging_id
|
|
|
|
assert staging_id
|
2018-03-14 16:37:46 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
2024-07-31 14:40:53 +07:00
|
|
|
repo.post_status('staging.master', 'failure')
|
2019-10-10 14:22:12 +07:00
|
|
|
env.run_crons()
|
2024-07-31 14:40:53 +07:00
|
|
|
assert staging_id.state == 'failure'
|
|
|
|
assert not staging_id.active
|
|
|
|
assert pr_id.state == 'error'
|
2018-03-14 16:37:46 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
prx.post_comment('hansen r-', config['role_' + disabler]['token'])
|
2024-07-31 14:40:53 +07:00
|
|
|
assert pr_id.state == 'validated'
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
repo.make_commit(prx.ref, 'third', None, tree={'m': 'c3'})
|
|
|
|
# just in case, apparently in some case the first post_status uses the old head...
|
|
|
|
with repo:
|
2024-07-31 14:40:53 +07:00
|
|
|
repo.post_status(prx.head, 'success')
|
2019-10-10 14:22:12 +07:00
|
|
|
env.run_crons()
|
2024-07-31 14:40:53 +07:00
|
|
|
assert pr_id.state == 'validated'
|
2018-03-14 16:37:46 +07:00
|
|
|
|
2018-08-28 20:42:28 +07:00
|
|
|
class TestMergeMethod:
|
2018-03-14 16:37:46 +07:00
|
|
|
"""
|
2018-06-01 16:59:18 +07:00
|
|
|
if event['pull_request']['commits'] == 1, "squash" (/rebase); otherwise
|
|
|
|
regular merge
|
2018-03-14 16:37:46 +07:00
|
|
|
"""
|
2019-10-10 14:22:12 +07:00
|
|
|
def test_pr_single_commit(self, repo, env, config):
|
2018-11-26 16:28:13 +07:00
|
|
|
""" If single commit, default to rebase & FF
|
|
|
|
"""
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
m = repo.make_commit(None, 'initial', None, tree={'m': 'm'})
|
|
|
|
m2 = repo.make_commit(m, 'second', None, tree={'m': 'm', 'm2': 'm2'})
|
|
|
|
repo.make_ref('heads/master', m2)
|
|
|
|
|
|
|
|
c1 = repo.make_commit(m, 'first', None, tree={'m': 'c1'})
|
|
|
|
prx = repo.make_pr(title='title', body='body', target='master', head=c1)
|
|
|
|
repo.post_status(prx.head, 'success', 'legal/cla')
|
|
|
|
repo.post_status(prx.head, 'success', 'ci/runbot')
|
|
|
|
prx.post_comment('hansen r+', config['role_reviewer']['token'])
|
2024-12-02 20:17:28 +07:00
|
|
|
assert to_pr(env, prx).squash
|
2018-03-14 16:37:46 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
env.run_crons()
|
2024-12-02 20:17:28 +07:00
|
|
|
assert to_pr(env, prx).staging_id
|
2018-03-14 16:37:46 +07:00
|
|
|
|
|
|
|
staging = repo.commit('heads/staging.master')
|
2018-06-05 15:10:32 +07:00
|
|
|
assert not repo.is_ancestor(prx.head, of=staging.id),\
|
2018-03-14 16:37:46 +07:00
|
|
|
"the pr head should not be an ancestor of the staging branch in a squash merge"
|
2018-06-05 15:10:32 +07:00
|
|
|
assert repo.read_tree(staging) == {
|
2019-08-23 21:16:30 +07:00
|
|
|
'm': 'c1', 'm2': 'm2',
|
2018-03-14 16:37:46 +07:00
|
|
|
}, "the tree should still be correctly merged"
|
2021-08-09 18:21:24 +07:00
|
|
|
assert staging.parents == [m2],\
|
2018-09-10 21:00:26 +07:00
|
|
|
"dummy commit aside, the previous master's tip should be the sole parent of the staging commit"
|
2018-03-14 16:37:46 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
2024-06-03 17:59:24 +07:00
|
|
|
repo.post_status('staging.master', 'success', 'legal/cla')
|
|
|
|
repo.post_status('staging.master', 'success', 'ci/runbot')
|
2019-10-10 14:22:12 +07:00
|
|
|
env.run_crons()
|
2024-12-02 20:17:28 +07:00
|
|
|
pr = to_pr(env, prx)
|
2019-07-31 14:19:39 +07:00
|
|
|
assert pr.state == 'merged'
|
2018-03-14 16:37:46 +07:00
|
|
|
assert prx.state == 'closed'
|
2019-07-31 14:19:39 +07:00
|
|
|
assert json.loads(pr.commits_map) == {
|
2021-08-09 18:21:24 +07:00
|
|
|
c1: staging.id,
|
|
|
|
'': staging.id,
|
2019-07-31 14:19:39 +07:00
|
|
|
}, "for a squash, the one PR commit should be mapped to the one rebased commit"
|
2018-03-14 16:37:46 +07:00
|
|
|
|
2021-10-05 20:05:17 +07:00
|
|
|
def test_delegate_method(self, repo, env, users, config):
|
|
|
|
"""Delegates should be able to configure the merge method.
|
|
|
|
"""
|
|
|
|
with repo:
|
|
|
|
m, _ = repo.make_commits(
|
|
|
|
None,
|
|
|
|
Commit('initial', tree={'m': 'm'}),
|
|
|
|
Commit('second', tree={'m2': 'm2'}),
|
|
|
|
ref="heads/master"
|
|
|
|
)
|
|
|
|
|
|
|
|
[c1] = repo.make_commits(m, Commit('first', tree={'m': 'c1'}))
|
|
|
|
pr = repo.make_pr(target='master', head=c1)
|
|
|
|
repo.post_status(pr.head, 'success', 'legal/cla')
|
|
|
|
repo.post_status(pr.head, 'success', 'ci/runbot')
|
|
|
|
pr.post_comment('hansen delegate+', config['role_reviewer']['token'])
|
|
|
|
pr.post_comment('hansen merge', config['role_user']['token'])
|
|
|
|
env.run_crons()
|
|
|
|
|
|
|
|
assert pr.user == users['user']
|
|
|
|
assert to_pr(env, pr).merge_method == 'merge'
|
|
|
|
|
2018-11-26 16:28:13 +07:00
|
|
|
def test_pr_update_to_many_commits(self, repo, env):
|
2018-06-01 17:33:31 +07:00
|
|
|
"""
|
|
|
|
If a PR starts with 1 commit and a second commit is added, the PR
|
|
|
|
should be unflagged as squash
|
|
|
|
"""
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
m = repo.make_commit(None, 'initial', None, tree={'m': 'm'})
|
|
|
|
m2 = repo.make_commit(m, 'second', None, tree={'m': 'm', 'm2': 'm2'})
|
|
|
|
repo.make_ref('heads/master', m2)
|
2018-06-01 17:33:31 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
c1 = repo.make_commit(m, 'first', None, tree={'m': 'c1'})
|
|
|
|
prx = repo.make_pr(title='title', body='body', target='master', head=c1)
|
2024-12-02 20:17:28 +07:00
|
|
|
pr = to_pr(env, prx)
|
2018-06-01 17:33:31 +07:00
|
|
|
assert pr.squash, "a PR with a single commit should be squashed"
|
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
repo.make_commit(prx.ref, 'second2', None, tree={'m': 'c2'})
|
2018-06-01 17:33:31 +07:00
|
|
|
assert not pr.squash, "a PR with a single commit should not be squashed"
|
|
|
|
|
2018-11-26 16:28:13 +07:00
|
|
|
def test_pr_reset_to_single_commit(self, repo, env):
|
2018-06-01 17:33:31 +07:00
|
|
|
"""
|
|
|
|
If a PR starts at >1 commits and is reset back to 1, the PR should be
|
|
|
|
re-flagged as squash
|
|
|
|
"""
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
m = repo.make_commit(None, 'initial', None, tree={'m': 'm'})
|
|
|
|
m2 = repo.make_commit(m, 'second', None, tree={'m': 'm', 'm2': 'm2'})
|
|
|
|
repo.make_ref('heads/master', m2)
|
|
|
|
|
|
|
|
c1 = repo.make_commit(m, 'first', None, tree={'m': 'c1'})
|
|
|
|
c2 = repo.make_commit(c1, 'second2', None, tree={'m': 'c2'})
|
|
|
|
prx = repo.make_pr(title='title', body='body', target='master', head=c2)
|
2024-12-02 20:17:28 +07:00
|
|
|
pr = to_pr(env, prx)
|
2020-05-28 18:27:34 +07:00
|
|
|
pr.merge_method = 'rebase-merge'
|
2018-06-01 17:33:31 +07:00
|
|
|
assert not pr.squash, "a PR with a single commit should not be squashed"
|
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
repo.update_ref(
|
|
|
|
prx.ref,
|
|
|
|
repo.make_commit(m, 'fixup', None, tree={'m': 'c2'}),
|
|
|
|
force=True
|
|
|
|
)
|
2018-06-01 17:33:31 +07:00
|
|
|
assert pr.squash, "a PR with a single commit should be squashed"
|
2020-05-28 18:27:34 +07:00
|
|
|
assert not pr.merge_method, \
|
|
|
|
"resetting a PR to a single commit should remove the merge method"
|
2018-06-01 17:33:31 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
def test_pr_no_method(self, repo, env, users, config):
|
2018-11-26 16:28:13 +07:00
|
|
|
""" a multi-repo PR should not be staged by default, should also get
|
|
|
|
feedback indicating a merge method is necessary
|
|
|
|
"""
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
2021-10-05 20:05:17 +07:00
|
|
|
_, m1, _ = repo.make_commits(
|
|
|
|
None,
|
|
|
|
Commit('M0', tree={'m': '0'}),
|
|
|
|
Commit('M1', tree={'m': '1'}),
|
|
|
|
Commit('M2', tree={'m': '2'}),
|
|
|
|
ref='heads/master'
|
|
|
|
)
|
2019-10-10 14:22:12 +07:00
|
|
|
|
2021-10-05 20:05:17 +07:00
|
|
|
_, b1 = repo.make_commits(
|
|
|
|
m1,
|
|
|
|
Commit('B0', tree={'b': '0'}),
|
|
|
|
Commit('B1', tree={'b': '1'}),
|
|
|
|
)
|
2019-10-10 14:22:12 +07:00
|
|
|
prx = repo.make_pr(title='title', body='body', target='master', head=b1)
|
|
|
|
repo.post_status(prx.head, 'success', 'legal/cla')
|
|
|
|
repo.post_status(prx.head, 'success', 'ci/runbot')
|
|
|
|
prx.post_comment('hansen r+', config['role_reviewer']['token'])
|
|
|
|
env.run_crons()
|
2018-11-26 16:28:13 +07:00
|
|
|
|
2021-10-05 20:05:17 +07:00
|
|
|
assert not to_pr(env, prx).staging_id
|
2018-11-26 16:28:13 +07:00
|
|
|
|
|
|
|
assert prx.comments == [
|
|
|
|
(users['reviewer'], 'hansen r+'),
|
2020-11-17 21:21:21 +07:00
|
|
|
seen(env, prx, users),
|
2022-06-23 19:25:07 +07:00
|
|
|
(users['user'], """@{user} @{reviewer} because this PR has multiple \
|
|
|
|
commits, I need to know how to merge it:
|
2018-11-26 16:28:13 +07:00
|
|
|
|
|
|
|
* `merge` to merge directly, using the PR as merge commit message
|
|
|
|
* `rebase-merge` to rebase and merge, using the PR as merge commit message
|
|
|
|
* `rebase-ff` to rebase and fast-forward
|
2022-06-23 19:25:07 +07:00
|
|
|
""".format_map(users)),
|
2018-11-26 16:28:13 +07:00
|
|
|
]
|
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
def test_pr_method_no_review(self, repo, env, users, config):
|
2021-10-05 20:05:17 +07:00
|
|
|
""" Configuring the method should be independent from the review
|
2018-11-26 16:28:13 +07:00
|
|
|
"""
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
m0 = repo.make_commit(None, 'M0', None, tree={'m': '0'})
|
|
|
|
m1 = repo.make_commit(m0, 'M1', None, tree={'m': '1'})
|
|
|
|
m2 = repo.make_commit(m1, 'M2', None, tree={'m': '2'})
|
|
|
|
repo.make_ref('heads/master', m2)
|
|
|
|
|
|
|
|
b0 = repo.make_commit(m1, 'B0', None, tree={'m': '1', 'b': '0'})
|
|
|
|
b1 = repo.make_commit(b0, 'B1', None, tree={'m': '1', 'b': '1'})
|
|
|
|
prx = repo.make_pr(title='title', body='body', target='master', head=b1)
|
2024-12-02 20:17:28 +07:00
|
|
|
pr = to_pr(env, prx)
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
repo.post_status(prx.head, 'success', 'legal/cla')
|
|
|
|
repo.post_status(prx.head, 'success', 'ci/runbot')
|
2018-11-26 16:28:13 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
prx.post_comment('hansen rebase-merge', config['role_reviewer']['token'])
|
2018-11-26 16:28:13 +07:00
|
|
|
assert pr.merge_method == 'rebase-merge'
|
2019-10-10 14:22:12 +07:00
|
|
|
env.run_crons()
|
2018-11-26 16:28:13 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
prx.post_comment('hansen merge', config['role_reviewer']['token'])
|
2018-11-26 16:28:13 +07:00
|
|
|
assert pr.merge_method == 'merge'
|
2019-10-10 14:22:12 +07:00
|
|
|
env.run_crons()
|
2018-11-26 16:28:13 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
prx.post_comment('hansen rebase-ff', config['role_reviewer']['token'])
|
2018-11-26 16:28:13 +07:00
|
|
|
assert pr.merge_method == 'rebase-ff'
|
2019-10-10 14:22:12 +07:00
|
|
|
env.run_crons()
|
2018-12-13 19:28:20 +07:00
|
|
|
|
|
|
|
assert prx.comments == [
|
|
|
|
(users['reviewer'], 'hansen rebase-merge'),
|
2020-11-17 21:21:21 +07:00
|
|
|
seen(env, prx, users),
|
2022-06-23 19:25:07 +07:00
|
|
|
(users['user'], "Merge method set to rebase and merge, using the PR as merge commit message."),
|
2018-12-13 19:28:20 +07:00
|
|
|
(users['reviewer'], 'hansen merge'),
|
2022-06-23 19:25:07 +07:00
|
|
|
(users['user'], "Merge method set to merge directly, using the PR as merge commit message."),
|
2018-12-13 19:28:20 +07:00
|
|
|
(users['reviewer'], 'hansen rebase-ff'),
|
2022-06-23 19:25:07 +07:00
|
|
|
(users['user'], "Merge method set to rebase and fast-forward."),
|
2018-12-13 19:28:20 +07:00
|
|
|
]
|
2018-11-26 16:28:13 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
def test_pr_rebase_merge(self, repo, env, users, config):
|
2018-11-26 16:28:13 +07:00
|
|
|
""" test result on rebase-merge
|
2018-08-28 20:42:28 +07:00
|
|
|
|
|
|
|
left: PR
|
|
|
|
right: post-merge result
|
|
|
|
|
|
|
|
+------+ +------+
|
|
|
|
| M0 | | M0 |
|
|
|
|
+--^---+ +--^---+
|
|
|
|
| |
|
|
|
|
| |
|
|
|
|
+--+---+ +--+---+
|
|
|
|
+----> M1 <--+ | M1 <--+
|
|
|
|
| +------+ | +------+ |
|
|
|
|
| | |
|
|
|
|
| | |
|
|
|
|
+--+---+ +---+---+ +------+ +---+---+
|
|
|
|
| B0 | | M2 | | B0 +------> M2 |
|
|
|
|
+--^---+ +-------+ +--^---+ +---^---+
|
|
|
|
| | |
|
|
|
|
+--+---+ +--+---+ |
|
|
|
|
PR | B1 | | B1 | |
|
|
|
|
+------+ +--^---+ |
|
|
|
|
| +---+---+
|
|
|
|
+----------+ merge |
|
|
|
|
+-------+
|
|
|
|
"""
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
m0 = repo.make_commit(None, 'M0', None, tree={'m': '0'})
|
|
|
|
m1 = repo.make_commit(m0, 'M1', None, tree={'m': '1'})
|
|
|
|
m2 = repo.make_commit(m1, 'M2', None, tree={'m': '2'})
|
|
|
|
repo.make_ref('heads/master', m2)
|
|
|
|
|
|
|
|
# test commit ordering issue while at it: github sorts commits on
|
|
|
|
# author.date instead of doing so topologically which is absolutely
|
|
|
|
# not what we want
|
|
|
|
committer = {'name': 'a', 'email': 'a', 'date': '2018-10-08T11:48:43Z'}
|
|
|
|
author0 = {'name': 'a', 'email': 'a', 'date': '2018-10-01T14:58:38Z'}
|
|
|
|
author1 = {'name': 'a', 'email': 'a', 'date': '2015-10-01T14:58:38Z'}
|
|
|
|
b0 = repo.make_commit(m1, 'B0', author=author0, committer=committer, tree={'m': '1', 'b': '0'})
|
|
|
|
b1 = repo.make_commit(b0, 'B1', author=author1, committer=committer, tree={'m': '1', 'b': '1'})
|
|
|
|
prx = repo.make_pr(title='title', body='body', target='master', head=b1)
|
|
|
|
repo.post_status(prx.head, 'success', 'legal/cla')
|
|
|
|
repo.post_status(prx.head, 'success', 'ci/runbot')
|
|
|
|
prx.post_comment('hansen r+ rebase-merge', config['role_reviewer']['token'])
|
|
|
|
env.run_crons()
|
2018-08-28 20:42:28 +07:00
|
|
|
|
2021-08-09 12:55:38 +07:00
|
|
|
pr_id = to_pr(env, prx)
|
2018-08-28 20:42:28 +07:00
|
|
|
# create a dag (msg:str, parents:set) from the log
|
|
|
|
staging = log_to_node(repo.log('heads/staging.master'))
|
2018-08-29 21:51:53 +07:00
|
|
|
# then compare to the dag version of the right graph
|
|
|
|
nm2 = node('M2', node('M1', node('M0')))
|
2021-08-09 12:55:38 +07:00
|
|
|
nb1 = node(part_of('B1', pr_id), node(part_of('B0', pr_id), nm2))
|
2018-11-22 00:43:05 +07:00
|
|
|
reviewer = get_partner(env, users["reviewer"]).formatted_email
|
2018-09-10 21:00:26 +07:00
|
|
|
merge_head = (
|
2021-08-09 12:55:38 +07:00
|
|
|
f'title\n\nbody\n\ncloses {pr_id.display_name}\n\nSigned-off-by: {reviewer}',
|
2018-08-28 20:42:28 +07:00
|
|
|
frozenset([nm2, nb1])
|
|
|
|
)
|
2021-08-09 18:21:24 +07:00
|
|
|
assert staging == merge_head
|
2022-06-09 13:55:34 +07:00
|
|
|
st = pr_id.staging_id
|
|
|
|
assert st
|
|
|
|
|
|
|
|
with repo: prx.title = 'title 2'
|
|
|
|
assert not pr_id.staging_id, "updating the message of a merge-staged PR should unstage rien"
|
|
|
|
assert st.reason == f'{pr_id.display_name} merge message updated'
|
|
|
|
# since we updated the description, the merge_head value is impacted,
|
|
|
|
# and it's checked again later on
|
|
|
|
merge_head = (
|
|
|
|
merge_head[0].replace('title', 'title 2'),
|
|
|
|
merge_head[1],
|
|
|
|
)
|
|
|
|
env.run_crons()
|
|
|
|
assert pr_id.staging_id, "PR should immediately be re-stageable"
|
2018-08-28 20:42:28 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
repo.post_status('heads/staging.master', 'success', 'legal/cla')
|
|
|
|
repo.post_status('heads/staging.master', 'success', 'ci/runbot')
|
|
|
|
env.run_crons()
|
2018-09-10 21:00:26 +07:00
|
|
|
|
2024-12-02 20:17:28 +07:00
|
|
|
pr = to_pr(env, prx)
|
2019-07-31 14:19:39 +07:00
|
|
|
assert pr.state == 'merged'
|
2018-09-10 21:00:26 +07:00
|
|
|
|
|
|
|
# check that the dummy commit is not in the final master
|
|
|
|
master = log_to_node(repo.log('heads/master'))
|
|
|
|
assert master == merge_head
|
2019-07-31 14:19:39 +07:00
|
|
|
head = repo.commit('heads/master')
|
|
|
|
final_tree = repo.read_tree(head)
|
2019-08-23 21:16:30 +07:00
|
|
|
assert final_tree == {'m': '2', 'b': '1'}, "sanity check of final tree"
|
2019-07-31 14:19:39 +07:00
|
|
|
r1 = repo.commit(head.parents[1])
|
|
|
|
r0 = repo.commit(r1.parents[0])
|
|
|
|
assert json.loads(pr.commits_map) == {
|
|
|
|
b0: r0.id,
|
|
|
|
b1: r1.id,
|
|
|
|
'': head.id,
|
|
|
|
}
|
|
|
|
assert r0.parents == [m2]
|
2018-08-28 20:42:28 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
def test_pr_rebase_ff(self, repo, env, users, config):
|
2018-11-26 16:28:13 +07:00
|
|
|
""" test result on rebase-merge
|
|
|
|
|
|
|
|
left: PR
|
|
|
|
right: post-merge result
|
|
|
|
|
|
|
|
+------+ +------+
|
|
|
|
| M0 | | M0 |
|
|
|
|
+--^---+ +--^---+
|
|
|
|
| |
|
|
|
|
| |
|
|
|
|
+--+---+ +--+---+
|
|
|
|
+----> M1 <--+ | M1 <--+
|
|
|
|
| +------+ | +------+ |
|
|
|
|
| | |
|
|
|
|
| | |
|
|
|
|
+--+---+ +---+---+ +------+ +---+---+
|
|
|
|
| B0 | | M2 | | B0 +------> M2 |
|
|
|
|
+--^---+ +-------+ +--^---+ +---^---+
|
|
|
|
| |
|
|
|
|
+--+---+ +--+---+
|
|
|
|
PR | B1 | | B1 |
|
|
|
|
+------+ +--^---+
|
|
|
|
"""
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
2021-08-09 18:21:24 +07:00
|
|
|
_, m1, m2 = repo.make_commits(
|
|
|
|
None,
|
|
|
|
Commit('M0', tree={'m': '0'}),
|
|
|
|
Commit('M1', tree={'m': '1'}),
|
|
|
|
Commit('M2', tree={'m': '2'}),
|
|
|
|
ref='heads/master'
|
|
|
|
)
|
|
|
|
|
|
|
|
b0, b1 = repo.make_commits(
|
|
|
|
m1,
|
|
|
|
Commit('B0', tree={'b': '0'}, author={'name': 'Maarten Tromp', 'email': 'm.tromp@example.nl', 'date': '1651-03-30T12:00:00Z'}),
|
|
|
|
Commit('B1', tree={'b': '1'}, author={'name': 'Rein Huydecoper', 'email': 'r.huydecoper@example.nl', 'date': '1986-04-17T12:00:00Z'}),
|
|
|
|
)
|
2019-10-10 14:22:12 +07:00
|
|
|
|
|
|
|
prx = repo.make_pr(title='title', body='body', target='master', head=b1)
|
|
|
|
repo.post_status(prx.head, 'success', 'legal/cla')
|
|
|
|
repo.post_status(prx.head, 'success', 'ci/runbot')
|
|
|
|
prx.post_comment('hansen r+ rebase-ff', config['role_reviewer']['token'])
|
|
|
|
env.run_crons()
|
2018-11-26 16:28:13 +07:00
|
|
|
|
2021-08-09 12:55:38 +07:00
|
|
|
pr_id = to_pr(env, prx)
|
2018-11-26 16:28:13 +07:00
|
|
|
# create a dag (msg:str, parents:set) from the log
|
|
|
|
staging = log_to_node(repo.log('heads/staging.master'))
|
|
|
|
# then compare to the dag version of the right graph
|
|
|
|
nm2 = node('M2', node('M1', node('M0')))
|
2018-11-22 00:43:05 +07:00
|
|
|
reviewer = get_partner(env, users["reviewer"]).formatted_email
|
2021-08-09 12:55:38 +07:00
|
|
|
nb1 = node(f'B1\n\ncloses {pr_id.display_name}\n\nSigned-off-by: {reviewer}',
|
|
|
|
node(part_of('B0', pr_id), nm2))
|
2021-08-09 18:21:24 +07:00
|
|
|
assert staging == nb1
|
2018-11-26 16:28:13 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
repo.post_status('heads/staging.master', 'success', 'legal/cla')
|
|
|
|
repo.post_status('heads/staging.master', 'success', 'ci/runbot')
|
|
|
|
env.run_crons()
|
2018-11-26 16:28:13 +07:00
|
|
|
|
2024-12-02 20:17:28 +07:00
|
|
|
pr = to_pr(env, prx)
|
2019-07-31 14:19:39 +07:00
|
|
|
assert pr.state == 'merged'
|
2018-11-26 16:28:13 +07:00
|
|
|
|
|
|
|
# check that the dummy commit is not in the final master
|
|
|
|
master = log_to_node(repo.log('heads/master'))
|
|
|
|
assert master == nb1
|
2019-07-31 14:19:39 +07:00
|
|
|
head = repo.commit('heads/master')
|
|
|
|
final_tree = repo.read_tree(head)
|
2019-08-23 21:16:30 +07:00
|
|
|
assert final_tree == {'m': '2', 'b': '1'}, "sanity check of final tree"
|
2018-11-26 16:28:13 +07:00
|
|
|
|
2019-07-31 14:19:39 +07:00
|
|
|
m1 = head
|
|
|
|
m0 = repo.commit(m1.parents[0])
|
|
|
|
assert json.loads(pr.commits_map) == {
|
|
|
|
'': m1.id, # merge commit
|
|
|
|
b1: m1.id, # second PR's commit
|
|
|
|
b0: m0.id, # first PR's commit
|
|
|
|
}
|
|
|
|
assert m0.parents == [m2], "can't hurt to check the parent of our root commit"
|
2021-08-09 18:21:24 +07:00
|
|
|
assert m0.author['date'] != m0.committer['date'], "commit date should have been rewritten"
|
|
|
|
assert m1.author['date'] != m1.committer['date'], "commit date should have been rewritten"
|
|
|
|
|
|
|
|
utcday = datetime.datetime.utcnow().date()
|
|
|
|
def parse(dt):
|
|
|
|
return datetime.datetime.strptime(dt, "%Y-%m-%dT%H:%M:%SZ")
|
|
|
|
|
|
|
|
# FIXME: actual commit creation could run before the date rollover and
|
|
|
|
# local datetime.utcnow() after
|
|
|
|
assert parse(m0.committer['date']).date() == utcday
|
|
|
|
# FIXME: git date storage is unreliable and non-portable outside of an
|
|
|
|
# unsigned 31b epoch range so the m0 event may get flung in the
|
|
|
|
# future (compared to the literal datum), this test unexpectedly
|
|
|
|
# becoming true if run on the exact wrong day
|
|
|
|
assert parse(m0.author['date']).date() != utcday
|
|
|
|
assert parse(m1.committer['date']).date() == utcday
|
|
|
|
assert parse(m0.author['date']).date() != utcday
|
2019-07-31 14:19:39 +07:00
|
|
|
|
2018-08-28 20:42:28 +07:00
|
|
|
@pytest.mark.skip(reason="what do if the PR contains merge commits???")
|
|
|
|
def test_pr_contains_merges(self, repo, env):
|
2018-08-29 21:51:53 +07:00
|
|
|
pass
|
2018-08-28 20:42:28 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
def test_pr_force_merge_single_commit(self, repo, env, users, config):
|
2018-08-29 21:51:53 +07:00
|
|
|
""" should be possible to flag a PR as regular-merged, regardless of
|
|
|
|
its commits count
|
|
|
|
|
|
|
|
M M<--+
|
|
|
|
^ ^ |
|
|
|
|
| -> | C0
|
|
|
|
+ | ^
|
|
|
|
C0 + |
|
|
|
|
gib-+
|
2018-08-28 20:42:28 +07:00
|
|
|
"""
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
m = repo.make_commit(None, "M", None, tree={'a': 'a'})
|
|
|
|
repo.make_ref('heads/master', m)
|
2018-08-29 21:51:53 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
c0 = repo.make_commit(m, 'C0', None, tree={'a': 'b'})
|
|
|
|
prx = repo.make_pr(title="gibberish", body="blahblah", target='master', head=c0)
|
2024-08-01 15:15:32 +07:00
|
|
|
env.run_crons(None)
|
2018-08-29 21:51:53 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
repo.post_status(prx.head, 'success', 'legal/cla')
|
|
|
|
repo.post_status(prx.head, 'success', 'ci/runbot')
|
|
|
|
prx.post_comment('hansen r+ merge', config['role_reviewer']['token'])
|
|
|
|
env.run_crons()
|
2018-08-29 21:51:53 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
repo.post_status('heads/staging.master', 'success', 'ci/runbot')
|
|
|
|
repo.post_status('heads/staging.master', 'success', 'legal/cla')
|
|
|
|
env.run_crons()
|
2018-08-29 21:51:53 +07:00
|
|
|
|
|
|
|
master = repo.commit('heads/master')
|
|
|
|
assert master.parents == [m, prx.head], \
|
|
|
|
"master's parents should be the old master & the PR head"
|
|
|
|
|
|
|
|
m = node('M')
|
|
|
|
c0 = node('C0', m)
|
2018-11-22 00:43:05 +07:00
|
|
|
reviewer = get_partner(env, users["reviewer"]).formatted_email
|
|
|
|
expected = node('gibberish\n\nblahblah\n\ncloses {}#{}'
|
|
|
|
'\n\nSigned-off-by: {}'.format(repo.name, prx.number, reviewer), m, c0)
|
2018-08-29 21:51:53 +07:00
|
|
|
assert log_to_node(repo.log('heads/master')), expected
|
2024-12-02 20:17:28 +07:00
|
|
|
pr = to_pr(env, prx)
|
2019-07-31 14:19:39 +07:00
|
|
|
assert json.loads(pr.commits_map) == {
|
|
|
|
prx.head: prx.head,
|
|
|
|
'': master.id
|
|
|
|
}
|
2018-08-28 20:42:28 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
def test_unrebase_emptymessage(self, repo, env, users, config):
|
2018-09-19 19:40:36 +07:00
|
|
|
""" When merging between master branches (e.g. forward port), the PR
|
|
|
|
may have only a title
|
|
|
|
"""
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
m = repo.make_commit(None, "M", None, tree={'a': 'a'})
|
|
|
|
repo.make_ref('heads/master', m)
|
2018-09-19 19:40:36 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
c0 = repo.make_commit(m, 'C0', None, tree={'a': 'b'})
|
|
|
|
prx = repo.make_pr(title="gibberish", body=None, target='master', head=c0)
|
2024-08-01 15:15:32 +07:00
|
|
|
env.run_crons(None)
|
2018-09-19 19:40:36 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
repo.post_status(prx.head, 'success', 'legal/cla')
|
|
|
|
repo.post_status(prx.head, 'success', 'ci/runbot')
|
|
|
|
prx.post_comment('hansen r+ merge', config['role_reviewer']['token'])
|
|
|
|
env.run_crons()
|
2018-09-19 19:40:36 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
repo.post_status('heads/staging.master', 'success', 'ci/runbot')
|
|
|
|
repo.post_status('heads/staging.master', 'success', 'legal/cla')
|
|
|
|
env.run_crons()
|
2018-09-19 19:40:36 +07:00
|
|
|
|
|
|
|
master = repo.commit('heads/master')
|
|
|
|
assert master.parents == [m, prx.head], \
|
|
|
|
"master's parents should be the old master & the PR head"
|
|
|
|
|
|
|
|
m = node('M')
|
|
|
|
c0 = node('C0', m)
|
2018-11-22 00:43:05 +07:00
|
|
|
reviewer = get_partner(env, users["reviewer"]).formatted_email
|
|
|
|
expected = node('gibberish\n\ncloses {}#{}'
|
|
|
|
'\n\nSigned-off-by: {}'.format(repo.name, prx.number, reviewer), m, c0)
|
2018-09-19 19:40:36 +07:00
|
|
|
assert log_to_node(repo.log('heads/master')), expected
|
|
|
|
|
2021-01-12 18:24:34 +07:00
|
|
|
@pytest.mark.parametrize('separator', [
|
|
|
|
'***', '___', '\n---',
|
|
|
|
'*'*12, '\n----------------',
|
|
|
|
'- - -', ' ** ** **'
|
|
|
|
])
|
|
|
|
def test_pr_message_break(self, repo, env, users, config, separator):
|
|
|
|
""" If the PR message contains a "thematic break", only the part before
|
|
|
|
should be included in the merge commit's message.
|
|
|
|
"""
|
|
|
|
reviewer = get_partner(env, users["reviewer"]).formatted_email
|
|
|
|
with repo:
|
|
|
|
root = repo.make_commits(None, Commit("root", tree={'a': 'a'}), ref='heads/master')
|
|
|
|
|
2023-08-10 18:21:21 +07:00
|
|
|
repo.make_commits(root, Commit('C', tree={'a': 'b'}), ref='heads/change')
|
2021-01-12 18:24:34 +07:00
|
|
|
pr = repo.make_pr(title="title", body=f'first\n{separator}\nsecond',
|
2023-08-10 18:21:21 +07:00
|
|
|
target='master', head='change')
|
2021-01-12 18:24:34 +07:00
|
|
|
repo.post_status(pr.head, 'success', 'legal/cla')
|
|
|
|
repo.post_status(pr.head, 'success', 'ci/runbot')
|
|
|
|
pr.post_comment('hansen r+ merge', config['role_reviewer']['token'])
|
|
|
|
env.run_crons()
|
|
|
|
|
|
|
|
with repo:
|
|
|
|
repo.post_status('heads/staging.master', 'success', 'ci/runbot')
|
|
|
|
repo.post_status('heads/staging.master', 'success', 'legal/cla')
|
|
|
|
env.run_crons()
|
|
|
|
|
|
|
|
head = repo.commit('heads/master')
|
|
|
|
assert head.message == textwrap.dedent(f"""\
|
|
|
|
title
|
|
|
|
|
|
|
|
first
|
|
|
|
|
|
|
|
closes {repo.name}#{pr.number}
|
|
|
|
|
|
|
|
Signed-off-by: {reviewer}
|
|
|
|
""").strip(), "should not contain the content which follows the thematic break"
|
|
|
|
|
|
|
|
def test_pr_message_setex_title(self, repo, env, users, config):
|
|
|
|
""" should not break on a proper SETEX-style title """
|
|
|
|
reviewer = get_partner(env, users["reviewer"]).formatted_email
|
|
|
|
with repo:
|
|
|
|
root = repo.make_commits(None, Commit("root", tree={'a': 'a'}), ref='heads/master')
|
|
|
|
|
2023-08-10 18:21:21 +07:00
|
|
|
repo.make_commits(root, Commit('C', tree={'a': 'b'}), ref='heads/change')
|
2021-01-12 18:24:34 +07:00
|
|
|
pr = repo.make_pr(title="title", body="""\
|
|
|
|
Title
|
|
|
|
---
|
|
|
|
This is some text
|
|
|
|
|
|
|
|
Title 2
|
|
|
|
-------
|
|
|
|
This is more text
|
|
|
|
***
|
|
|
|
removed
|
|
|
|
""",
|
2023-08-10 18:21:21 +07:00
|
|
|
target='master', head='change')
|
2021-01-12 18:24:34 +07:00
|
|
|
repo.post_status(pr.head, 'success', 'legal/cla')
|
|
|
|
repo.post_status(pr.head, 'success', 'ci/runbot')
|
|
|
|
pr.post_comment('hansen r+ merge', config['role_reviewer']['token'])
|
|
|
|
env.run_crons()
|
|
|
|
|
|
|
|
with repo:
|
|
|
|
repo.post_status('heads/staging.master', 'success', 'ci/runbot')
|
|
|
|
repo.post_status('heads/staging.master', 'success', 'legal/cla')
|
|
|
|
env.run_crons()
|
|
|
|
|
|
|
|
head = repo.commit('heads/master')
|
|
|
|
assert head.message == textwrap.dedent(f"""\
|
|
|
|
title
|
|
|
|
|
|
|
|
Title
|
|
|
|
---
|
|
|
|
This is some text
|
|
|
|
|
|
|
|
Title 2
|
|
|
|
-------
|
|
|
|
This is more text
|
|
|
|
|
|
|
|
closes {repo.name}#{pr.number}
|
|
|
|
|
|
|
|
Signed-off-by: {reviewer}
|
|
|
|
""").strip(), "should not break the SETEX titles"
|
|
|
|
|
2021-01-21 19:15:32 +07:00
|
|
|
def test_rebase_no_edit(self, repo, env, users, config):
|
|
|
|
""" Only the merge messages should be de-breaked
|
|
|
|
"""
|
|
|
|
reviewer = get_partner(env, users["reviewer"]).formatted_email
|
|
|
|
with repo:
|
|
|
|
root = repo.make_commits(None, Commit("root", tree={'a': 'a'}), ref='heads/master')
|
|
|
|
|
2023-08-10 18:21:21 +07:00
|
|
|
repo.make_commits(root, Commit('Commit\n\nfirst\n***\nsecond', tree={'a': 'b'}), ref='heads/change')
|
|
|
|
pr = repo.make_pr(title="PR", body='first\n***\nsecond',
|
2021-10-20 14:46:53 +07:00
|
|
|
target='master', head='change')
|
2021-01-21 19:15:32 +07:00
|
|
|
repo.post_status(pr.head, 'success', 'legal/cla')
|
|
|
|
repo.post_status(pr.head, 'success', 'ci/runbot')
|
|
|
|
pr.post_comment('hansen r+', config['role_reviewer']['token'])
|
|
|
|
env.run_crons()
|
|
|
|
|
|
|
|
with repo:
|
|
|
|
repo.post_status('heads/staging.master', 'success', 'ci/runbot')
|
|
|
|
repo.post_status('heads/staging.master', 'success', 'legal/cla')
|
|
|
|
env.run_crons()
|
|
|
|
|
|
|
|
head = repo.commit('heads/master')
|
|
|
|
assert head.message == textwrap.dedent(f"""\
|
|
|
|
Commit
|
|
|
|
|
|
|
|
first
|
|
|
|
***
|
|
|
|
second
|
|
|
|
|
|
|
|
closes {repo.name}#{pr.number}
|
|
|
|
|
|
|
|
Signed-off-by: {reviewer}
|
|
|
|
""").strip(), "squashed / rebased messages should not be stripped"
|
|
|
|
|
2021-10-20 14:46:53 +07:00
|
|
|
def test_title_no_edit(self, repo, env, users, config):
|
|
|
|
"""The first line of a commit message should not be taken in account for
|
|
|
|
rewriting, especially as it can be untagged and interpreted as a
|
|
|
|
pseudo-header
|
|
|
|
"""
|
|
|
|
with repo:
|
|
|
|
repo.make_commits(None, Commit("0", tree={'a': '1'}), ref='heads/master')
|
|
|
|
repo.make_commits(
|
|
|
|
'master',
|
|
|
|
Commit('Some: thing\n\nis odd', tree={'b': '1'}),
|
|
|
|
Commit('thing: thong', tree={'b': '2'}),
|
|
|
|
ref='heads/change')
|
|
|
|
|
|
|
|
pr = repo.make_pr(target='master', head='change')
|
|
|
|
repo.post_status(pr.head, 'success', 'legal/cla')
|
|
|
|
repo.post_status(pr.head, 'success', 'ci/runbot')
|
|
|
|
pr.post_comment('hansen rebase-ff r+', config['role_reviewer']['token'])
|
|
|
|
env.run_crons()
|
|
|
|
|
|
|
|
pr_id = to_pr(env, pr)
|
|
|
|
assert pr_id.staging_id # check PR is staged
|
|
|
|
|
|
|
|
|
|
|
|
reviewer = get_partner(env, users["reviewer"]).formatted_email
|
|
|
|
staging_head = repo.commit('staging.master')
|
|
|
|
assert staging_head.message == f"""\
|
|
|
|
thing: thong
|
|
|
|
|
|
|
|
closes {pr_id.display_name}
|
|
|
|
|
|
|
|
Signed-off-by: {reviewer}"""
|
|
|
|
assert repo.commit(staging_head.parents[0]).message == f"""\
|
|
|
|
Some: thing
|
|
|
|
|
|
|
|
is odd
|
|
|
|
|
2024-05-30 15:55:40 +07:00
|
|
|
Part-of: {pr_id.display_name}
|
|
|
|
Signed-off-by: {reviewer}"""
|
2021-10-20 14:46:53 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
def test_pr_mergehead(self, repo, env, config):
|
2018-08-28 20:42:28 +07:00
|
|
|
""" if the head of the PR is a merge commit and one of the parents is
|
|
|
|
in the target, replicate the merge commit instead of merging
|
2018-08-29 21:51:53 +07:00
|
|
|
|
|
|
|
rankdir="BT"
|
|
|
|
M2 -> M1
|
|
|
|
C0 -> M1
|
|
|
|
C1 -> C0
|
|
|
|
C1 -> M2
|
|
|
|
|
|
|
|
C1 [label = "\\N / MERGE"]
|
2018-08-28 20:42:28 +07:00
|
|
|
"""
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
m1 = repo.make_commit(None, "M1", None, tree={'a': '0'})
|
|
|
|
m2 = repo.make_commit(m1, "M2", None, tree={'a': '1'})
|
|
|
|
repo.make_ref('heads/master', m2)
|
|
|
|
|
|
|
|
c0 = repo.make_commit(m1, 'C0', None, tree={'a': '0', 'b': '2'})
|
|
|
|
c1 = repo.make_commit([c0, m2], 'C1', None, tree={'a': '1', 'b': '2'})
|
|
|
|
prx = repo.make_pr(title="T", body="TT", target='master', head=c1)
|
|
|
|
env.run_crons()
|
|
|
|
|
|
|
|
with repo:
|
|
|
|
repo.post_status(prx.head, 'success', 'legal/cla')
|
|
|
|
repo.post_status(prx.head, 'success', 'ci/runbot')
|
|
|
|
prx.post_comment('hansen r+ merge', config['role_reviewer']['token'])
|
|
|
|
env.run_crons()
|
|
|
|
|
|
|
|
with repo:
|
|
|
|
repo.post_status('heads/staging.master', 'success', 'ci/runbot')
|
|
|
|
repo.post_status('heads/staging.master', 'success', 'legal/cla')
|
|
|
|
env.run_crons()
|
2018-08-29 21:51:53 +07:00
|
|
|
|
|
|
|
master = repo.commit('heads/master')
|
|
|
|
assert master.parents == [m2, c0]
|
|
|
|
m1 = node('M1')
|
|
|
|
expected = node('C1', node('C0', m1), node('M2', m1))
|
|
|
|
assert log_to_node(repo.log('heads/master')), expected
|
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
def test_pr_mergehead_nonmember(self, repo, env, users, config):
|
2018-08-29 21:51:53 +07:00
|
|
|
""" if the head of the PR is a merge commit but none of the parents is
|
|
|
|
in the target, merge normally
|
|
|
|
|
|
|
|
rankdir="BT"
|
|
|
|
M2 -> M1
|
|
|
|
B0 -> M1
|
|
|
|
C0 -> M1
|
|
|
|
C1 -> C0
|
|
|
|
C1 -> B0
|
|
|
|
|
|
|
|
MERGE -> M2
|
|
|
|
MERGE -> C1
|
|
|
|
"""
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
m1 = repo.make_commit(None, "M1", None, tree={'a': '0'})
|
|
|
|
m2 = repo.make_commit(m1, "M2", None, tree={'a': '1'})
|
|
|
|
repo.make_ref('heads/master', m2)
|
2018-08-29 21:51:53 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
b0 = repo.make_commit(m1, 'B0', None, tree={'a': '0', 'bb': 'bb'})
|
2018-08-29 21:51:53 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
c0 = repo.make_commit(m1, 'C0', None, tree={'a': '0', 'b': '2'})
|
|
|
|
c1 = repo.make_commit([c0, b0], 'C1', None, tree={'a': '0', 'b': '2', 'bb': 'bb'})
|
|
|
|
prx = repo.make_pr(title="T", body="TT", target='master', head=c1)
|
|
|
|
env.run_crons()
|
2018-08-29 21:51:53 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
repo.post_status(prx.head, 'success', 'legal/cla')
|
|
|
|
repo.post_status(prx.head, 'success', 'ci/runbot')
|
|
|
|
prx.post_comment('hansen r+ merge', config['role_reviewer']['token'])
|
|
|
|
env.run_crons()
|
2018-08-29 21:51:53 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
repo.post_status('heads/staging.master', 'success', 'ci/runbot')
|
|
|
|
repo.post_status('heads/staging.master', 'success', 'legal/cla')
|
|
|
|
env.run_crons()
|
2018-08-29 21:51:53 +07:00
|
|
|
|
|
|
|
master = repo.commit('heads/master')
|
|
|
|
assert master.parents == [m2, c1]
|
2019-08-23 21:16:30 +07:00
|
|
|
assert repo.read_tree(master) == {'a': '1', 'b': '2', 'bb': 'bb'}
|
2018-08-29 21:51:53 +07:00
|
|
|
|
|
|
|
m1 = node('M1')
|
2018-11-22 00:43:05 +07:00
|
|
|
reviewer = get_partner(env, users["reviewer"]).formatted_email
|
2018-08-29 21:51:53 +07:00
|
|
|
expected = node(
|
2018-11-22 00:43:05 +07:00
|
|
|
'T\n\nTT\n\ncloses {}#{}\n\nSigned-off-by: {}'.format(repo.name, prx.number, reviewer),
|
2018-08-29 21:51:53 +07:00
|
|
|
node('M2', m1),
|
|
|
|
node('C1', node('C0', m1), node('B0', m1))
|
|
|
|
)
|
|
|
|
assert log_to_node(repo.log('heads/master')), expected
|
2018-08-28 20:42:28 +07:00
|
|
|
|
2021-10-20 13:58:12 +07:00
|
|
|
def test_squash_merge(self, repo, env, config, users):
|
2023-08-10 18:21:21 +07:00
|
|
|
other_user = requests.get('https://api.github.com/user', headers={
|
2022-12-07 19:25:08 +07:00
|
|
|
'Authorization': 'token %s' % config['role_other']['token'],
|
|
|
|
}).json()
|
|
|
|
other_user = {
|
|
|
|
'name': other_user['name'] or other_user['login'],
|
|
|
|
# FIXME: not guaranteed
|
|
|
|
'email': other_user['email'] or 'other@example.org',
|
|
|
|
}
|
|
|
|
a_user = {'name': 'bob', 'email': 'builder@example.org', 'date': '1999-04-12T08:19:30Z'}
|
2021-10-20 13:58:12 +07:00
|
|
|
with repo:
|
|
|
|
repo.make_commits(None, Commit('initial', tree={'a': '0'}), ref='heads/master')
|
2018-03-14 16:37:46 +07:00
|
|
|
|
2022-02-07 18:00:31 +07:00
|
|
|
repo.make_commits(
|
|
|
|
'master',
|
2022-12-07 19:25:08 +07:00
|
|
|
Commit('sub', tree={'b': '0'}, committer=a_user),
|
2022-02-07 18:00:31 +07:00
|
|
|
ref='heads/other'
|
|
|
|
)
|
2021-10-20 13:58:12 +07:00
|
|
|
pr1 = repo.make_pr(title='first pr', target='master', head='other')
|
|
|
|
repo.post_status('other', 'success', 'legal/cla')
|
|
|
|
repo.post_status('other', 'success', 'ci/runbot')
|
2018-03-14 16:37:46 +07:00
|
|
|
|
2022-12-07 19:25:08 +07:00
|
|
|
pr_2_commits = repo.make_commits(
|
|
|
|
'master',
|
|
|
|
Commit('x', tree={'x': '0'}, author=other_user, committer=a_user),
|
|
|
|
Commit('y', tree={'x': '1'}, author=a_user, committer=other_user),
|
|
|
|
ref='heads/other2',
|
|
|
|
)
|
|
|
|
c1, c2 = map(repo.commit, pr_2_commits)
|
|
|
|
assert c1.author['name'] != c2.author['name']
|
|
|
|
assert c1.committer['name'] != c2.committer['name']
|
2021-10-20 13:58:12 +07:00
|
|
|
pr2 = repo.make_pr(title='second pr', target='master', head='other2')
|
|
|
|
repo.post_status('other2', 'success', 'legal/cla')
|
|
|
|
repo.post_status('other2', 'success', 'ci/runbot')
|
2019-10-10 14:22:12 +07:00
|
|
|
env.run_crons()
|
2018-03-14 16:37:46 +07:00
|
|
|
|
2021-10-20 13:58:12 +07:00
|
|
|
with repo: # comments sequencing
|
|
|
|
pr1.post_comment('hansen r+ squash', config['role_reviewer']['token'])
|
|
|
|
pr2.post_comment('hansen r+ squash', config['role_reviewer']['token'])
|
|
|
|
env.run_crons()
|
2018-03-14 16:37:46 +07:00
|
|
|
|
2021-10-20 13:58:12 +07:00
|
|
|
with repo:
|
|
|
|
repo.post_status('staging.master', 'success', 'legal/cla')
|
|
|
|
repo.post_status('staging.master', 'success', 'ci/runbot')
|
2019-10-10 14:22:12 +07:00
|
|
|
env.run_crons()
|
2021-10-20 13:58:12 +07:00
|
|
|
|
|
|
|
# PR 1 should have merged properly, the PR message should be the
|
|
|
|
# message of the merged commit
|
|
|
|
pr1_id = to_pr(env, pr1)
|
|
|
|
assert pr1_id.state == 'merged'
|
|
|
|
assert pr1.comments == [
|
|
|
|
seen(env, pr1, users),
|
|
|
|
(users['reviewer'], 'hansen r+ squash'),
|
2022-06-23 19:25:07 +07:00
|
|
|
(users['user'], 'Merge method set to squash.')
|
2021-10-20 13:58:12 +07:00
|
|
|
]
|
2022-11-04 15:16:58 +07:00
|
|
|
|
|
|
|
pr2_id = to_pr(env, pr2)
|
|
|
|
assert pr2_id.state == 'merged'
|
|
|
|
assert pr2.comments == [
|
|
|
|
seen(env, pr2, users),
|
|
|
|
(users['reviewer'], 'hansen r+ squash'),
|
|
|
|
(users['user'], 'Merge method set to squash.'),
|
|
|
|
]
|
|
|
|
|
|
|
|
two, one, _root = repo.log('master')
|
|
|
|
|
|
|
|
assert one['commit']['message'] == f"""first pr
|
2021-10-20 13:58:12 +07:00
|
|
|
|
|
|
|
closes {pr1_id.display_name}
|
|
|
|
|
|
|
|
Signed-off-by: {get_partner(env, users["reviewer"]).formatted_email}\
|
|
|
|
"""
|
2022-12-07 19:25:08 +07:00
|
|
|
assert one['commit']['committer']['name'] == a_user['name']
|
|
|
|
assert one['commit']['committer']['email'] == a_user['email']
|
2022-11-04 15:16:58 +07:00
|
|
|
commit_date = datetime.datetime.strptime(one['commit']['committer']['date'], '%Y-%m-%dT%H:%M:%SZ')
|
2022-02-07 18:00:31 +07:00
|
|
|
# using timestamp (and working in seconds) because `pytest.approx`
|
|
|
|
# silently fails on datetimes (#8395)
|
|
|
|
assert commit_date.timestamp() == pytest.approx(time.time(), abs=5*60), \
|
|
|
|
"the commit date of the merged commit should be about now, despite" \
|
|
|
|
" the source commit being >20 years old"
|
2021-10-20 13:58:12 +07:00
|
|
|
|
2022-12-07 19:25:08 +07:00
|
|
|
# FIXME: should probably get the token from the project to be sure it's
|
|
|
|
# the bot user
|
2023-08-10 18:21:21 +07:00
|
|
|
current_user = repo._session.get('https://api.github.com/user').json()
|
2022-12-07 19:25:08 +07:00
|
|
|
current_user = {
|
|
|
|
'name': current_user['name'] or current_user['login'],
|
|
|
|
# FIXME: not guaranteed
|
|
|
|
'email': current_user['email'] or 'user@example.org',
|
|
|
|
}
|
|
|
|
# since there are two authors & two committers on pr2, the auhor and
|
|
|
|
# committer of a squash commit should be reset to the bot's identity
|
|
|
|
assert two['commit']['committer']['name'] == current_user['name']
|
|
|
|
assert two['commit']['committer']['email'] == current_user['email']
|
|
|
|
assert two['commit']['author']['name'] == current_user['name']
|
|
|
|
assert two['commit']['author']['email'] == current_user['email']
|
2022-11-04 15:16:58 +07:00
|
|
|
assert two['commit']['message'] == f"""second pr
|
2018-03-14 16:37:46 +07:00
|
|
|
|
2022-11-04 15:16:58 +07:00
|
|
|
closes {pr2_id.display_name}
|
2018-03-14 16:37:46 +07:00
|
|
|
|
2022-12-07 19:25:08 +07:00
|
|
|
Signed-off-by: {get_partner(env, users["reviewer"]).formatted_email}
|
|
|
|
Co-authored-by: {a_user['name']} <{a_user['email']}>
|
|
|
|
Co-authored-by: {other_user['name']} <{other_user['email']}>\
|
2022-11-04 15:16:58 +07:00
|
|
|
"""
|
|
|
|
assert repo.read_tree(repo.commit(two['sha'])) == {
|
|
|
|
'a': '0',
|
|
|
|
'b': '0',
|
|
|
|
'x': '1',
|
2018-03-14 16:37:46 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2024-09-19 17:17:59 +07:00
|
|
|
class TestPRUpdate:
|
2018-03-14 16:37:46 +07:00
|
|
|
""" Pushing on a PR should update the HEAD except for merged PRs, it
|
|
|
|
can have additional effect (see individual tests)
|
|
|
|
"""
|
2024-09-19 17:17:59 +07:00
|
|
|
@pytest.fixture(autouse=True)
|
|
|
|
def master(self, repo):
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
2024-09-19 17:17:59 +07:00
|
|
|
[m] = repo.make_commits(None, Commit('initial', tree={'m': 'm'}), ref="heads/master")
|
|
|
|
return m
|
2018-03-14 16:37:46 +07:00
|
|
|
|
2024-09-19 17:17:59 +07:00
|
|
|
def test_update_opened(self, env, repo):
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
2024-10-07 13:07:59 +07:00
|
|
|
[c] = repo.make_commits("master", Commit('first', tree={'m': 'c1'}))
|
2024-09-19 17:17:59 +07:00
|
|
|
prx = repo.make_pr(target='master', head=c)
|
2024-01-16 21:03:45 +07:00
|
|
|
|
|
|
|
pr = to_pr(env, prx)
|
2018-03-14 16:37:46 +07:00
|
|
|
assert pr.head == c
|
2024-09-19 17:17:59 +07:00
|
|
|
# alter & push force PR entirely
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
2024-09-19 17:17:59 +07:00
|
|
|
[c2] = repo.make_commits("master", Commit('first', tree={'m': 'cc'}))
|
2019-10-10 14:22:12 +07:00
|
|
|
repo.update_ref(prx.ref, c2, force=True)
|
2018-03-14 16:37:46 +07:00
|
|
|
assert pr.head == c2
|
|
|
|
|
2024-09-19 17:17:59 +07:00
|
|
|
@pytest.mark.defaultstatuses
|
2018-03-14 16:37:46 +07:00
|
|
|
def test_update_validated(self, env, repo):
|
|
|
|
""" Should reset to opened
|
|
|
|
"""
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
2024-09-19 17:17:59 +07:00
|
|
|
[c] = repo.make_commits("master", Commit('first', tree={'m': 'c1'}))
|
|
|
|
pr = repo.make_pr(target='master', head=c)
|
|
|
|
repo.post_status(c, 'success')
|
2019-10-10 14:22:12 +07:00
|
|
|
env.run_crons()
|
2024-09-19 17:17:59 +07:00
|
|
|
|
|
|
|
pr_id = to_pr(env, pr)
|
|
|
|
assert pr_id.head == c
|
|
|
|
assert pr_id.state == 'validated'
|
2018-03-14 16:37:46 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
2024-09-19 17:17:59 +07:00
|
|
|
[c2] = repo.make_commits("master", Commit('first', tree={'m': 'cc'}))
|
|
|
|
repo.update_ref(pr.ref, c2, force=True)
|
|
|
|
assert pr_id.head == c2
|
|
|
|
assert pr_id.state == 'opened'
|
2018-03-14 16:37:46 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
def test_update_approved(self, env, repo, config):
|
|
|
|
with repo:
|
2024-09-19 17:17:59 +07:00
|
|
|
[c] = repo.make_commits("master", Commit('fist', tree={'m': 'c1'}))
|
|
|
|
prx = repo.make_pr(target='master', head=c)
|
2019-10-10 14:22:12 +07:00
|
|
|
prx.post_comment('hansen r+', config['role_reviewer']['token'])
|
2024-01-16 21:03:45 +07:00
|
|
|
|
|
|
|
pr = to_pr(env, prx)
|
2018-03-14 16:37:46 +07:00
|
|
|
assert pr.head == c
|
|
|
|
assert pr.state == 'approved'
|
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
2024-09-19 17:17:59 +07:00
|
|
|
[c2] = repo.make_commits("master", Commit('first', tree={'m': 'cc'}))
|
2019-10-10 14:22:12 +07:00
|
|
|
repo.update_ref(prx.ref, c2, force=True)
|
2018-03-14 16:37:46 +07:00
|
|
|
assert pr.head == c2
|
2018-09-03 18:55:39 +07:00
|
|
|
assert pr.state == 'opened'
|
2018-03-14 16:37:46 +07:00
|
|
|
|
2024-09-19 17:17:59 +07:00
|
|
|
@pytest.mark.defaultstatuses
|
2019-10-10 14:22:12 +07:00
|
|
|
def test_update_ready(self, env, repo, config):
|
2018-09-03 18:55:39 +07:00
|
|
|
""" Should reset to opened
|
2018-03-14 16:37:46 +07:00
|
|
|
"""
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
2024-09-19 17:17:59 +07:00
|
|
|
[c] = repo.make_commits("master", Commit('fist', tree={'m': 'c1'}))
|
|
|
|
prx = repo.make_pr(target='master', head=c)
|
|
|
|
repo.post_status(prx.head, 'success')
|
2019-10-10 14:22:12 +07:00
|
|
|
prx.post_comment('hansen r+', config['role_reviewer']['token'])
|
|
|
|
env.run_crons()
|
2024-01-16 21:03:45 +07:00
|
|
|
pr = to_pr(env, prx)
|
2018-03-14 16:37:46 +07:00
|
|
|
assert pr.head == c
|
|
|
|
assert pr.state == 'ready'
|
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
2024-09-19 17:17:59 +07:00
|
|
|
[c2] = repo.make_commits(c, Commit('first', tree={'m': 'cc'}))
|
2019-10-10 14:22:12 +07:00
|
|
|
repo.update_ref(prx.ref, c2, force=True)
|
2018-03-14 16:37:46 +07:00
|
|
|
assert pr.head == c2
|
2018-09-03 18:55:39 +07:00
|
|
|
assert pr.state == 'opened'
|
2018-03-14 16:37:46 +07:00
|
|
|
|
2024-09-19 17:17:59 +07:00
|
|
|
@pytest.mark.defaultstatuses
|
2019-10-10 14:22:12 +07:00
|
|
|
def test_update_staged(self, env, repo, config):
|
2018-09-03 18:55:39 +07:00
|
|
|
""" Should cancel the staging & reset PR to opened
|
2018-03-14 16:37:46 +07:00
|
|
|
"""
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
2024-09-19 17:17:59 +07:00
|
|
|
[c] = repo.make_commits("master", Commit('fist', tree={'m': 'c1'}))
|
|
|
|
prx = repo.make_pr(target='master', head=c)
|
|
|
|
repo.post_status(prx.head, 'success')
|
2019-10-10 14:22:12 +07:00
|
|
|
prx.post_comment('hansen r+', config['role_reviewer']['token'])
|
2024-01-16 21:03:45 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
env.run_crons()
|
2024-01-16 21:03:45 +07:00
|
|
|
pr = to_pr(env, prx)
|
2018-03-14 16:37:46 +07:00
|
|
|
assert pr.state == 'ready'
|
|
|
|
assert pr.staging_id
|
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
2024-09-19 17:17:59 +07:00
|
|
|
[c2] = repo.make_commits(c, Commit('first', tree={'m': 'cc'}))
|
2019-10-10 14:22:12 +07:00
|
|
|
repo.update_ref(prx.ref, c2, force=True)
|
2018-03-14 16:37:46 +07:00
|
|
|
assert pr.head == c2
|
2018-09-03 18:55:39 +07:00
|
|
|
assert pr.state == 'opened'
|
2018-03-14 16:37:46 +07:00
|
|
|
assert not pr.staging_id
|
|
|
|
assert not env['runbot_merge.stagings'].search([])
|
|
|
|
|
2024-09-19 17:17:59 +07:00
|
|
|
@pytest.mark.defaultstatuses
|
2019-10-10 14:22:12 +07:00
|
|
|
def test_split(self, env, repo, config):
|
2019-07-31 14:20:02 +07:00
|
|
|
""" Should remove the PR from its split, and possibly delete the split
|
|
|
|
entirely.
|
|
|
|
"""
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
2024-09-19 17:17:59 +07:00
|
|
|
repo.make_commits("master", Commit('first', tree={'1': '1'}), ref="heads/p1")
|
|
|
|
prx1 = repo.make_pr(target='master', head='p1')
|
|
|
|
repo.post_status(prx1.head, 'success')
|
2019-10-10 14:22:12 +07:00
|
|
|
prx1.post_comment('hansen r+', config['role_reviewer']['token'])
|
|
|
|
|
2024-09-19 17:17:59 +07:00
|
|
|
[c] = repo.make_commits("master", Commit('first', tree={'2': '2'}), ref="heads/p2")
|
|
|
|
prx2 = repo.make_pr(target='master', head='p2')
|
|
|
|
repo.post_status(prx2.head, 'success')
|
2019-10-10 14:22:12 +07:00
|
|
|
prx2.post_comment('hansen r+', config['role_reviewer']['token'])
|
|
|
|
env.run_crons()
|
2019-07-31 14:20:02 +07:00
|
|
|
|
|
|
|
pr1, pr2 = env['runbot_merge.pull_requests'].search([], order='number')
|
|
|
|
assert pr1.number == prx1.number
|
|
|
|
assert pr2.number == prx2.number
|
|
|
|
assert pr1.staging_id == pr2.staging_id
|
|
|
|
s0 = pr1.staging_id
|
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
2024-09-19 17:17:59 +07:00
|
|
|
repo.post_status('heads/staging.master', 'failure')
|
2019-10-10 14:22:12 +07:00
|
|
|
env.run_crons()
|
2019-07-31 14:20:02 +07:00
|
|
|
|
|
|
|
assert pr1.staging_id and pr1.staging_id != s0, "pr1 should have been re-staged"
|
|
|
|
assert not pr2.staging_id, "pr2 should not"
|
|
|
|
# TODO: remote doesn't currently handle env context so can't mess
|
|
|
|
# around using active_test=False
|
|
|
|
assert env['runbot_merge.split'].search([])
|
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
2024-09-19 17:17:59 +07:00
|
|
|
[c2] = repo.make_commits(c, Commit('second', tree={'2': '22'}))
|
|
|
|
repo.update_ref(prx2.ref, c2, force=True)
|
2019-07-31 14:20:02 +07:00
|
|
|
# probably not necessary ATM but...
|
2019-10-10 14:22:12 +07:00
|
|
|
env.run_crons()
|
2019-07-31 14:20:02 +07:00
|
|
|
|
|
|
|
assert pr2.state == 'opened', "state should have been reset"
|
|
|
|
assert not env['runbot_merge.split'].search([]), "there should be no split left"
|
|
|
|
|
2024-09-19 17:17:59 +07:00
|
|
|
@pytest.mark.defaultstatuses
|
2019-10-10 14:22:12 +07:00
|
|
|
def test_update_error(self, env, repo, config):
|
|
|
|
with repo:
|
2024-09-19 17:17:59 +07:00
|
|
|
[c] = repo.make_commits("master", Commit('fist', tree={'m': 'c1'}))
|
|
|
|
prx = repo.make_pr(target='master', head=c)
|
|
|
|
repo.post_status(prx.head, 'success')
|
2019-10-10 14:22:12 +07:00
|
|
|
prx.post_comment('hansen r+', config['role_reviewer']['token'])
|
|
|
|
env.run_crons()
|
2024-01-16 21:03:45 +07:00
|
|
|
pr = to_pr(env, prx)
|
2018-03-14 16:37:46 +07:00
|
|
|
assert pr.state == 'ready'
|
|
|
|
assert pr.staging_id
|
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
2024-09-19 17:17:59 +07:00
|
|
|
repo.post_status('staging.master', 'failure')
|
2019-10-10 14:22:12 +07:00
|
|
|
env.run_crons()
|
2018-03-14 16:37:46 +07:00
|
|
|
assert not pr.staging_id
|
|
|
|
assert pr.state == 'error'
|
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
2024-09-19 17:17:59 +07:00
|
|
|
[c2] = repo.make_commits(c, Commit('first', tree={'m': 'cc'}))
|
2019-10-10 14:22:12 +07:00
|
|
|
repo.update_ref(prx.ref, c2, force=True)
|
2018-03-14 16:37:46 +07:00
|
|
|
assert pr.head == c2
|
2019-03-01 21:52:13 +07:00
|
|
|
assert pr.state == 'opened'
|
2018-03-14 16:37:46 +07:00
|
|
|
|
|
|
|
def test_unknown_pr(self, env, repo):
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
2024-09-19 17:17:59 +07:00
|
|
|
[m, c] = repo.make_commits(
|
|
|
|
None,
|
|
|
|
Commit('initial', tree={'m': 'm'}),
|
|
|
|
Commit('first', tree={'m': 'c1'}),
|
|
|
|
)
|
2019-10-10 14:22:12 +07:00
|
|
|
repo.make_ref('heads/1.0', m)
|
|
|
|
prx = repo.make_pr(title='title', body='body', target='1.0', head=c)
|
2024-12-02 20:17:28 +07:00
|
|
|
with pytest.raises(TimeoutError):
|
|
|
|
to_pr(env, prx)
|
2018-03-14 16:37:46 +07:00
|
|
|
|
|
|
|
env['runbot_merge.project'].search([]).write({
|
|
|
|
'branch_ids': [(0, 0, {'name': '1.0'})]
|
|
|
|
})
|
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
2024-09-19 17:17:59 +07:00
|
|
|
[c2] = repo.make_commits(c, Commit('second', tree={'m': 'c2'}))
|
2019-10-10 14:22:12 +07:00
|
|
|
repo.update_ref(prx.ref, c2, force=True)
|
2018-03-14 16:37:46 +07:00
|
|
|
|
2024-12-02 20:17:28 +07:00
|
|
|
with pytest.raises(TimeoutError):
|
|
|
|
to_pr(env, prx)
|
2018-03-14 16:37:46 +07:00
|
|
|
|
2024-09-19 17:17:59 +07:00
|
|
|
@pytest.mark.defaultstatuses
|
2019-03-04 16:34:40 +07:00
|
|
|
def test_update_to_ci(self, env, repo):
|
|
|
|
""" If a PR is updated to a known-valid commit, it should be
|
|
|
|
validated
|
|
|
|
"""
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
2024-09-19 17:17:59 +07:00
|
|
|
[c] = repo.make_commits("master", Commit('fist', tree={'m': 'c1'}))
|
|
|
|
[c2] = repo.make_commits("master", Commit('first', tree={'m': 'cc'}))
|
|
|
|
repo.post_status(c2, 'success')
|
2019-10-10 14:22:12 +07:00
|
|
|
env.run_crons()
|
|
|
|
|
|
|
|
with repo:
|
2024-09-19 17:17:59 +07:00
|
|
|
prx = repo.make_pr(target='master', head=c)
|
2024-01-16 21:03:45 +07:00
|
|
|
pr = to_pr(env, prx)
|
2019-03-04 16:34:40 +07:00
|
|
|
assert pr.head == c
|
|
|
|
assert pr.state == 'opened'
|
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
repo.update_ref(prx.ref, c2, force=True)
|
2019-03-04 16:34:40 +07:00
|
|
|
assert pr.head == c2
|
|
|
|
assert pr.state == 'validated'
|
|
|
|
|
2024-09-19 17:17:59 +07:00
|
|
|
@pytest.mark.defaultstatuses
|
2023-01-20 21:16:37 +07:00
|
|
|
def test_update_missed(self, env, repo, config, users):
|
2019-11-20 20:57:40 +07:00
|
|
|
""" Sometimes github's webhooks don't trigger properly, a branch's HEAD
|
|
|
|
does not get updated and we might e.g. attempt to merge a PR despite it
|
|
|
|
now being unreviewed or failing CI or somesuch.
|
|
|
|
|
2023-01-20 21:16:37 +07:00
|
|
|
Therefore during the staging process we should check what we can, reject
|
|
|
|
the staging if cricical properties were found to mismatch, and notify
|
|
|
|
the pull request.
|
2019-11-20 20:57:40 +07:00
|
|
|
|
2023-01-20 21:16:37 +07:00
|
|
|
The PR should then be reset to open (and transition to validated on its
|
|
|
|
own if the existing or new head has valid statuses), we don't want to
|
|
|
|
put it in an error state as technically there's no error, just something
|
|
|
|
which went a bit weird.
|
2019-11-20 20:57:40 +07:00
|
|
|
"""
|
|
|
|
with repo:
|
2023-01-20 21:16:37 +07:00
|
|
|
[c] = repo.make_commits(None, repo.Commit('m', tree={'a': '0'}), ref='heads/master')
|
|
|
|
repo.make_ref('heads/somethingelse', c)
|
2019-11-20 20:57:40 +07:00
|
|
|
|
|
|
|
[c] = repo.make_commits(
|
2024-09-19 17:17:59 +07:00
|
|
|
'master', repo.Commit('title \n\nbody', tree={'a': '1'}), ref='heads/abranch')
|
2019-11-20 20:57:40 +07:00
|
|
|
pr = repo.make_pr(target='master', head='abranch')
|
2024-09-19 17:17:59 +07:00
|
|
|
repo.post_status(pr.head, 'success')
|
2019-11-20 20:57:40 +07:00
|
|
|
pr.post_comment('hansen r+', config['role_reviewer']['token'])
|
2024-01-16 21:03:45 +07:00
|
|
|
|
|
|
|
env.run_crons()
|
|
|
|
pr_id = to_pr(env, pr)
|
2024-07-30 18:42:00 +07:00
|
|
|
env.run_crons(None)
|
2023-02-14 19:38:37 +07:00
|
|
|
assert pr_id.message == 'title\n\nbody'
|
2019-11-20 20:57:40 +07:00
|
|
|
assert pr_id.state == 'ready'
|
[CHG] *: rewrite commands set, rework status management
This commit revisits the commands set in order to make it more
regular, and limit inconsistent command-sets, although it includes
pseudo-command aliases for common tasks now removed from the core set.
Hard Errors
===========
The previous iteration of the commands set would ignore any
non-command term in a command line. This has been changed to hard
error (and ignoring the entire thing) if any command is unknown or
invalid.
This fixes inconsistent / unexpected interpretations where a user
sends a command, then writes a novel on the same line some words of
which happen to *also* be commands, leading to merge states they did
not expect. They should now be told to fuck off.
Priority Restructuring
----------------------
The numerical priority system was pretty messy in that it confused
"staging priority" (in ways which were not entirely straightforward)
with overrides to other concerns.
This has now being split along all the axis, with separate command
subsets for:
- staging prioritisation, now separated between `default`, `priority`,
and `alone`,
- `default` means PRs are picked by an unspecified order when
creating a staging, if nothing better is available
- `priority` means PRs are picked first when staging, however if
`priority` PRs don't fill the staging the rest will be filled with
`default`, this mode did not previously exist
- `alone` means the PRs are picked first, before splits, and only
`alone` PRs can be part of the staging (which usually matches the
modename)
- `skipchecks` overrides both statuses and approval checks, for the
batch, something previously implied in `p=0`, but now
independent. Setting `skipchecks` basically makes the entire batch
`ready`.
For consistency this also sets the reviewer implicitly: since
skipchecks overrides both statuses *and approval*, whoever enables
this mode is essentially the reviewer.
- `cancel` cancels any ongoing staging when the marked PR becomes
ready again, previously this was also implied (in a more restricted
form) by setting `p=0`
FWBot removal
=============
While the "forwardport bot" still exists as an API level (to segregate
access rights between tokens) it has been removed as an interaction
point, as part of the modules merge plan. As a result,
fwbot stops responding
----------------------
Feedback messages are now always sent by the mergebot, the
forward-porting bot should not send any message or notification
anymore.
commands moved to the merge bot
-------------------------------
- `ignore`/`up to` simply changes bot
- `close` as well
- `skipci` is now a choice / flag of an `fw` command, which denotes
the forward-port policy,
- `fw=default` is the old `ci` and resets the policy to default,
that is wait for the PR to be merged to create forward ports, and
for the required statuses on each forward port to be received
before creating the next
- `fw=skipci` is the old `skipci`, it waits for the merge of the
base PR but then creates all the forward ports immediately (unless
it gets a conflict)
- `fw=skipmerge` immediately creates all the forward ports, without
even waiting for the PR to be merged
This is a completely new mode, and may be rather broken as until
now the 'bot has always assumed the source PR had been merged.
approval rework
---------------
Because of the previous section, there is no distinguishing feature
between `mergebot r+` = "merge this PR" and `forwardbot r+` = "merge
this PR and all its parent with different access rights".
As a result, the two have been merged under a single `mergebot r+`
with heuristics attempting to provide the best experience:
- if approving a non-forward port, the behavior does not change
- else, with review rights on the source, all ancestors are approved
- else, as author of the original, approves all ancestors which descend
from a merged PR
- else, approves all ancestors up to and including the oldest ancestor
to which we have review rights
Most notably, the source's author is not delegated on the source or
any of its descendants anymore. This might need to be revisited if it
provides too restrictive.
For the very specialized need of approving a forward-port *and none of
its ancestors*, `review=` can now take a comma (`,`) separated list of
pull request numbers (github numbers, not mergebot ids).
Computed State
==============
The `state` field of pull requests is now computed. Hopefully this
makes the status more consistent and predictable in the long run, and
importantly makes status management more reliable (because reference
datum get updated naturally flowing to the state).
For now however it makes things more complicated as some of the states
have to be separately signaled or updated:
- `closed` and `error` are now separate flags
- `merge_date` is pulled down from forwardport and becomes the
transition signal for ready -> merged
- `reviewed_by` becomes the transition signal for approval (might be a
good idea to rename it...)
- `status` is computed from the head's statuses and overrides, and
*that* becomes the validation state
Ideally, batch-level flags like `skipchecks` should be on, well, the
batch, and `state` should have a dependency on the batch. However
currently the batch is not a durable / permanent member of the system,
so it's a PR-level flag and a messy pile.
On notable change is that *forcing* the state to `ready` now does that
but also sets the reviewer, `skipchecks`, and overrides to ensure the
API-mediated readying does not get rolled back by e.g. the runbot
sending a status.
This is useful for a few types of automated / programmatic PRs
e.g. translation exports, where we set the state programmatically to
limit noise.
recursive dependency hack
-------------------------
Given a sequence of PRs with an override of the source, if one of the
PRs is updated its descendants should not have the override
anymore. However if the updated PR gets overridden, its descendants
should have *that* override.
This requires some unholy manipulations via an override of `modified`,
as the ORM supports recursive fields but not recursive
dependencies (on a different field).
unconditional followup scheduling
---------------------------------
Previously scheduling forward-port followup was contigent on the FW
policy, but it's not actually correct if the new PR is *immediately*
validated (which can happen now that the field is computed, if there
are no required statuses *or* all of the required statuses are
overridden by an ancestor) as nothing will trigger the state change
and thus scheduling of the fp followup.
The followup function checks all the properties of the batch to port,
so this should not result on incorrect ports. Although it's a bit more
expensive, and will lead to more spam.
Previously this would not happen because on creation of a PR the
validation task (commit -> PR) would still have to execute.
Misc Changes
============
- If a PR is marked as overriding / canceling stagings, it now does
so on retry not just when setting initially.
This was not handled at all previously, so a PR in P0 going into
error due to e.g. a non-deterministic bug would be retried and still
p=0, but a current staging would not get cancelled. Same when a PR
in p=0 goes into error because something was failed, then is updated
with a fix.
- Add tracking to a bunch of relevant PR fields.
Post-mortem analysis currently generally requires going through the
text logs to see what happened, which is annoying.
There is a nondeterminism / inconsistency in the tracking which
sometimes leads the admin user to trigger tracking before the bot
does, leading to the staging tracking being attributed to them
during tests, shove under the carpet by ignoring the user to whom
that tracking is attributed.
When multiple users update tracked fields in the same transaction
all the changes are attributed to the first one having triggered
tracking (?), I couldn't find why the admin sometimes takes over.
- added and leveraged support for enum-backed selection fields
- moved variuous fields from forwardport to runbot_merge
- fix a migration which had never worked and which never run (because
I forgot to bump the version on the module)
- remove some unnecessary intermediate de/serialisation
fixes #673, fixes #309, fixes #792, fixes #846 (probably)
2023-10-31 13:42:07 +07:00
|
|
|
old_reviewer = pr_id.reviewed_by
|
2019-11-20 20:57:40 +07:00
|
|
|
|
|
|
|
# TODO: find way to somehow skip / ignore the update_ref?
|
|
|
|
with repo:
|
|
|
|
# can't push a second commit because then the staging crashes due
|
|
|
|
# to the PR *actually* having more than 1 commit and thus needing
|
|
|
|
# a configuration
|
|
|
|
[c2] = repo.make_commits('heads/master', repo.Commit('c2', tree={'a': '2'}))
|
2024-09-19 17:17:59 +07:00
|
|
|
repo.post_status(c2, 'success')
|
2019-11-20 20:57:40 +07:00
|
|
|
repo.update_ref(pr.ref, c2, force=True)
|
|
|
|
|
2023-01-20 21:16:37 +07:00
|
|
|
other = env['runbot_merge.branch'].create({
|
|
|
|
'name': 'somethingelse',
|
|
|
|
'project_id': env['runbot_merge.project'].search([]).id,
|
|
|
|
})
|
|
|
|
|
2019-11-20 20:57:40 +07:00
|
|
|
# we missed the update notification so the db should still be at c and
|
|
|
|
# in a "ready" state
|
|
|
|
pr_id.write({
|
|
|
|
'head': c,
|
[CHG] *: rewrite commands set, rework status management
This commit revisits the commands set in order to make it more
regular, and limit inconsistent command-sets, although it includes
pseudo-command aliases for common tasks now removed from the core set.
Hard Errors
===========
The previous iteration of the commands set would ignore any
non-command term in a command line. This has been changed to hard
error (and ignoring the entire thing) if any command is unknown or
invalid.
This fixes inconsistent / unexpected interpretations where a user
sends a command, then writes a novel on the same line some words of
which happen to *also* be commands, leading to merge states they did
not expect. They should now be told to fuck off.
Priority Restructuring
----------------------
The numerical priority system was pretty messy in that it confused
"staging priority" (in ways which were not entirely straightforward)
with overrides to other concerns.
This has now being split along all the axis, with separate command
subsets for:
- staging prioritisation, now separated between `default`, `priority`,
and `alone`,
- `default` means PRs are picked by an unspecified order when
creating a staging, if nothing better is available
- `priority` means PRs are picked first when staging, however if
`priority` PRs don't fill the staging the rest will be filled with
`default`, this mode did not previously exist
- `alone` means the PRs are picked first, before splits, and only
`alone` PRs can be part of the staging (which usually matches the
modename)
- `skipchecks` overrides both statuses and approval checks, for the
batch, something previously implied in `p=0`, but now
independent. Setting `skipchecks` basically makes the entire batch
`ready`.
For consistency this also sets the reviewer implicitly: since
skipchecks overrides both statuses *and approval*, whoever enables
this mode is essentially the reviewer.
- `cancel` cancels any ongoing staging when the marked PR becomes
ready again, previously this was also implied (in a more restricted
form) by setting `p=0`
FWBot removal
=============
While the "forwardport bot" still exists as an API level (to segregate
access rights between tokens) it has been removed as an interaction
point, as part of the modules merge plan. As a result,
fwbot stops responding
----------------------
Feedback messages are now always sent by the mergebot, the
forward-porting bot should not send any message or notification
anymore.
commands moved to the merge bot
-------------------------------
- `ignore`/`up to` simply changes bot
- `close` as well
- `skipci` is now a choice / flag of an `fw` command, which denotes
the forward-port policy,
- `fw=default` is the old `ci` and resets the policy to default,
that is wait for the PR to be merged to create forward ports, and
for the required statuses on each forward port to be received
before creating the next
- `fw=skipci` is the old `skipci`, it waits for the merge of the
base PR but then creates all the forward ports immediately (unless
it gets a conflict)
- `fw=skipmerge` immediately creates all the forward ports, without
even waiting for the PR to be merged
This is a completely new mode, and may be rather broken as until
now the 'bot has always assumed the source PR had been merged.
approval rework
---------------
Because of the previous section, there is no distinguishing feature
between `mergebot r+` = "merge this PR" and `forwardbot r+` = "merge
this PR and all its parent with different access rights".
As a result, the two have been merged under a single `mergebot r+`
with heuristics attempting to provide the best experience:
- if approving a non-forward port, the behavior does not change
- else, with review rights on the source, all ancestors are approved
- else, as author of the original, approves all ancestors which descend
from a merged PR
- else, approves all ancestors up to and including the oldest ancestor
to which we have review rights
Most notably, the source's author is not delegated on the source or
any of its descendants anymore. This might need to be revisited if it
provides too restrictive.
For the very specialized need of approving a forward-port *and none of
its ancestors*, `review=` can now take a comma (`,`) separated list of
pull request numbers (github numbers, not mergebot ids).
Computed State
==============
The `state` field of pull requests is now computed. Hopefully this
makes the status more consistent and predictable in the long run, and
importantly makes status management more reliable (because reference
datum get updated naturally flowing to the state).
For now however it makes things more complicated as some of the states
have to be separately signaled or updated:
- `closed` and `error` are now separate flags
- `merge_date` is pulled down from forwardport and becomes the
transition signal for ready -> merged
- `reviewed_by` becomes the transition signal for approval (might be a
good idea to rename it...)
- `status` is computed from the head's statuses and overrides, and
*that* becomes the validation state
Ideally, batch-level flags like `skipchecks` should be on, well, the
batch, and `state` should have a dependency on the batch. However
currently the batch is not a durable / permanent member of the system,
so it's a PR-level flag and a messy pile.
On notable change is that *forcing* the state to `ready` now does that
but also sets the reviewer, `skipchecks`, and overrides to ensure the
API-mediated readying does not get rolled back by e.g. the runbot
sending a status.
This is useful for a few types of automated / programmatic PRs
e.g. translation exports, where we set the state programmatically to
limit noise.
recursive dependency hack
-------------------------
Given a sequence of PRs with an override of the source, if one of the
PRs is updated its descendants should not have the override
anymore. However if the updated PR gets overridden, its descendants
should have *that* override.
This requires some unholy manipulations via an override of `modified`,
as the ORM supports recursive fields but not recursive
dependencies (on a different field).
unconditional followup scheduling
---------------------------------
Previously scheduling forward-port followup was contigent on the FW
policy, but it's not actually correct if the new PR is *immediately*
validated (which can happen now that the field is computed, if there
are no required statuses *or* all of the required statuses are
overridden by an ancestor) as nothing will trigger the state change
and thus scheduling of the fp followup.
The followup function checks all the properties of the batch to port,
so this should not result on incorrect ports. Although it's a bit more
expensive, and will lead to more spam.
Previously this would not happen because on creation of a PR the
validation task (commit -> PR) would still have to execute.
Misc Changes
============
- If a PR is marked as overriding / canceling stagings, it now does
so on retry not just when setting initially.
This was not handled at all previously, so a PR in P0 going into
error due to e.g. a non-deterministic bug would be retried and still
p=0, but a current staging would not get cancelled. Same when a PR
in p=0 goes into error because something was failed, then is updated
with a fix.
- Add tracking to a bunch of relevant PR fields.
Post-mortem analysis currently generally requires going through the
text logs to see what happened, which is annoying.
There is a nondeterminism / inconsistency in the tracking which
sometimes leads the admin user to trigger tracking before the bot
does, leading to the staging tracking being attributed to them
during tests, shove under the carpet by ignoring the user to whom
that tracking is attributed.
When multiple users update tracked fields in the same transaction
all the changes are attributed to the first one having triggered
tracking (?), I couldn't find why the admin sometimes takes over.
- added and leveraged support for enum-backed selection fields
- moved variuous fields from forwardport to runbot_merge
- fix a migration which had never worked and which never run (because
I forgot to bump the version on the module)
- remove some unnecessary intermediate de/serialisation
fixes #673, fixes #309, fixes #792, fixes #846 (probably)
2023-10-31 13:42:07 +07:00
|
|
|
'reviewed_by': old_reviewer.id,
|
2023-01-20 21:16:37 +07:00
|
|
|
'message': "Something else",
|
|
|
|
'target': other.id,
|
2019-11-20 20:57:40 +07:00
|
|
|
})
|
[CHG] *: rewrite commands set, rework status management
This commit revisits the commands set in order to make it more
regular, and limit inconsistent command-sets, although it includes
pseudo-command aliases for common tasks now removed from the core set.
Hard Errors
===========
The previous iteration of the commands set would ignore any
non-command term in a command line. This has been changed to hard
error (and ignoring the entire thing) if any command is unknown or
invalid.
This fixes inconsistent / unexpected interpretations where a user
sends a command, then writes a novel on the same line some words of
which happen to *also* be commands, leading to merge states they did
not expect. They should now be told to fuck off.
Priority Restructuring
----------------------
The numerical priority system was pretty messy in that it confused
"staging priority" (in ways which were not entirely straightforward)
with overrides to other concerns.
This has now being split along all the axis, with separate command
subsets for:
- staging prioritisation, now separated between `default`, `priority`,
and `alone`,
- `default` means PRs are picked by an unspecified order when
creating a staging, if nothing better is available
- `priority` means PRs are picked first when staging, however if
`priority` PRs don't fill the staging the rest will be filled with
`default`, this mode did not previously exist
- `alone` means the PRs are picked first, before splits, and only
`alone` PRs can be part of the staging (which usually matches the
modename)
- `skipchecks` overrides both statuses and approval checks, for the
batch, something previously implied in `p=0`, but now
independent. Setting `skipchecks` basically makes the entire batch
`ready`.
For consistency this also sets the reviewer implicitly: since
skipchecks overrides both statuses *and approval*, whoever enables
this mode is essentially the reviewer.
- `cancel` cancels any ongoing staging when the marked PR becomes
ready again, previously this was also implied (in a more restricted
form) by setting `p=0`
FWBot removal
=============
While the "forwardport bot" still exists as an API level (to segregate
access rights between tokens) it has been removed as an interaction
point, as part of the modules merge plan. As a result,
fwbot stops responding
----------------------
Feedback messages are now always sent by the mergebot, the
forward-porting bot should not send any message or notification
anymore.
commands moved to the merge bot
-------------------------------
- `ignore`/`up to` simply changes bot
- `close` as well
- `skipci` is now a choice / flag of an `fw` command, which denotes
the forward-port policy,
- `fw=default` is the old `ci` and resets the policy to default,
that is wait for the PR to be merged to create forward ports, and
for the required statuses on each forward port to be received
before creating the next
- `fw=skipci` is the old `skipci`, it waits for the merge of the
base PR but then creates all the forward ports immediately (unless
it gets a conflict)
- `fw=skipmerge` immediately creates all the forward ports, without
even waiting for the PR to be merged
This is a completely new mode, and may be rather broken as until
now the 'bot has always assumed the source PR had been merged.
approval rework
---------------
Because of the previous section, there is no distinguishing feature
between `mergebot r+` = "merge this PR" and `forwardbot r+` = "merge
this PR and all its parent with different access rights".
As a result, the two have been merged under a single `mergebot r+`
with heuristics attempting to provide the best experience:
- if approving a non-forward port, the behavior does not change
- else, with review rights on the source, all ancestors are approved
- else, as author of the original, approves all ancestors which descend
from a merged PR
- else, approves all ancestors up to and including the oldest ancestor
to which we have review rights
Most notably, the source's author is not delegated on the source or
any of its descendants anymore. This might need to be revisited if it
provides too restrictive.
For the very specialized need of approving a forward-port *and none of
its ancestors*, `review=` can now take a comma (`,`) separated list of
pull request numbers (github numbers, not mergebot ids).
Computed State
==============
The `state` field of pull requests is now computed. Hopefully this
makes the status more consistent and predictable in the long run, and
importantly makes status management more reliable (because reference
datum get updated naturally flowing to the state).
For now however it makes things more complicated as some of the states
have to be separately signaled or updated:
- `closed` and `error` are now separate flags
- `merge_date` is pulled down from forwardport and becomes the
transition signal for ready -> merged
- `reviewed_by` becomes the transition signal for approval (might be a
good idea to rename it...)
- `status` is computed from the head's statuses and overrides, and
*that* becomes the validation state
Ideally, batch-level flags like `skipchecks` should be on, well, the
batch, and `state` should have a dependency on the batch. However
currently the batch is not a durable / permanent member of the system,
so it's a PR-level flag and a messy pile.
On notable change is that *forcing* the state to `ready` now does that
but also sets the reviewer, `skipchecks`, and overrides to ensure the
API-mediated readying does not get rolled back by e.g. the runbot
sending a status.
This is useful for a few types of automated / programmatic PRs
e.g. translation exports, where we set the state programmatically to
limit noise.
recursive dependency hack
-------------------------
Given a sequence of PRs with an override of the source, if one of the
PRs is updated its descendants should not have the override
anymore. However if the updated PR gets overridden, its descendants
should have *that* override.
This requires some unholy manipulations via an override of `modified`,
as the ORM supports recursive fields but not recursive
dependencies (on a different field).
unconditional followup scheduling
---------------------------------
Previously scheduling forward-port followup was contigent on the FW
policy, but it's not actually correct if the new PR is *immediately*
validated (which can happen now that the field is computed, if there
are no required statuses *or* all of the required statuses are
overridden by an ancestor) as nothing will trigger the state change
and thus scheduling of the fp followup.
The followup function checks all the properties of the batch to port,
so this should not result on incorrect ports. Although it's a bit more
expensive, and will lead to more spam.
Previously this would not happen because on creation of a PR the
validation task (commit -> PR) would still have to execute.
Misc Changes
============
- If a PR is marked as overriding / canceling stagings, it now does
so on retry not just when setting initially.
This was not handled at all previously, so a PR in P0 going into
error due to e.g. a non-deterministic bug would be retried and still
p=0, but a current staging would not get cancelled. Same when a PR
in p=0 goes into error because something was failed, then is updated
with a fix.
- Add tracking to a bunch of relevant PR fields.
Post-mortem analysis currently generally requires going through the
text logs to see what happened, which is annoying.
There is a nondeterminism / inconsistency in the tracking which
sometimes leads the admin user to trigger tracking before the bot
does, leading to the staging tracking being attributed to them
during tests, shove under the carpet by ignoring the user to whom
that tracking is attributed.
When multiple users update tracked fields in the same transaction
all the changes are attributed to the first one having triggered
tracking (?), I couldn't find why the admin sometimes takes over.
- added and leveraged support for enum-backed selection fields
- moved variuous fields from forwardport to runbot_merge
- fix a migration which had never worked and which never run (because
I forgot to bump the version on the module)
- remove some unnecessary intermediate de/serialisation
fixes #673, fixes #309, fixes #792, fixes #846 (probably)
2023-10-31 13:42:07 +07:00
|
|
|
assert pr_id.head == c
|
|
|
|
assert pr_id.state == "ready"
|
2019-11-20 20:57:40 +07:00
|
|
|
|
|
|
|
env.run_crons()
|
|
|
|
|
|
|
|
# the PR should not get merged, and should be updated
|
|
|
|
assert pr_id.state == 'validated'
|
|
|
|
assert pr_id.head == c2
|
2023-02-14 19:38:37 +07:00
|
|
|
assert pr_id.message == 'title\n\nbody'
|
2023-01-20 21:16:37 +07:00
|
|
|
assert pr_id.target.name == 'master'
|
[CHG] *: rewrite commands set, rework status management
This commit revisits the commands set in order to make it more
regular, and limit inconsistent command-sets, although it includes
pseudo-command aliases for common tasks now removed from the core set.
Hard Errors
===========
The previous iteration of the commands set would ignore any
non-command term in a command line. This has been changed to hard
error (and ignoring the entire thing) if any command is unknown or
invalid.
This fixes inconsistent / unexpected interpretations where a user
sends a command, then writes a novel on the same line some words of
which happen to *also* be commands, leading to merge states they did
not expect. They should now be told to fuck off.
Priority Restructuring
----------------------
The numerical priority system was pretty messy in that it confused
"staging priority" (in ways which were not entirely straightforward)
with overrides to other concerns.
This has now being split along all the axis, with separate command
subsets for:
- staging prioritisation, now separated between `default`, `priority`,
and `alone`,
- `default` means PRs are picked by an unspecified order when
creating a staging, if nothing better is available
- `priority` means PRs are picked first when staging, however if
`priority` PRs don't fill the staging the rest will be filled with
`default`, this mode did not previously exist
- `alone` means the PRs are picked first, before splits, and only
`alone` PRs can be part of the staging (which usually matches the
modename)
- `skipchecks` overrides both statuses and approval checks, for the
batch, something previously implied in `p=0`, but now
independent. Setting `skipchecks` basically makes the entire batch
`ready`.
For consistency this also sets the reviewer implicitly: since
skipchecks overrides both statuses *and approval*, whoever enables
this mode is essentially the reviewer.
- `cancel` cancels any ongoing staging when the marked PR becomes
ready again, previously this was also implied (in a more restricted
form) by setting `p=0`
FWBot removal
=============
While the "forwardport bot" still exists as an API level (to segregate
access rights between tokens) it has been removed as an interaction
point, as part of the modules merge plan. As a result,
fwbot stops responding
----------------------
Feedback messages are now always sent by the mergebot, the
forward-porting bot should not send any message or notification
anymore.
commands moved to the merge bot
-------------------------------
- `ignore`/`up to` simply changes bot
- `close` as well
- `skipci` is now a choice / flag of an `fw` command, which denotes
the forward-port policy,
- `fw=default` is the old `ci` and resets the policy to default,
that is wait for the PR to be merged to create forward ports, and
for the required statuses on each forward port to be received
before creating the next
- `fw=skipci` is the old `skipci`, it waits for the merge of the
base PR but then creates all the forward ports immediately (unless
it gets a conflict)
- `fw=skipmerge` immediately creates all the forward ports, without
even waiting for the PR to be merged
This is a completely new mode, and may be rather broken as until
now the 'bot has always assumed the source PR had been merged.
approval rework
---------------
Because of the previous section, there is no distinguishing feature
between `mergebot r+` = "merge this PR" and `forwardbot r+` = "merge
this PR and all its parent with different access rights".
As a result, the two have been merged under a single `mergebot r+`
with heuristics attempting to provide the best experience:
- if approving a non-forward port, the behavior does not change
- else, with review rights on the source, all ancestors are approved
- else, as author of the original, approves all ancestors which descend
from a merged PR
- else, approves all ancestors up to and including the oldest ancestor
to which we have review rights
Most notably, the source's author is not delegated on the source or
any of its descendants anymore. This might need to be revisited if it
provides too restrictive.
For the very specialized need of approving a forward-port *and none of
its ancestors*, `review=` can now take a comma (`,`) separated list of
pull request numbers (github numbers, not mergebot ids).
Computed State
==============
The `state` field of pull requests is now computed. Hopefully this
makes the status more consistent and predictable in the long run, and
importantly makes status management more reliable (because reference
datum get updated naturally flowing to the state).
For now however it makes things more complicated as some of the states
have to be separately signaled or updated:
- `closed` and `error` are now separate flags
- `merge_date` is pulled down from forwardport and becomes the
transition signal for ready -> merged
- `reviewed_by` becomes the transition signal for approval (might be a
good idea to rename it...)
- `status` is computed from the head's statuses and overrides, and
*that* becomes the validation state
Ideally, batch-level flags like `skipchecks` should be on, well, the
batch, and `state` should have a dependency on the batch. However
currently the batch is not a durable / permanent member of the system,
so it's a PR-level flag and a messy pile.
On notable change is that *forcing* the state to `ready` now does that
but also sets the reviewer, `skipchecks`, and overrides to ensure the
API-mediated readying does not get rolled back by e.g. the runbot
sending a status.
This is useful for a few types of automated / programmatic PRs
e.g. translation exports, where we set the state programmatically to
limit noise.
recursive dependency hack
-------------------------
Given a sequence of PRs with an override of the source, if one of the
PRs is updated its descendants should not have the override
anymore. However if the updated PR gets overridden, its descendants
should have *that* override.
This requires some unholy manipulations via an override of `modified`,
as the ORM supports recursive fields but not recursive
dependencies (on a different field).
unconditional followup scheduling
---------------------------------
Previously scheduling forward-port followup was contigent on the FW
policy, but it's not actually correct if the new PR is *immediately*
validated (which can happen now that the field is computed, if there
are no required statuses *or* all of the required statuses are
overridden by an ancestor) as nothing will trigger the state change
and thus scheduling of the fp followup.
The followup function checks all the properties of the batch to port,
so this should not result on incorrect ports. Although it's a bit more
expensive, and will lead to more spam.
Previously this would not happen because on creation of a PR the
validation task (commit -> PR) would still have to execute.
Misc Changes
============
- If a PR is marked as overriding / canceling stagings, it now does
so on retry not just when setting initially.
This was not handled at all previously, so a PR in P0 going into
error due to e.g. a non-deterministic bug would be retried and still
p=0, but a current staging would not get cancelled. Same when a PR
in p=0 goes into error because something was failed, then is updated
with a fix.
- Add tracking to a bunch of relevant PR fields.
Post-mortem analysis currently generally requires going through the
text logs to see what happened, which is annoying.
There is a nondeterminism / inconsistency in the tracking which
sometimes leads the admin user to trigger tracking before the bot
does, leading to the staging tracking being attributed to them
during tests, shove under the carpet by ignoring the user to whom
that tracking is attributed.
When multiple users update tracked fields in the same transaction
all the changes are attributed to the first one having triggered
tracking (?), I couldn't find why the admin sometimes takes over.
- added and leveraged support for enum-backed selection fields
- moved variuous fields from forwardport to runbot_merge
- fix a migration which had never worked and which never run (because
I forgot to bump the version on the module)
- remove some unnecessary intermediate de/serialisation
fixes #673, fixes #309, fixes #792, fixes #846 (probably)
2023-10-31 13:42:07 +07:00
|
|
|
assert pr.comments[-1]['body'] == f"""\
|
|
|
|
@{users['user']} we apparently missed updates to this PR and tried to stage it in a state \
|
2023-01-20 21:16:37 +07:00
|
|
|
which might not have been approved.
|
|
|
|
|
2023-02-14 19:38:37 +07:00
|
|
|
The properties Head, Target, Message were not correctly synchronized and have been updated.
|
|
|
|
|
|
|
|
<details><summary>differences</summary>
|
|
|
|
|
|
|
|
```diff
|
|
|
|
Head:
|
[CHG] *: rewrite commands set, rework status management
This commit revisits the commands set in order to make it more
regular, and limit inconsistent command-sets, although it includes
pseudo-command aliases for common tasks now removed from the core set.
Hard Errors
===========
The previous iteration of the commands set would ignore any
non-command term in a command line. This has been changed to hard
error (and ignoring the entire thing) if any command is unknown or
invalid.
This fixes inconsistent / unexpected interpretations where a user
sends a command, then writes a novel on the same line some words of
which happen to *also* be commands, leading to merge states they did
not expect. They should now be told to fuck off.
Priority Restructuring
----------------------
The numerical priority system was pretty messy in that it confused
"staging priority" (in ways which were not entirely straightforward)
with overrides to other concerns.
This has now being split along all the axis, with separate command
subsets for:
- staging prioritisation, now separated between `default`, `priority`,
and `alone`,
- `default` means PRs are picked by an unspecified order when
creating a staging, if nothing better is available
- `priority` means PRs are picked first when staging, however if
`priority` PRs don't fill the staging the rest will be filled with
`default`, this mode did not previously exist
- `alone` means the PRs are picked first, before splits, and only
`alone` PRs can be part of the staging (which usually matches the
modename)
- `skipchecks` overrides both statuses and approval checks, for the
batch, something previously implied in `p=0`, but now
independent. Setting `skipchecks` basically makes the entire batch
`ready`.
For consistency this also sets the reviewer implicitly: since
skipchecks overrides both statuses *and approval*, whoever enables
this mode is essentially the reviewer.
- `cancel` cancels any ongoing staging when the marked PR becomes
ready again, previously this was also implied (in a more restricted
form) by setting `p=0`
FWBot removal
=============
While the "forwardport bot" still exists as an API level (to segregate
access rights between tokens) it has been removed as an interaction
point, as part of the modules merge plan. As a result,
fwbot stops responding
----------------------
Feedback messages are now always sent by the mergebot, the
forward-porting bot should not send any message or notification
anymore.
commands moved to the merge bot
-------------------------------
- `ignore`/`up to` simply changes bot
- `close` as well
- `skipci` is now a choice / flag of an `fw` command, which denotes
the forward-port policy,
- `fw=default` is the old `ci` and resets the policy to default,
that is wait for the PR to be merged to create forward ports, and
for the required statuses on each forward port to be received
before creating the next
- `fw=skipci` is the old `skipci`, it waits for the merge of the
base PR but then creates all the forward ports immediately (unless
it gets a conflict)
- `fw=skipmerge` immediately creates all the forward ports, without
even waiting for the PR to be merged
This is a completely new mode, and may be rather broken as until
now the 'bot has always assumed the source PR had been merged.
approval rework
---------------
Because of the previous section, there is no distinguishing feature
between `mergebot r+` = "merge this PR" and `forwardbot r+` = "merge
this PR and all its parent with different access rights".
As a result, the two have been merged under a single `mergebot r+`
with heuristics attempting to provide the best experience:
- if approving a non-forward port, the behavior does not change
- else, with review rights on the source, all ancestors are approved
- else, as author of the original, approves all ancestors which descend
from a merged PR
- else, approves all ancestors up to and including the oldest ancestor
to which we have review rights
Most notably, the source's author is not delegated on the source or
any of its descendants anymore. This might need to be revisited if it
provides too restrictive.
For the very specialized need of approving a forward-port *and none of
its ancestors*, `review=` can now take a comma (`,`) separated list of
pull request numbers (github numbers, not mergebot ids).
Computed State
==============
The `state` field of pull requests is now computed. Hopefully this
makes the status more consistent and predictable in the long run, and
importantly makes status management more reliable (because reference
datum get updated naturally flowing to the state).
For now however it makes things more complicated as some of the states
have to be separately signaled or updated:
- `closed` and `error` are now separate flags
- `merge_date` is pulled down from forwardport and becomes the
transition signal for ready -> merged
- `reviewed_by` becomes the transition signal for approval (might be a
good idea to rename it...)
- `status` is computed from the head's statuses and overrides, and
*that* becomes the validation state
Ideally, batch-level flags like `skipchecks` should be on, well, the
batch, and `state` should have a dependency on the batch. However
currently the batch is not a durable / permanent member of the system,
so it's a PR-level flag and a messy pile.
On notable change is that *forcing* the state to `ready` now does that
but also sets the reviewer, `skipchecks`, and overrides to ensure the
API-mediated readying does not get rolled back by e.g. the runbot
sending a status.
This is useful for a few types of automated / programmatic PRs
e.g. translation exports, where we set the state programmatically to
limit noise.
recursive dependency hack
-------------------------
Given a sequence of PRs with an override of the source, if one of the
PRs is updated its descendants should not have the override
anymore. However if the updated PR gets overridden, its descendants
should have *that* override.
This requires some unholy manipulations via an override of `modified`,
as the ORM supports recursive fields but not recursive
dependencies (on a different field).
unconditional followup scheduling
---------------------------------
Previously scheduling forward-port followup was contigent on the FW
policy, but it's not actually correct if the new PR is *immediately*
validated (which can happen now that the field is computed, if there
are no required statuses *or* all of the required statuses are
overridden by an ancestor) as nothing will trigger the state change
and thus scheduling of the fp followup.
The followup function checks all the properties of the batch to port,
so this should not result on incorrect ports. Although it's a bit more
expensive, and will lead to more spam.
Previously this would not happen because on creation of a PR the
validation task (commit -> PR) would still have to execute.
Misc Changes
============
- If a PR is marked as overriding / canceling stagings, it now does
so on retry not just when setting initially.
This was not handled at all previously, so a PR in P0 going into
error due to e.g. a non-deterministic bug would be retried and still
p=0, but a current staging would not get cancelled. Same when a PR
in p=0 goes into error because something was failed, then is updated
with a fix.
- Add tracking to a bunch of relevant PR fields.
Post-mortem analysis currently generally requires going through the
text logs to see what happened, which is annoying.
There is a nondeterminism / inconsistency in the tracking which
sometimes leads the admin user to trigger tracking before the bot
does, leading to the staging tracking being attributed to them
during tests, shove under the carpet by ignoring the user to whom
that tracking is attributed.
When multiple users update tracked fields in the same transaction
all the changes are attributed to the first one having triggered
tracking (?), I couldn't find why the admin sometimes takes over.
- added and leveraged support for enum-backed selection fields
- moved variuous fields from forwardport to runbot_merge
- fix a migration which had never worked and which never run (because
I forgot to bump the version on the module)
- remove some unnecessary intermediate de/serialisation
fixes #673, fixes #309, fixes #792, fixes #846 (probably)
2023-10-31 13:42:07 +07:00
|
|
|
- {c}
|
|
|
|
+ {c2}
|
2023-02-14 19:38:37 +07:00
|
|
|
|
|
|
|
Target branch:
|
|
|
|
- somethingelse
|
|
|
|
+ master
|
|
|
|
|
|
|
|
Message:
|
|
|
|
- Something else
|
|
|
|
+ title
|
|
|
|
|
|
|
|
+ body
|
|
|
|
+
|
|
|
|
```
|
|
|
|
</details>
|
2023-01-20 21:16:37 +07:00
|
|
|
|
|
|
|
Note that we are unable to check the properties Merge Method, Overrides, Draft.
|
2019-11-20 20:57:40 +07:00
|
|
|
|
2023-01-20 21:16:37 +07:00
|
|
|
Please check and re-approve.
|
[CHG] *: rewrite commands set, rework status management
This commit revisits the commands set in order to make it more
regular, and limit inconsistent command-sets, although it includes
pseudo-command aliases for common tasks now removed from the core set.
Hard Errors
===========
The previous iteration of the commands set would ignore any
non-command term in a command line. This has been changed to hard
error (and ignoring the entire thing) if any command is unknown or
invalid.
This fixes inconsistent / unexpected interpretations where a user
sends a command, then writes a novel on the same line some words of
which happen to *also* be commands, leading to merge states they did
not expect. They should now be told to fuck off.
Priority Restructuring
----------------------
The numerical priority system was pretty messy in that it confused
"staging priority" (in ways which were not entirely straightforward)
with overrides to other concerns.
This has now being split along all the axis, with separate command
subsets for:
- staging prioritisation, now separated between `default`, `priority`,
and `alone`,
- `default` means PRs are picked by an unspecified order when
creating a staging, if nothing better is available
- `priority` means PRs are picked first when staging, however if
`priority` PRs don't fill the staging the rest will be filled with
`default`, this mode did not previously exist
- `alone` means the PRs are picked first, before splits, and only
`alone` PRs can be part of the staging (which usually matches the
modename)
- `skipchecks` overrides both statuses and approval checks, for the
batch, something previously implied in `p=0`, but now
independent. Setting `skipchecks` basically makes the entire batch
`ready`.
For consistency this also sets the reviewer implicitly: since
skipchecks overrides both statuses *and approval*, whoever enables
this mode is essentially the reviewer.
- `cancel` cancels any ongoing staging when the marked PR becomes
ready again, previously this was also implied (in a more restricted
form) by setting `p=0`
FWBot removal
=============
While the "forwardport bot" still exists as an API level (to segregate
access rights between tokens) it has been removed as an interaction
point, as part of the modules merge plan. As a result,
fwbot stops responding
----------------------
Feedback messages are now always sent by the mergebot, the
forward-porting bot should not send any message or notification
anymore.
commands moved to the merge bot
-------------------------------
- `ignore`/`up to` simply changes bot
- `close` as well
- `skipci` is now a choice / flag of an `fw` command, which denotes
the forward-port policy,
- `fw=default` is the old `ci` and resets the policy to default,
that is wait for the PR to be merged to create forward ports, and
for the required statuses on each forward port to be received
before creating the next
- `fw=skipci` is the old `skipci`, it waits for the merge of the
base PR but then creates all the forward ports immediately (unless
it gets a conflict)
- `fw=skipmerge` immediately creates all the forward ports, without
even waiting for the PR to be merged
This is a completely new mode, and may be rather broken as until
now the 'bot has always assumed the source PR had been merged.
approval rework
---------------
Because of the previous section, there is no distinguishing feature
between `mergebot r+` = "merge this PR" and `forwardbot r+` = "merge
this PR and all its parent with different access rights".
As a result, the two have been merged under a single `mergebot r+`
with heuristics attempting to provide the best experience:
- if approving a non-forward port, the behavior does not change
- else, with review rights on the source, all ancestors are approved
- else, as author of the original, approves all ancestors which descend
from a merged PR
- else, approves all ancestors up to and including the oldest ancestor
to which we have review rights
Most notably, the source's author is not delegated on the source or
any of its descendants anymore. This might need to be revisited if it
provides too restrictive.
For the very specialized need of approving a forward-port *and none of
its ancestors*, `review=` can now take a comma (`,`) separated list of
pull request numbers (github numbers, not mergebot ids).
Computed State
==============
The `state` field of pull requests is now computed. Hopefully this
makes the status more consistent and predictable in the long run, and
importantly makes status management more reliable (because reference
datum get updated naturally flowing to the state).
For now however it makes things more complicated as some of the states
have to be separately signaled or updated:
- `closed` and `error` are now separate flags
- `merge_date` is pulled down from forwardport and becomes the
transition signal for ready -> merged
- `reviewed_by` becomes the transition signal for approval (might be a
good idea to rename it...)
- `status` is computed from the head's statuses and overrides, and
*that* becomes the validation state
Ideally, batch-level flags like `skipchecks` should be on, well, the
batch, and `state` should have a dependency on the batch. However
currently the batch is not a durable / permanent member of the system,
so it's a PR-level flag and a messy pile.
On notable change is that *forcing* the state to `ready` now does that
but also sets the reviewer, `skipchecks`, and overrides to ensure the
API-mediated readying does not get rolled back by e.g. the runbot
sending a status.
This is useful for a few types of automated / programmatic PRs
e.g. translation exports, where we set the state programmatically to
limit noise.
recursive dependency hack
-------------------------
Given a sequence of PRs with an override of the source, if one of the
PRs is updated its descendants should not have the override
anymore. However if the updated PR gets overridden, its descendants
should have *that* override.
This requires some unholy manipulations via an override of `modified`,
as the ORM supports recursive fields but not recursive
dependencies (on a different field).
unconditional followup scheduling
---------------------------------
Previously scheduling forward-port followup was contigent on the FW
policy, but it's not actually correct if the new PR is *immediately*
validated (which can happen now that the field is computed, if there
are no required statuses *or* all of the required statuses are
overridden by an ancestor) as nothing will trigger the state change
and thus scheduling of the fp followup.
The followup function checks all the properties of the batch to port,
so this should not result on incorrect ports. Although it's a bit more
expensive, and will lead to more spam.
Previously this would not happen because on creation of a PR the
validation task (commit -> PR) would still have to execute.
Misc Changes
============
- If a PR is marked as overriding / canceling stagings, it now does
so on retry not just when setting initially.
This was not handled at all previously, so a PR in P0 going into
error due to e.g. a non-deterministic bug would be retried and still
p=0, but a current staging would not get cancelled. Same when a PR
in p=0 goes into error because something was failed, then is updated
with a fix.
- Add tracking to a bunch of relevant PR fields.
Post-mortem analysis currently generally requires going through the
text logs to see what happened, which is annoying.
There is a nondeterminism / inconsistency in the tracking which
sometimes leads the admin user to trigger tracking before the bot
does, leading to the staging tracking being attributed to them
during tests, shove under the carpet by ignoring the user to whom
that tracking is attributed.
When multiple users update tracked fields in the same transaction
all the changes are attributed to the first one having triggered
tracking (?), I couldn't find why the admin sometimes takes over.
- added and leveraged support for enum-backed selection fields
- moved variuous fields from forwardport to runbot_merge
- fix a migration which had never worked and which never run (because
I forgot to bump the version on the module)
- remove some unnecessary intermediate de/serialisation
fixes #673, fixes #309, fixes #792, fixes #846 (probably)
2023-10-31 13:42:07 +07:00
|
|
|
"""
|
2023-02-14 19:38:37 +07:00
|
|
|
|
|
|
|
# if the head commit doesn't change, that part should still be valid
|
|
|
|
with repo:
|
|
|
|
pr.post_comment('hansen r+', config['role_reviewer']['token'])
|
|
|
|
assert pr_id.state == 'ready'
|
|
|
|
pr_id.write({'message': 'wrong'})
|
|
|
|
env.run_crons()
|
|
|
|
|
|
|
|
assert pr_id.message == 'title\n\nbody'
|
|
|
|
assert pr_id.state == 'validated'
|
[CHG] *: rewrite commands set, rework status management
This commit revisits the commands set in order to make it more
regular, and limit inconsistent command-sets, although it includes
pseudo-command aliases for common tasks now removed from the core set.
Hard Errors
===========
The previous iteration of the commands set would ignore any
non-command term in a command line. This has been changed to hard
error (and ignoring the entire thing) if any command is unknown or
invalid.
This fixes inconsistent / unexpected interpretations where a user
sends a command, then writes a novel on the same line some words of
which happen to *also* be commands, leading to merge states they did
not expect. They should now be told to fuck off.
Priority Restructuring
----------------------
The numerical priority system was pretty messy in that it confused
"staging priority" (in ways which were not entirely straightforward)
with overrides to other concerns.
This has now being split along all the axis, with separate command
subsets for:
- staging prioritisation, now separated between `default`, `priority`,
and `alone`,
- `default` means PRs are picked by an unspecified order when
creating a staging, if nothing better is available
- `priority` means PRs are picked first when staging, however if
`priority` PRs don't fill the staging the rest will be filled with
`default`, this mode did not previously exist
- `alone` means the PRs are picked first, before splits, and only
`alone` PRs can be part of the staging (which usually matches the
modename)
- `skipchecks` overrides both statuses and approval checks, for the
batch, something previously implied in `p=0`, but now
independent. Setting `skipchecks` basically makes the entire batch
`ready`.
For consistency this also sets the reviewer implicitly: since
skipchecks overrides both statuses *and approval*, whoever enables
this mode is essentially the reviewer.
- `cancel` cancels any ongoing staging when the marked PR becomes
ready again, previously this was also implied (in a more restricted
form) by setting `p=0`
FWBot removal
=============
While the "forwardport bot" still exists as an API level (to segregate
access rights between tokens) it has been removed as an interaction
point, as part of the modules merge plan. As a result,
fwbot stops responding
----------------------
Feedback messages are now always sent by the mergebot, the
forward-porting bot should not send any message or notification
anymore.
commands moved to the merge bot
-------------------------------
- `ignore`/`up to` simply changes bot
- `close` as well
- `skipci` is now a choice / flag of an `fw` command, which denotes
the forward-port policy,
- `fw=default` is the old `ci` and resets the policy to default,
that is wait for the PR to be merged to create forward ports, and
for the required statuses on each forward port to be received
before creating the next
- `fw=skipci` is the old `skipci`, it waits for the merge of the
base PR but then creates all the forward ports immediately (unless
it gets a conflict)
- `fw=skipmerge` immediately creates all the forward ports, without
even waiting for the PR to be merged
This is a completely new mode, and may be rather broken as until
now the 'bot has always assumed the source PR had been merged.
approval rework
---------------
Because of the previous section, there is no distinguishing feature
between `mergebot r+` = "merge this PR" and `forwardbot r+` = "merge
this PR and all its parent with different access rights".
As a result, the two have been merged under a single `mergebot r+`
with heuristics attempting to provide the best experience:
- if approving a non-forward port, the behavior does not change
- else, with review rights on the source, all ancestors are approved
- else, as author of the original, approves all ancestors which descend
from a merged PR
- else, approves all ancestors up to and including the oldest ancestor
to which we have review rights
Most notably, the source's author is not delegated on the source or
any of its descendants anymore. This might need to be revisited if it
provides too restrictive.
For the very specialized need of approving a forward-port *and none of
its ancestors*, `review=` can now take a comma (`,`) separated list of
pull request numbers (github numbers, not mergebot ids).
Computed State
==============
The `state` field of pull requests is now computed. Hopefully this
makes the status more consistent and predictable in the long run, and
importantly makes status management more reliable (because reference
datum get updated naturally flowing to the state).
For now however it makes things more complicated as some of the states
have to be separately signaled or updated:
- `closed` and `error` are now separate flags
- `merge_date` is pulled down from forwardport and becomes the
transition signal for ready -> merged
- `reviewed_by` becomes the transition signal for approval (might be a
good idea to rename it...)
- `status` is computed from the head's statuses and overrides, and
*that* becomes the validation state
Ideally, batch-level flags like `skipchecks` should be on, well, the
batch, and `state` should have a dependency on the batch. However
currently the batch is not a durable / permanent member of the system,
so it's a PR-level flag and a messy pile.
On notable change is that *forcing* the state to `ready` now does that
but also sets the reviewer, `skipchecks`, and overrides to ensure the
API-mediated readying does not get rolled back by e.g. the runbot
sending a status.
This is useful for a few types of automated / programmatic PRs
e.g. translation exports, where we set the state programmatically to
limit noise.
recursive dependency hack
-------------------------
Given a sequence of PRs with an override of the source, if one of the
PRs is updated its descendants should not have the override
anymore. However if the updated PR gets overridden, its descendants
should have *that* override.
This requires some unholy manipulations via an override of `modified`,
as the ORM supports recursive fields but not recursive
dependencies (on a different field).
unconditional followup scheduling
---------------------------------
Previously scheduling forward-port followup was contigent on the FW
policy, but it's not actually correct if the new PR is *immediately*
validated (which can happen now that the field is computed, if there
are no required statuses *or* all of the required statuses are
overridden by an ancestor) as nothing will trigger the state change
and thus scheduling of the fp followup.
The followup function checks all the properties of the batch to port,
so this should not result on incorrect ports. Although it's a bit more
expensive, and will lead to more spam.
Previously this would not happen because on creation of a PR the
validation task (commit -> PR) would still have to execute.
Misc Changes
============
- If a PR is marked as overriding / canceling stagings, it now does
so on retry not just when setting initially.
This was not handled at all previously, so a PR in P0 going into
error due to e.g. a non-deterministic bug would be retried and still
p=0, but a current staging would not get cancelled. Same when a PR
in p=0 goes into error because something was failed, then is updated
with a fix.
- Add tracking to a bunch of relevant PR fields.
Post-mortem analysis currently generally requires going through the
text logs to see what happened, which is annoying.
There is a nondeterminism / inconsistency in the tracking which
sometimes leads the admin user to trigger tracking before the bot
does, leading to the staging tracking being attributed to them
during tests, shove under the carpet by ignoring the user to whom
that tracking is attributed.
When multiple users update tracked fields in the same transaction
all the changes are attributed to the first one having triggered
tracking (?), I couldn't find why the admin sometimes takes over.
- added and leveraged support for enum-backed selection fields
- moved variuous fields from forwardport to runbot_merge
- fix a migration which had never worked and which never run (because
I forgot to bump the version on the module)
- remove some unnecessary intermediate de/serialisation
fixes #673, fixes #309, fixes #792, fixes #846 (probably)
2023-10-31 13:42:07 +07:00
|
|
|
assert pr.comments[-1]['body'] == f"""\
|
|
|
|
@{users['user']} we apparently missed updates to this PR and tried to stage it in a state \
|
2023-02-14 19:38:37 +07:00
|
|
|
which might not have been approved.
|
|
|
|
|
|
|
|
The properties Message were not correctly synchronized and have been updated.
|
|
|
|
|
|
|
|
<details><summary>differences</summary>
|
|
|
|
|
|
|
|
```diff
|
|
|
|
Message:
|
|
|
|
- wrong
|
|
|
|
+ title
|
|
|
|
|
|
|
|
+ body
|
|
|
|
+
|
|
|
|
```
|
|
|
|
</details>
|
|
|
|
|
|
|
|
Note that we are unable to check the properties Merge Method, Overrides, Draft.
|
|
|
|
|
|
|
|
Please check and re-approve.
|
[CHG] *: rewrite commands set, rework status management
This commit revisits the commands set in order to make it more
regular, and limit inconsistent command-sets, although it includes
pseudo-command aliases for common tasks now removed from the core set.
Hard Errors
===========
The previous iteration of the commands set would ignore any
non-command term in a command line. This has been changed to hard
error (and ignoring the entire thing) if any command is unknown or
invalid.
This fixes inconsistent / unexpected interpretations where a user
sends a command, then writes a novel on the same line some words of
which happen to *also* be commands, leading to merge states they did
not expect. They should now be told to fuck off.
Priority Restructuring
----------------------
The numerical priority system was pretty messy in that it confused
"staging priority" (in ways which were not entirely straightforward)
with overrides to other concerns.
This has now being split along all the axis, with separate command
subsets for:
- staging prioritisation, now separated between `default`, `priority`,
and `alone`,
- `default` means PRs are picked by an unspecified order when
creating a staging, if nothing better is available
- `priority` means PRs are picked first when staging, however if
`priority` PRs don't fill the staging the rest will be filled with
`default`, this mode did not previously exist
- `alone` means the PRs are picked first, before splits, and only
`alone` PRs can be part of the staging (which usually matches the
modename)
- `skipchecks` overrides both statuses and approval checks, for the
batch, something previously implied in `p=0`, but now
independent. Setting `skipchecks` basically makes the entire batch
`ready`.
For consistency this also sets the reviewer implicitly: since
skipchecks overrides both statuses *and approval*, whoever enables
this mode is essentially the reviewer.
- `cancel` cancels any ongoing staging when the marked PR becomes
ready again, previously this was also implied (in a more restricted
form) by setting `p=0`
FWBot removal
=============
While the "forwardport bot" still exists as an API level (to segregate
access rights between tokens) it has been removed as an interaction
point, as part of the modules merge plan. As a result,
fwbot stops responding
----------------------
Feedback messages are now always sent by the mergebot, the
forward-porting bot should not send any message or notification
anymore.
commands moved to the merge bot
-------------------------------
- `ignore`/`up to` simply changes bot
- `close` as well
- `skipci` is now a choice / flag of an `fw` command, which denotes
the forward-port policy,
- `fw=default` is the old `ci` and resets the policy to default,
that is wait for the PR to be merged to create forward ports, and
for the required statuses on each forward port to be received
before creating the next
- `fw=skipci` is the old `skipci`, it waits for the merge of the
base PR but then creates all the forward ports immediately (unless
it gets a conflict)
- `fw=skipmerge` immediately creates all the forward ports, without
even waiting for the PR to be merged
This is a completely new mode, and may be rather broken as until
now the 'bot has always assumed the source PR had been merged.
approval rework
---------------
Because of the previous section, there is no distinguishing feature
between `mergebot r+` = "merge this PR" and `forwardbot r+` = "merge
this PR and all its parent with different access rights".
As a result, the two have been merged under a single `mergebot r+`
with heuristics attempting to provide the best experience:
- if approving a non-forward port, the behavior does not change
- else, with review rights on the source, all ancestors are approved
- else, as author of the original, approves all ancestors which descend
from a merged PR
- else, approves all ancestors up to and including the oldest ancestor
to which we have review rights
Most notably, the source's author is not delegated on the source or
any of its descendants anymore. This might need to be revisited if it
provides too restrictive.
For the very specialized need of approving a forward-port *and none of
its ancestors*, `review=` can now take a comma (`,`) separated list of
pull request numbers (github numbers, not mergebot ids).
Computed State
==============
The `state` field of pull requests is now computed. Hopefully this
makes the status more consistent and predictable in the long run, and
importantly makes status management more reliable (because reference
datum get updated naturally flowing to the state).
For now however it makes things more complicated as some of the states
have to be separately signaled or updated:
- `closed` and `error` are now separate flags
- `merge_date` is pulled down from forwardport and becomes the
transition signal for ready -> merged
- `reviewed_by` becomes the transition signal for approval (might be a
good idea to rename it...)
- `status` is computed from the head's statuses and overrides, and
*that* becomes the validation state
Ideally, batch-level flags like `skipchecks` should be on, well, the
batch, and `state` should have a dependency on the batch. However
currently the batch is not a durable / permanent member of the system,
so it's a PR-level flag and a messy pile.
On notable change is that *forcing* the state to `ready` now does that
but also sets the reviewer, `skipchecks`, and overrides to ensure the
API-mediated readying does not get rolled back by e.g. the runbot
sending a status.
This is useful for a few types of automated / programmatic PRs
e.g. translation exports, where we set the state programmatically to
limit noise.
recursive dependency hack
-------------------------
Given a sequence of PRs with an override of the source, if one of the
PRs is updated its descendants should not have the override
anymore. However if the updated PR gets overridden, its descendants
should have *that* override.
This requires some unholy manipulations via an override of `modified`,
as the ORM supports recursive fields but not recursive
dependencies (on a different field).
unconditional followup scheduling
---------------------------------
Previously scheduling forward-port followup was contigent on the FW
policy, but it's not actually correct if the new PR is *immediately*
validated (which can happen now that the field is computed, if there
are no required statuses *or* all of the required statuses are
overridden by an ancestor) as nothing will trigger the state change
and thus scheduling of the fp followup.
The followup function checks all the properties of the batch to port,
so this should not result on incorrect ports. Although it's a bit more
expensive, and will lead to more spam.
Previously this would not happen because on creation of a PR the
validation task (commit -> PR) would still have to execute.
Misc Changes
============
- If a PR is marked as overriding / canceling stagings, it now does
so on retry not just when setting initially.
This was not handled at all previously, so a PR in P0 going into
error due to e.g. a non-deterministic bug would be retried and still
p=0, but a current staging would not get cancelled. Same when a PR
in p=0 goes into error because something was failed, then is updated
with a fix.
- Add tracking to a bunch of relevant PR fields.
Post-mortem analysis currently generally requires going through the
text logs to see what happened, which is annoying.
There is a nondeterminism / inconsistency in the tracking which
sometimes leads the admin user to trigger tracking before the bot
does, leading to the staging tracking being attributed to them
during tests, shove under the carpet by ignoring the user to whom
that tracking is attributed.
When multiple users update tracked fields in the same transaction
all the changes are attributed to the first one having triggered
tracking (?), I couldn't find why the admin sometimes takes over.
- added and leveraged support for enum-backed selection fields
- moved variuous fields from forwardport to runbot_merge
- fix a migration which had never worked and which never run (because
I forgot to bump the version on the module)
- remove some unnecessary intermediate de/serialisation
fixes #673, fixes #309, fixes #792, fixes #846 (probably)
2023-10-31 13:42:07 +07:00
|
|
|
"""
|
2023-01-20 21:16:37 +07:00
|
|
|
|
|
|
|
pr_id.write({
|
|
|
|
'head': c,
|
[CHG] *: rewrite commands set, rework status management
This commit revisits the commands set in order to make it more
regular, and limit inconsistent command-sets, although it includes
pseudo-command aliases for common tasks now removed from the core set.
Hard Errors
===========
The previous iteration of the commands set would ignore any
non-command term in a command line. This has been changed to hard
error (and ignoring the entire thing) if any command is unknown or
invalid.
This fixes inconsistent / unexpected interpretations where a user
sends a command, then writes a novel on the same line some words of
which happen to *also* be commands, leading to merge states they did
not expect. They should now be told to fuck off.
Priority Restructuring
----------------------
The numerical priority system was pretty messy in that it confused
"staging priority" (in ways which were not entirely straightforward)
with overrides to other concerns.
This has now being split along all the axis, with separate command
subsets for:
- staging prioritisation, now separated between `default`, `priority`,
and `alone`,
- `default` means PRs are picked by an unspecified order when
creating a staging, if nothing better is available
- `priority` means PRs are picked first when staging, however if
`priority` PRs don't fill the staging the rest will be filled with
`default`, this mode did not previously exist
- `alone` means the PRs are picked first, before splits, and only
`alone` PRs can be part of the staging (which usually matches the
modename)
- `skipchecks` overrides both statuses and approval checks, for the
batch, something previously implied in `p=0`, but now
independent. Setting `skipchecks` basically makes the entire batch
`ready`.
For consistency this also sets the reviewer implicitly: since
skipchecks overrides both statuses *and approval*, whoever enables
this mode is essentially the reviewer.
- `cancel` cancels any ongoing staging when the marked PR becomes
ready again, previously this was also implied (in a more restricted
form) by setting `p=0`
FWBot removal
=============
While the "forwardport bot" still exists as an API level (to segregate
access rights between tokens) it has been removed as an interaction
point, as part of the modules merge plan. As a result,
fwbot stops responding
----------------------
Feedback messages are now always sent by the mergebot, the
forward-porting bot should not send any message or notification
anymore.
commands moved to the merge bot
-------------------------------
- `ignore`/`up to` simply changes bot
- `close` as well
- `skipci` is now a choice / flag of an `fw` command, which denotes
the forward-port policy,
- `fw=default` is the old `ci` and resets the policy to default,
that is wait for the PR to be merged to create forward ports, and
for the required statuses on each forward port to be received
before creating the next
- `fw=skipci` is the old `skipci`, it waits for the merge of the
base PR but then creates all the forward ports immediately (unless
it gets a conflict)
- `fw=skipmerge` immediately creates all the forward ports, without
even waiting for the PR to be merged
This is a completely new mode, and may be rather broken as until
now the 'bot has always assumed the source PR had been merged.
approval rework
---------------
Because of the previous section, there is no distinguishing feature
between `mergebot r+` = "merge this PR" and `forwardbot r+` = "merge
this PR and all its parent with different access rights".
As a result, the two have been merged under a single `mergebot r+`
with heuristics attempting to provide the best experience:
- if approving a non-forward port, the behavior does not change
- else, with review rights on the source, all ancestors are approved
- else, as author of the original, approves all ancestors which descend
from a merged PR
- else, approves all ancestors up to and including the oldest ancestor
to which we have review rights
Most notably, the source's author is not delegated on the source or
any of its descendants anymore. This might need to be revisited if it
provides too restrictive.
For the very specialized need of approving a forward-port *and none of
its ancestors*, `review=` can now take a comma (`,`) separated list of
pull request numbers (github numbers, not mergebot ids).
Computed State
==============
The `state` field of pull requests is now computed. Hopefully this
makes the status more consistent and predictable in the long run, and
importantly makes status management more reliable (because reference
datum get updated naturally flowing to the state).
For now however it makes things more complicated as some of the states
have to be separately signaled or updated:
- `closed` and `error` are now separate flags
- `merge_date` is pulled down from forwardport and becomes the
transition signal for ready -> merged
- `reviewed_by` becomes the transition signal for approval (might be a
good idea to rename it...)
- `status` is computed from the head's statuses and overrides, and
*that* becomes the validation state
Ideally, batch-level flags like `skipchecks` should be on, well, the
batch, and `state` should have a dependency on the batch. However
currently the batch is not a durable / permanent member of the system,
so it's a PR-level flag and a messy pile.
On notable change is that *forcing* the state to `ready` now does that
but also sets the reviewer, `skipchecks`, and overrides to ensure the
API-mediated readying does not get rolled back by e.g. the runbot
sending a status.
This is useful for a few types of automated / programmatic PRs
e.g. translation exports, where we set the state programmatically to
limit noise.
recursive dependency hack
-------------------------
Given a sequence of PRs with an override of the source, if one of the
PRs is updated its descendants should not have the override
anymore. However if the updated PR gets overridden, its descendants
should have *that* override.
This requires some unholy manipulations via an override of `modified`,
as the ORM supports recursive fields but not recursive
dependencies (on a different field).
unconditional followup scheduling
---------------------------------
Previously scheduling forward-port followup was contigent on the FW
policy, but it's not actually correct if the new PR is *immediately*
validated (which can happen now that the field is computed, if there
are no required statuses *or* all of the required statuses are
overridden by an ancestor) as nothing will trigger the state change
and thus scheduling of the fp followup.
The followup function checks all the properties of the batch to port,
so this should not result on incorrect ports. Although it's a bit more
expensive, and will lead to more spam.
Previously this would not happen because on creation of a PR the
validation task (commit -> PR) would still have to execute.
Misc Changes
============
- If a PR is marked as overriding / canceling stagings, it now does
so on retry not just when setting initially.
This was not handled at all previously, so a PR in P0 going into
error due to e.g. a non-deterministic bug would be retried and still
p=0, but a current staging would not get cancelled. Same when a PR
in p=0 goes into error because something was failed, then is updated
with a fix.
- Add tracking to a bunch of relevant PR fields.
Post-mortem analysis currently generally requires going through the
text logs to see what happened, which is annoying.
There is a nondeterminism / inconsistency in the tracking which
sometimes leads the admin user to trigger tracking before the bot
does, leading to the staging tracking being attributed to them
during tests, shove under the carpet by ignoring the user to whom
that tracking is attributed.
When multiple users update tracked fields in the same transaction
all the changes are attributed to the first one having triggered
tracking (?), I couldn't find why the admin sometimes takes over.
- added and leveraged support for enum-backed selection fields
- moved variuous fields from forwardport to runbot_merge
- fix a migration which had never worked and which never run (because
I forgot to bump the version on the module)
- remove some unnecessary intermediate de/serialisation
fixes #673, fixes #309, fixes #792, fixes #846 (probably)
2023-10-31 13:42:07 +07:00
|
|
|
'reviewed_by': old_reviewer.id,
|
2023-01-20 21:16:37 +07:00
|
|
|
'message': "Something else",
|
|
|
|
'target': other.id,
|
2023-06-08 15:03:26 +07:00
|
|
|
'draft': True,
|
2023-01-20 21:16:37 +07:00
|
|
|
})
|
2019-11-20 20:57:40 +07:00
|
|
|
with repo:
|
|
|
|
pr.post_comment('hansen check')
|
|
|
|
env.run_crons()
|
|
|
|
assert pr_id.state == 'validated'
|
|
|
|
assert pr_id.head == c2
|
2023-02-14 19:38:37 +07:00
|
|
|
assert pr_id.message == 'title\n\nbody' # the commit's message was used for the PR
|
2023-01-20 21:16:37 +07:00
|
|
|
assert pr_id.target.name == 'master'
|
2023-06-08 15:03:26 +07:00
|
|
|
assert not pr_id.draft
|
|
|
|
assert pr.comments[-1] == (
|
|
|
|
users['user'],
|
|
|
|
f"Updated target, squash, message. Updated {pr_id.display_name} to ready. Updated to {c2}."
|
|
|
|
)
|
2019-11-20 20:57:40 +07:00
|
|
|
|
2024-09-19 17:17:59 +07:00
|
|
|
@pytest.mark.defaultstatuses
|
|
|
|
def test_update_closed(self, env, repo, config):
|
2020-02-07 22:15:26 +07:00
|
|
|
with repo:
|
2024-09-19 17:17:59 +07:00
|
|
|
[c] = repo.make_commits("master", repo.Commit('first', tree={'m': 'm3'}), ref='heads/abranch')
|
|
|
|
pr = repo.make_pr(target='master', head=c)
|
|
|
|
pr.post_comment("hansen r+", config['role_reviewer']['token'])
|
2020-02-07 22:15:26 +07:00
|
|
|
env.run_crons()
|
2020-05-28 18:10:55 +07:00
|
|
|
|
2024-09-19 17:17:59 +07:00
|
|
|
pr_id = to_pr(env, pr)
|
|
|
|
assert pr_id.state == 'approved'
|
|
|
|
assert pr_id.head == c
|
|
|
|
assert pr_id.squash
|
|
|
|
assert pr_id.reviewed_by
|
2020-02-07 22:15:26 +07:00
|
|
|
|
|
|
|
with repo:
|
2024-09-19 17:17:59 +07:00
|
|
|
pr.close()
|
2020-02-07 22:15:26 +07:00
|
|
|
assert pr.state == 'closed'
|
|
|
|
assert pr.head == c
|
2024-09-19 17:17:59 +07:00
|
|
|
assert not pr_id.reviewed_by
|
|
|
|
assert pr_id.squash
|
2020-02-07 22:15:26 +07:00
|
|
|
|
|
|
|
with repo:
|
2024-09-19 17:17:59 +07:00
|
|
|
[c2] = repo.make_commits(c, Commit('xxx', tree={'m': 'm4'}))
|
|
|
|
repo.update_ref(pr.ref, c2)
|
|
|
|
repo.post_status(c2, "success")
|
2020-05-28 18:10:55 +07:00
|
|
|
|
2024-09-19 17:17:59 +07:00
|
|
|
assert pr_id.state == 'closed'
|
|
|
|
assert pr_id.head == c
|
|
|
|
assert not pr_id.reviewed_by
|
|
|
|
assert pr_id.squash
|
2020-02-07 22:15:26 +07:00
|
|
|
|
2024-09-19 17:17:59 +07:00
|
|
|
with repo:
|
|
|
|
pr.open()
|
|
|
|
assert pr_id.state == 'validated'
|
|
|
|
assert pr_id.head == c2
|
|
|
|
assert not pr_id.reviewed_by
|
|
|
|
assert not pr_id.squash
|
2020-02-07 22:15:26 +07:00
|
|
|
|
2024-10-07 13:07:59 +07:00
|
|
|
@pytest.mark.defaultstatuses
|
|
|
|
def test_update_incorrect_commits_count(self, port, env, project, repo, config, users):
|
|
|
|
"""This is not a great test but it aims to kinda sorta simulate the
|
|
|
|
behaviour when a user retargets and updates a PR at about the same time:
|
|
|
|
github can send the hooks in the wrong order, which leads to the correct
|
|
|
|
base and head but can lead to the wrong squash status.
|
2020-05-28 18:10:55 +07:00
|
|
|
"""
|
2024-10-07 13:07:59 +07:00
|
|
|
project.write({
|
|
|
|
'branch_ids': [(0, 0, {
|
|
|
|
'name': 'xxx',
|
|
|
|
})]
|
|
|
|
})
|
2020-05-28 18:10:55 +07:00
|
|
|
with repo:
|
2024-10-07 13:07:59 +07:00
|
|
|
[c] = repo.make_commits("master", Commit("c", tree={"m": "n"}), ref="heads/thing")
|
|
|
|
pr = repo.make_pr(target='master', head='thing')
|
2020-05-28 18:10:55 +07:00
|
|
|
|
2024-10-07 13:07:59 +07:00
|
|
|
pr_id = to_pr(env, pr)
|
|
|
|
pr_id.head = '0'*40
|
|
|
|
with requests.Session() as s:
|
|
|
|
r = s.post(
|
|
|
|
f"http://localhost:{port}/runbot_merge/hooks",
|
|
|
|
headers={
|
|
|
|
"X-Github-Event": "pull_request",
|
|
|
|
},
|
|
|
|
json={
|
|
|
|
'action': 'synchronize',
|
|
|
|
'sender': {
|
|
|
|
'login': users['user'],
|
|
|
|
},
|
|
|
|
'repository': {
|
|
|
|
'full_name': repo.name,
|
|
|
|
},
|
|
|
|
'pull_request': {
|
|
|
|
'number': pr.number,
|
|
|
|
'head': {'sha': c},
|
|
|
|
'title': "c",
|
|
|
|
'commits': 40123,
|
|
|
|
'base': {
|
|
|
|
'ref': 'xxx',
|
|
|
|
'repo': {
|
|
|
|
'full_name': repo.name,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
)
|
|
|
|
r.raise_for_status()
|
|
|
|
assert pr_id.head == c, "the head should have been updated"
|
|
|
|
assert not pr_id.squash, "the wrong count should be used"
|
2020-05-28 18:10:55 +07:00
|
|
|
|
2024-10-07 13:07:59 +07:00
|
|
|
with repo:
|
|
|
|
pr.post_comment("hansen r+", config['role_reviewer']['token'])
|
|
|
|
repo.post_status(c, 'success')
|
2024-01-16 21:03:45 +07:00
|
|
|
env.run_crons()
|
2024-10-07 13:07:59 +07:00
|
|
|
assert not pr_id.blocked
|
|
|
|
assert pr_id.message_ids[::-1].mapped(lambda m: (
|
|
|
|
((m.subject or '') + '\n\n' + m.body).strip(),
|
|
|
|
list(map(read_tracking_value, m.tracking_value_ids)),
|
|
|
|
)) == [
|
|
|
|
('<p>Pull Request created</p>', []),
|
|
|
|
('', [('head', c, '0'*40)]),
|
|
|
|
('', [('head', '0'*40, c), ('squash', 1, 0)]),
|
2024-11-20 18:40:15 +07:00
|
|
|
('', [('reviewed_by', '', 'Reviewer'), ('state', 'Opened', 'Approved')]),
|
2024-10-07 13:07:59 +07:00
|
|
|
(f'<p>statuses changed on {c}</p>', [('state', 'Approved', 'Ready')]),
|
|
|
|
]
|
|
|
|
assert pr_id.staging_id
|
|
|
|
with repo:
|
|
|
|
repo.post_status('staging.master', 'success')
|
|
|
|
env.run_crons()
|
|
|
|
assert pr_id.merge_date
|
2020-02-07 22:15:26 +07:00
|
|
|
|
2018-03-14 16:37:46 +07:00
|
|
|
class TestBatching(object):
|
2019-10-10 14:22:12 +07:00
|
|
|
def _pr(self, repo, prefix, trees, *, target='master', user, reviewer,
|
2018-03-27 18:33:04 +07:00
|
|
|
statuses=(('ci/runbot', 'success'), ('legal/cla', 'success'))
|
|
|
|
):
|
2018-03-14 16:37:46 +07:00
|
|
|
""" Helper creating a PR from a series of commits on a base
|
|
|
|
"""
|
2019-10-10 14:22:12 +07:00
|
|
|
*_, c = repo.make_commits(
|
|
|
|
'heads/{}'.format(target),
|
|
|
|
*(
|
|
|
|
repo.Commit('commit_{}_{:02}'.format(prefix, i), tree=t)
|
|
|
|
for i, t in enumerate(trees)
|
|
|
|
),
|
|
|
|
ref='heads/{}'.format(prefix)
|
|
|
|
)
|
|
|
|
pr = repo.make_pr(title='title {}'.format(prefix), body='body {}'.format(prefix),
|
|
|
|
target=target, head=prefix, token=user)
|
2018-03-27 18:33:04 +07:00
|
|
|
|
|
|
|
for context, result in statuses:
|
|
|
|
repo.post_status(c, result, context)
|
|
|
|
if reviewer:
|
2018-11-26 16:28:13 +07:00
|
|
|
pr.post_comment(
|
|
|
|
'hansen r+%s' % (' rebase-merge' if len(trees) > 1 else ''),
|
|
|
|
reviewer
|
|
|
|
)
|
2018-03-14 16:37:46 +07:00
|
|
|
return pr
|
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
def test_staging_batch(self, env, repo, users, config):
|
2018-03-14 16:37:46 +07:00
|
|
|
""" If multiple PRs are ready for the same target at the same point,
|
|
|
|
they should be staged together
|
|
|
|
"""
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
m = repo.make_commit(None, 'initial', None, tree={'a': 'some content'})
|
|
|
|
repo.make_ref('heads/master', m)
|
2018-03-14 16:37:46 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
pr1 = self._pr(repo, 'PR1', [{'a': 'AAA'}, {'b': 'BBB'}], user=config['role_user']['token'], reviewer=config['role_reviewer']['token'])
|
|
|
|
pr2 = self._pr(repo, 'PR2', [{'c': 'CCC'}, {'d': 'DDD'}], user=config['role_user']['token'], reviewer=config['role_reviewer']['token'])
|
|
|
|
env.run_crons()
|
2018-03-14 16:37:46 +07:00
|
|
|
|
2021-08-09 12:55:38 +07:00
|
|
|
pr1 = to_pr(env, pr1)
|
2018-03-14 16:37:46 +07:00
|
|
|
assert pr1.staging_id
|
2021-08-09 12:55:38 +07:00
|
|
|
pr2 = to_pr(env, pr2)
|
2018-03-14 16:37:46 +07:00
|
|
|
assert pr1.staging_id
|
|
|
|
assert pr2.staging_id
|
|
|
|
assert pr1.staging_id == pr2.staging_id
|
|
|
|
|
2018-09-19 22:33:25 +07:00
|
|
|
log = list(repo.log('heads/staging.master'))
|
|
|
|
staging = log_to_node(log)
|
2018-11-22 00:43:05 +07:00
|
|
|
reviewer = get_partner(env, users["reviewer"]).formatted_email
|
2018-09-19 22:33:25 +07:00
|
|
|
p1 = node(
|
2021-08-09 12:55:38 +07:00
|
|
|
'title PR1\n\nbody PR1\n\ncloses {}\n\nSigned-off-by: {}'.format(pr1.display_name, reviewer),
|
2018-09-19 22:33:25 +07:00
|
|
|
node('initial'),
|
2021-08-09 12:55:38 +07:00
|
|
|
node(part_of('commit_PR1_01', pr1), node(part_of('commit_PR1_00', pr1), node('initial')))
|
2018-09-19 22:33:25 +07:00
|
|
|
)
|
|
|
|
p2 = node(
|
2021-08-09 12:55:38 +07:00
|
|
|
'title PR2\n\nbody PR2\n\ncloses {}\n\nSigned-off-by: {}'.format(pr2.display_name, reviewer),
|
2018-09-19 22:33:25 +07:00
|
|
|
p1,
|
2021-08-09 12:55:38 +07:00
|
|
|
node(part_of('commit_PR2_01', pr2), node(part_of('commit_PR2_00', pr2), p1))
|
2018-09-19 22:33:25 +07:00
|
|
|
)
|
2021-08-09 18:21:24 +07:00
|
|
|
assert staging == p2
|
2018-09-19 22:33:25 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
def test_staging_batch_norebase(self, env, repo, users, config):
|
2018-09-19 22:33:25 +07:00
|
|
|
""" If multiple PRs are ready for the same target at the same point,
|
|
|
|
they should be staged together
|
|
|
|
"""
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
m = repo.make_commit(None, 'initial', None, tree={'a': 'some content'})
|
|
|
|
repo.make_ref('heads/master', m)
|
2018-09-19 22:33:25 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
pr1 = self._pr(repo, 'PR1', [{'a': 'AAA'}, {'b': 'BBB'}], user=config['role_user']['token'], reviewer=config['role_reviewer']['token'])
|
|
|
|
pr1.post_comment('hansen merge', config['role_reviewer']['token'])
|
|
|
|
pr2 = self._pr(repo, 'PR2', [{'c': 'CCC'}, {'d': 'DDD'}], user=config['role_user']['token'], reviewer=config['role_reviewer']['token'])
|
|
|
|
pr2.post_comment('hansen merge', config['role_reviewer']['token'])
|
|
|
|
env.run_crons()
|
2018-09-19 22:33:25 +07:00
|
|
|
|
2021-08-09 12:55:38 +07:00
|
|
|
pr1 = to_pr(env, pr1)
|
2018-09-19 22:33:25 +07:00
|
|
|
assert pr1.staging_id
|
2018-11-26 16:28:13 +07:00
|
|
|
assert pr1.merge_method == 'merge'
|
2021-08-09 12:55:38 +07:00
|
|
|
pr2 = to_pr(env, pr2)
|
2018-11-26 16:28:13 +07:00
|
|
|
assert pr2.merge_method == 'merge'
|
2018-09-19 22:33:25 +07:00
|
|
|
assert pr1.staging_id
|
|
|
|
assert pr2.staging_id
|
|
|
|
assert pr1.staging_id == pr2.staging_id
|
|
|
|
|
2021-08-09 18:21:24 +07:00
|
|
|
log = list(repo.log('staging.master'))
|
2018-09-19 22:33:25 +07:00
|
|
|
|
|
|
|
staging = log_to_node(log)
|
2018-11-22 00:43:05 +07:00
|
|
|
reviewer = get_partner(env, users["reviewer"]).formatted_email
|
2018-09-19 22:33:25 +07:00
|
|
|
|
|
|
|
p1 = node(
|
2018-11-22 00:43:05 +07:00
|
|
|
'title PR1\n\nbody PR1\n\ncloses {}#{}\n\nSigned-off-by: {}'.format(repo.name, pr1.number, reviewer),
|
2018-09-19 22:33:25 +07:00
|
|
|
node('initial'),
|
|
|
|
node('commit_PR1_01', node('commit_PR1_00', node('initial')))
|
|
|
|
)
|
|
|
|
p2 = node(
|
2018-11-22 00:43:05 +07:00
|
|
|
'title PR2\n\nbody PR2\n\ncloses {}#{}\n\nSigned-off-by: {}'.format(repo.name, pr2.number, reviewer),
|
2018-09-19 22:33:25 +07:00
|
|
|
p1,
|
|
|
|
node('commit_PR2_01', node('commit_PR2_00', node('initial')))
|
|
|
|
)
|
2021-08-09 18:21:24 +07:00
|
|
|
assert staging == p2
|
2018-09-19 22:33:25 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
def test_staging_batch_squash(self, env, repo, users, config):
|
2018-09-20 15:52:58 +07:00
|
|
|
""" If multiple PRs are ready for the same target at the same point,
|
|
|
|
they should be staged together
|
|
|
|
"""
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
m = repo.make_commit(None, 'initial', None, tree={'a': 'some content'})
|
|
|
|
repo.make_ref('heads/master', m)
|
2018-09-20 15:52:58 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
pr1 = self._pr(repo, 'PR1', [{'a': 'AAA'}], user=config['role_user']['token'], reviewer=config['role_reviewer']['token'])
|
|
|
|
pr2 = self._pr(repo, 'PR2', [{'c': 'CCC'}], user=config['role_user']['token'], reviewer=config['role_reviewer']['token'])
|
|
|
|
env.run_crons()
|
2018-09-20 15:52:58 +07:00
|
|
|
|
2021-08-09 12:55:38 +07:00
|
|
|
pr1 = to_pr(env, pr1)
|
2018-09-20 15:52:58 +07:00
|
|
|
assert pr1.staging_id
|
2021-08-09 12:55:38 +07:00
|
|
|
pr2 = to_pr(env, pr2)
|
2018-09-20 15:52:58 +07:00
|
|
|
assert pr1.staging_id
|
|
|
|
assert pr2.staging_id
|
|
|
|
assert pr1.staging_id == pr2.staging_id
|
|
|
|
|
|
|
|
log = list(repo.log('heads/staging.master'))
|
|
|
|
|
|
|
|
staging = log_to_node(log)
|
2018-11-22 00:43:05 +07:00
|
|
|
reviewer = get_partner(env, users["reviewer"]).formatted_email
|
2021-08-09 18:21:24 +07:00
|
|
|
expected = node('commit_PR2_00\n\ncloses {}#{}\n\nSigned-off-by: {}'.format(repo.name, pr2.number, reviewer),
|
|
|
|
node('commit_PR1_00\n\ncloses {}#{}\n\nSigned-off-by: {}'.format(repo.name, pr1.number, reviewer),
|
|
|
|
node('initial')))
|
2018-09-20 15:52:58 +07:00
|
|
|
assert staging == expected
|
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
def test_batching_pressing(self, env, repo, config):
|
2018-03-14 16:37:46 +07:00
|
|
|
""" "Pressing" PRs should be selected before normal & batched together
|
|
|
|
"""
|
[ADD] *: per-repository webhook secret
Currently webhook secrets are configured per *project* which is an
issue both because different repositories may have different
administrators and thus creates safety concerns, and because multiple
repositories can feed into different projects (e.g. on mergebot,
odoo-dev/odoo is both an ancillary repository to the main RD project,
and the main repository to the minor / legacy master-wowl
project). This means it can be necessary to have multiple projects
share the same secret as well, this then mandates the secret for more
repositories per (1).
This is a pain in the ass, so just detach secrets from projects and
link them *only* to repositories, it's cleaner and easier to manage
and set up progressively.
This requires a lot of changes to the tests, as they all need to
correctly configure the signaling.
For `runbot_merge` there was *some* setup sharing already via the
module-level `repo` fixtures`, those were merged into a conftest-level
fixture which could handle the signaling setup. A few tests which
unnecessarily set up repositories ad-hoc were also moved to the
fixture. But for most of the ad-hoc setup in `runbot_merge`, as well
as `forwardport` where it's all ad-hoc, events sources setup was just
appended as is. This should probably be cleaned up at one point, with
the various requirements collected and organised into a small set of
fixtures doing the job more uniformly.
Fixes #887
2024-06-06 16:07:57 +07:00
|
|
|
# by limiting the batch size to 3 we allow both high-priority PRs, but
|
[CHG] *: rewrite commands set, rework status management
This commit revisits the commands set in order to make it more
regular, and limit inconsistent command-sets, although it includes
pseudo-command aliases for common tasks now removed from the core set.
Hard Errors
===========
The previous iteration of the commands set would ignore any
non-command term in a command line. This has been changed to hard
error (and ignoring the entire thing) if any command is unknown or
invalid.
This fixes inconsistent / unexpected interpretations where a user
sends a command, then writes a novel on the same line some words of
which happen to *also* be commands, leading to merge states they did
not expect. They should now be told to fuck off.
Priority Restructuring
----------------------
The numerical priority system was pretty messy in that it confused
"staging priority" (in ways which were not entirely straightforward)
with overrides to other concerns.
This has now being split along all the axis, with separate command
subsets for:
- staging prioritisation, now separated between `default`, `priority`,
and `alone`,
- `default` means PRs are picked by an unspecified order when
creating a staging, if nothing better is available
- `priority` means PRs are picked first when staging, however if
`priority` PRs don't fill the staging the rest will be filled with
`default`, this mode did not previously exist
- `alone` means the PRs are picked first, before splits, and only
`alone` PRs can be part of the staging (which usually matches the
modename)
- `skipchecks` overrides both statuses and approval checks, for the
batch, something previously implied in `p=0`, but now
independent. Setting `skipchecks` basically makes the entire batch
`ready`.
For consistency this also sets the reviewer implicitly: since
skipchecks overrides both statuses *and approval*, whoever enables
this mode is essentially the reviewer.
- `cancel` cancels any ongoing staging when the marked PR becomes
ready again, previously this was also implied (in a more restricted
form) by setting `p=0`
FWBot removal
=============
While the "forwardport bot" still exists as an API level (to segregate
access rights between tokens) it has been removed as an interaction
point, as part of the modules merge plan. As a result,
fwbot stops responding
----------------------
Feedback messages are now always sent by the mergebot, the
forward-porting bot should not send any message or notification
anymore.
commands moved to the merge bot
-------------------------------
- `ignore`/`up to` simply changes bot
- `close` as well
- `skipci` is now a choice / flag of an `fw` command, which denotes
the forward-port policy,
- `fw=default` is the old `ci` and resets the policy to default,
that is wait for the PR to be merged to create forward ports, and
for the required statuses on each forward port to be received
before creating the next
- `fw=skipci` is the old `skipci`, it waits for the merge of the
base PR but then creates all the forward ports immediately (unless
it gets a conflict)
- `fw=skipmerge` immediately creates all the forward ports, without
even waiting for the PR to be merged
This is a completely new mode, and may be rather broken as until
now the 'bot has always assumed the source PR had been merged.
approval rework
---------------
Because of the previous section, there is no distinguishing feature
between `mergebot r+` = "merge this PR" and `forwardbot r+` = "merge
this PR and all its parent with different access rights".
As a result, the two have been merged under a single `mergebot r+`
with heuristics attempting to provide the best experience:
- if approving a non-forward port, the behavior does not change
- else, with review rights on the source, all ancestors are approved
- else, as author of the original, approves all ancestors which descend
from a merged PR
- else, approves all ancestors up to and including the oldest ancestor
to which we have review rights
Most notably, the source's author is not delegated on the source or
any of its descendants anymore. This might need to be revisited if it
provides too restrictive.
For the very specialized need of approving a forward-port *and none of
its ancestors*, `review=` can now take a comma (`,`) separated list of
pull request numbers (github numbers, not mergebot ids).
Computed State
==============
The `state` field of pull requests is now computed. Hopefully this
makes the status more consistent and predictable in the long run, and
importantly makes status management more reliable (because reference
datum get updated naturally flowing to the state).
For now however it makes things more complicated as some of the states
have to be separately signaled or updated:
- `closed` and `error` are now separate flags
- `merge_date` is pulled down from forwardport and becomes the
transition signal for ready -> merged
- `reviewed_by` becomes the transition signal for approval (might be a
good idea to rename it...)
- `status` is computed from the head's statuses and overrides, and
*that* becomes the validation state
Ideally, batch-level flags like `skipchecks` should be on, well, the
batch, and `state` should have a dependency on the batch. However
currently the batch is not a durable / permanent member of the system,
so it's a PR-level flag and a messy pile.
On notable change is that *forcing* the state to `ready` now does that
but also sets the reviewer, `skipchecks`, and overrides to ensure the
API-mediated readying does not get rolled back by e.g. the runbot
sending a status.
This is useful for a few types of automated / programmatic PRs
e.g. translation exports, where we set the state programmatically to
limit noise.
recursive dependency hack
-------------------------
Given a sequence of PRs with an override of the source, if one of the
PRs is updated its descendants should not have the override
anymore. However if the updated PR gets overridden, its descendants
should have *that* override.
This requires some unholy manipulations via an override of `modified`,
as the ORM supports recursive fields but not recursive
dependencies (on a different field).
unconditional followup scheduling
---------------------------------
Previously scheduling forward-port followup was contigent on the FW
policy, but it's not actually correct if the new PR is *immediately*
validated (which can happen now that the field is computed, if there
are no required statuses *or* all of the required statuses are
overridden by an ancestor) as nothing will trigger the state change
and thus scheduling of the fp followup.
The followup function checks all the properties of the batch to port,
so this should not result on incorrect ports. Although it's a bit more
expensive, and will lead to more spam.
Previously this would not happen because on creation of a PR the
validation task (commit -> PR) would still have to execute.
Misc Changes
============
- If a PR is marked as overriding / canceling stagings, it now does
so on retry not just when setting initially.
This was not handled at all previously, so a PR in P0 going into
error due to e.g. a non-deterministic bug would be retried and still
p=0, but a current staging would not get cancelled. Same when a PR
in p=0 goes into error because something was failed, then is updated
with a fix.
- Add tracking to a bunch of relevant PR fields.
Post-mortem analysis currently generally requires going through the
text logs to see what happened, which is annoying.
There is a nondeterminism / inconsistency in the tracking which
sometimes leads the admin user to trigger tracking before the bot
does, leading to the staging tracking being attributed to them
during tests, shove under the carpet by ignoring the user to whom
that tracking is attributed.
When multiple users update tracked fields in the same transaction
all the changes are attributed to the first one having triggered
tracking (?), I couldn't find why the admin sometimes takes over.
- added and leveraged support for enum-backed selection fields
- moved variuous fields from forwardport to runbot_merge
- fix a migration which had never worked and which never run (because
I forgot to bump the version on the module)
- remove some unnecessary intermediate de/serialisation
fixes #673, fixes #309, fixes #792, fixes #846 (probably)
2023-10-31 13:42:07 +07:00
|
|
|
# a single normal priority one
|
|
|
|
env['runbot_merge.project'].search([]).batch_limit = 3
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
m = repo.make_commit(None, 'initial', None, tree={'a': 'some content'})
|
|
|
|
repo.make_ref('heads/master', m)
|
2018-03-14 16:37:46 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
pr21 = self._pr(repo, 'PR1', [{'a': 'AAA'}, {'b': 'BBB'}], user=config['role_user']['token'], reviewer=config['role_reviewer']['token'])
|
|
|
|
pr22 = self._pr(repo, 'PR2', [{'c': 'CCC'}, {'d': 'DDD'}], user=config['role_user']['token'], reviewer=config['role_reviewer']['token'])
|
2018-03-14 16:37:46 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
pr11 = self._pr(repo, 'Pressing1', [{'x': 'x'}, {'y': 'y'}], user=config['role_user']['token'], reviewer=config['role_reviewer']['token'])
|
|
|
|
pr12 = self._pr(repo, 'Pressing2', [{'z': 'z'}, {'zz': 'zz'}], user=config['role_user']['token'], reviewer=config['role_reviewer']['token'])
|
[CHG] *: rewrite commands set, rework status management
This commit revisits the commands set in order to make it more
regular, and limit inconsistent command-sets, although it includes
pseudo-command aliases for common tasks now removed from the core set.
Hard Errors
===========
The previous iteration of the commands set would ignore any
non-command term in a command line. This has been changed to hard
error (and ignoring the entire thing) if any command is unknown or
invalid.
This fixes inconsistent / unexpected interpretations where a user
sends a command, then writes a novel on the same line some words of
which happen to *also* be commands, leading to merge states they did
not expect. They should now be told to fuck off.
Priority Restructuring
----------------------
The numerical priority system was pretty messy in that it confused
"staging priority" (in ways which were not entirely straightforward)
with overrides to other concerns.
This has now being split along all the axis, with separate command
subsets for:
- staging prioritisation, now separated between `default`, `priority`,
and `alone`,
- `default` means PRs are picked by an unspecified order when
creating a staging, if nothing better is available
- `priority` means PRs are picked first when staging, however if
`priority` PRs don't fill the staging the rest will be filled with
`default`, this mode did not previously exist
- `alone` means the PRs are picked first, before splits, and only
`alone` PRs can be part of the staging (which usually matches the
modename)
- `skipchecks` overrides both statuses and approval checks, for the
batch, something previously implied in `p=0`, but now
independent. Setting `skipchecks` basically makes the entire batch
`ready`.
For consistency this also sets the reviewer implicitly: since
skipchecks overrides both statuses *and approval*, whoever enables
this mode is essentially the reviewer.
- `cancel` cancels any ongoing staging when the marked PR becomes
ready again, previously this was also implied (in a more restricted
form) by setting `p=0`
FWBot removal
=============
While the "forwardport bot" still exists as an API level (to segregate
access rights between tokens) it has been removed as an interaction
point, as part of the modules merge plan. As a result,
fwbot stops responding
----------------------
Feedback messages are now always sent by the mergebot, the
forward-porting bot should not send any message or notification
anymore.
commands moved to the merge bot
-------------------------------
- `ignore`/`up to` simply changes bot
- `close` as well
- `skipci` is now a choice / flag of an `fw` command, which denotes
the forward-port policy,
- `fw=default` is the old `ci` and resets the policy to default,
that is wait for the PR to be merged to create forward ports, and
for the required statuses on each forward port to be received
before creating the next
- `fw=skipci` is the old `skipci`, it waits for the merge of the
base PR but then creates all the forward ports immediately (unless
it gets a conflict)
- `fw=skipmerge` immediately creates all the forward ports, without
even waiting for the PR to be merged
This is a completely new mode, and may be rather broken as until
now the 'bot has always assumed the source PR had been merged.
approval rework
---------------
Because of the previous section, there is no distinguishing feature
between `mergebot r+` = "merge this PR" and `forwardbot r+` = "merge
this PR and all its parent with different access rights".
As a result, the two have been merged under a single `mergebot r+`
with heuristics attempting to provide the best experience:
- if approving a non-forward port, the behavior does not change
- else, with review rights on the source, all ancestors are approved
- else, as author of the original, approves all ancestors which descend
from a merged PR
- else, approves all ancestors up to and including the oldest ancestor
to which we have review rights
Most notably, the source's author is not delegated on the source or
any of its descendants anymore. This might need to be revisited if it
provides too restrictive.
For the very specialized need of approving a forward-port *and none of
its ancestors*, `review=` can now take a comma (`,`) separated list of
pull request numbers (github numbers, not mergebot ids).
Computed State
==============
The `state` field of pull requests is now computed. Hopefully this
makes the status more consistent and predictable in the long run, and
importantly makes status management more reliable (because reference
datum get updated naturally flowing to the state).
For now however it makes things more complicated as some of the states
have to be separately signaled or updated:
- `closed` and `error` are now separate flags
- `merge_date` is pulled down from forwardport and becomes the
transition signal for ready -> merged
- `reviewed_by` becomes the transition signal for approval (might be a
good idea to rename it...)
- `status` is computed from the head's statuses and overrides, and
*that* becomes the validation state
Ideally, batch-level flags like `skipchecks` should be on, well, the
batch, and `state` should have a dependency on the batch. However
currently the batch is not a durable / permanent member of the system,
so it's a PR-level flag and a messy pile.
On notable change is that *forcing* the state to `ready` now does that
but also sets the reviewer, `skipchecks`, and overrides to ensure the
API-mediated readying does not get rolled back by e.g. the runbot
sending a status.
This is useful for a few types of automated / programmatic PRs
e.g. translation exports, where we set the state programmatically to
limit noise.
recursive dependency hack
-------------------------
Given a sequence of PRs with an override of the source, if one of the
PRs is updated its descendants should not have the override
anymore. However if the updated PR gets overridden, its descendants
should have *that* override.
This requires some unholy manipulations via an override of `modified`,
as the ORM supports recursive fields but not recursive
dependencies (on a different field).
unconditional followup scheduling
---------------------------------
Previously scheduling forward-port followup was contigent on the FW
policy, but it's not actually correct if the new PR is *immediately*
validated (which can happen now that the field is computed, if there
are no required statuses *or* all of the required statuses are
overridden by an ancestor) as nothing will trigger the state change
and thus scheduling of the fp followup.
The followup function checks all the properties of the batch to port,
so this should not result on incorrect ports. Although it's a bit more
expensive, and will lead to more spam.
Previously this would not happen because on creation of a PR the
validation task (commit -> PR) would still have to execute.
Misc Changes
============
- If a PR is marked as overriding / canceling stagings, it now does
so on retry not just when setting initially.
This was not handled at all previously, so a PR in P0 going into
error due to e.g. a non-deterministic bug would be retried and still
p=0, but a current staging would not get cancelled. Same when a PR
in p=0 goes into error because something was failed, then is updated
with a fix.
- Add tracking to a bunch of relevant PR fields.
Post-mortem analysis currently generally requires going through the
text logs to see what happened, which is annoying.
There is a nondeterminism / inconsistency in the tracking which
sometimes leads the admin user to trigger tracking before the bot
does, leading to the staging tracking being attributed to them
during tests, shove under the carpet by ignoring the user to whom
that tracking is attributed.
When multiple users update tracked fields in the same transaction
all the changes are attributed to the first one having triggered
tracking (?), I couldn't find why the admin sometimes takes over.
- added and leveraged support for enum-backed selection fields
- moved variuous fields from forwardport to runbot_merge
- fix a migration which had never worked and which never run (because
I forgot to bump the version on the module)
- remove some unnecessary intermediate de/serialisation
fixes #673, fixes #309, fixes #792, fixes #846 (probably)
2023-10-31 13:42:07 +07:00
|
|
|
pr11.post_comment('hansen priority', config['role_reviewer']['token'])
|
|
|
|
pr12.post_comment('hansen priority', config['role_reviewer']['token'])
|
|
|
|
# necessary to project commit statuses onto PRs
|
2019-10-10 14:22:12 +07:00
|
|
|
env.run_crons()
|
2018-03-14 16:37:46 +07:00
|
|
|
|
[CHG] *: rewrite commands set, rework status management
This commit revisits the commands set in order to make it more
regular, and limit inconsistent command-sets, although it includes
pseudo-command aliases for common tasks now removed from the core set.
Hard Errors
===========
The previous iteration of the commands set would ignore any
non-command term in a command line. This has been changed to hard
error (and ignoring the entire thing) if any command is unknown or
invalid.
This fixes inconsistent / unexpected interpretations where a user
sends a command, then writes a novel on the same line some words of
which happen to *also* be commands, leading to merge states they did
not expect. They should now be told to fuck off.
Priority Restructuring
----------------------
The numerical priority system was pretty messy in that it confused
"staging priority" (in ways which were not entirely straightforward)
with overrides to other concerns.
This has now being split along all the axis, with separate command
subsets for:
- staging prioritisation, now separated between `default`, `priority`,
and `alone`,
- `default` means PRs are picked by an unspecified order when
creating a staging, if nothing better is available
- `priority` means PRs are picked first when staging, however if
`priority` PRs don't fill the staging the rest will be filled with
`default`, this mode did not previously exist
- `alone` means the PRs are picked first, before splits, and only
`alone` PRs can be part of the staging (which usually matches the
modename)
- `skipchecks` overrides both statuses and approval checks, for the
batch, something previously implied in `p=0`, but now
independent. Setting `skipchecks` basically makes the entire batch
`ready`.
For consistency this also sets the reviewer implicitly: since
skipchecks overrides both statuses *and approval*, whoever enables
this mode is essentially the reviewer.
- `cancel` cancels any ongoing staging when the marked PR becomes
ready again, previously this was also implied (in a more restricted
form) by setting `p=0`
FWBot removal
=============
While the "forwardport bot" still exists as an API level (to segregate
access rights between tokens) it has been removed as an interaction
point, as part of the modules merge plan. As a result,
fwbot stops responding
----------------------
Feedback messages are now always sent by the mergebot, the
forward-porting bot should not send any message or notification
anymore.
commands moved to the merge bot
-------------------------------
- `ignore`/`up to` simply changes bot
- `close` as well
- `skipci` is now a choice / flag of an `fw` command, which denotes
the forward-port policy,
- `fw=default` is the old `ci` and resets the policy to default,
that is wait for the PR to be merged to create forward ports, and
for the required statuses on each forward port to be received
before creating the next
- `fw=skipci` is the old `skipci`, it waits for the merge of the
base PR but then creates all the forward ports immediately (unless
it gets a conflict)
- `fw=skipmerge` immediately creates all the forward ports, without
even waiting for the PR to be merged
This is a completely new mode, and may be rather broken as until
now the 'bot has always assumed the source PR had been merged.
approval rework
---------------
Because of the previous section, there is no distinguishing feature
between `mergebot r+` = "merge this PR" and `forwardbot r+` = "merge
this PR and all its parent with different access rights".
As a result, the two have been merged under a single `mergebot r+`
with heuristics attempting to provide the best experience:
- if approving a non-forward port, the behavior does not change
- else, with review rights on the source, all ancestors are approved
- else, as author of the original, approves all ancestors which descend
from a merged PR
- else, approves all ancestors up to and including the oldest ancestor
to which we have review rights
Most notably, the source's author is not delegated on the source or
any of its descendants anymore. This might need to be revisited if it
provides too restrictive.
For the very specialized need of approving a forward-port *and none of
its ancestors*, `review=` can now take a comma (`,`) separated list of
pull request numbers (github numbers, not mergebot ids).
Computed State
==============
The `state` field of pull requests is now computed. Hopefully this
makes the status more consistent and predictable in the long run, and
importantly makes status management more reliable (because reference
datum get updated naturally flowing to the state).
For now however it makes things more complicated as some of the states
have to be separately signaled or updated:
- `closed` and `error` are now separate flags
- `merge_date` is pulled down from forwardport and becomes the
transition signal for ready -> merged
- `reviewed_by` becomes the transition signal for approval (might be a
good idea to rename it...)
- `status` is computed from the head's statuses and overrides, and
*that* becomes the validation state
Ideally, batch-level flags like `skipchecks` should be on, well, the
batch, and `state` should have a dependency on the batch. However
currently the batch is not a durable / permanent member of the system,
so it's a PR-level flag and a messy pile.
On notable change is that *forcing* the state to `ready` now does that
but also sets the reviewer, `skipchecks`, and overrides to ensure the
API-mediated readying does not get rolled back by e.g. the runbot
sending a status.
This is useful for a few types of automated / programmatic PRs
e.g. translation exports, where we set the state programmatically to
limit noise.
recursive dependency hack
-------------------------
Given a sequence of PRs with an override of the source, if one of the
PRs is updated its descendants should not have the override
anymore. However if the updated PR gets overridden, its descendants
should have *that* override.
This requires some unholy manipulations via an override of `modified`,
as the ORM supports recursive fields but not recursive
dependencies (on a different field).
unconditional followup scheduling
---------------------------------
Previously scheduling forward-port followup was contigent on the FW
policy, but it's not actually correct if the new PR is *immediately*
validated (which can happen now that the field is computed, if there
are no required statuses *or* all of the required statuses are
overridden by an ancestor) as nothing will trigger the state change
and thus scheduling of the fp followup.
The followup function checks all the properties of the batch to port,
so this should not result on incorrect ports. Although it's a bit more
expensive, and will lead to more spam.
Previously this would not happen because on creation of a PR the
validation task (commit -> PR) would still have to execute.
Misc Changes
============
- If a PR is marked as overriding / canceling stagings, it now does
so on retry not just when setting initially.
This was not handled at all previously, so a PR in P0 going into
error due to e.g. a non-deterministic bug would be retried and still
p=0, but a current staging would not get cancelled. Same when a PR
in p=0 goes into error because something was failed, then is updated
with a fix.
- Add tracking to a bunch of relevant PR fields.
Post-mortem analysis currently generally requires going through the
text logs to see what happened, which is annoying.
There is a nondeterminism / inconsistency in the tracking which
sometimes leads the admin user to trigger tracking before the bot
does, leading to the staging tracking being attributed to them
during tests, shove under the carpet by ignoring the user to whom
that tracking is attributed.
When multiple users update tracked fields in the same transaction
all the changes are attributed to the first one having triggered
tracking (?), I couldn't find why the admin sometimes takes over.
- added and leveraged support for enum-backed selection fields
- moved variuous fields from forwardport to runbot_merge
- fix a migration which had never worked and which never run (because
I forgot to bump the version on the module)
- remove some unnecessary intermediate de/serialisation
fixes #673, fixes #309, fixes #792, fixes #846 (probably)
2023-10-31 13:42:07 +07:00
|
|
|
pr21, pr22, pr11, pr12 = prs = [to_pr(env, pr) for pr in [pr21, pr22, pr11, pr12]]
|
|
|
|
assert pr11.priority == pr12.priority == 'priority'
|
|
|
|
assert pr21.priority == pr22.priority == 'default'
|
2018-03-14 16:37:46 +07:00
|
|
|
assert all(pr.state == 'ready' for pr in prs)
|
[CHG] *: rewrite commands set, rework status management
This commit revisits the commands set in order to make it more
regular, and limit inconsistent command-sets, although it includes
pseudo-command aliases for common tasks now removed from the core set.
Hard Errors
===========
The previous iteration of the commands set would ignore any
non-command term in a command line. This has been changed to hard
error (and ignoring the entire thing) if any command is unknown or
invalid.
This fixes inconsistent / unexpected interpretations where a user
sends a command, then writes a novel on the same line some words of
which happen to *also* be commands, leading to merge states they did
not expect. They should now be told to fuck off.
Priority Restructuring
----------------------
The numerical priority system was pretty messy in that it confused
"staging priority" (in ways which were not entirely straightforward)
with overrides to other concerns.
This has now being split along all the axis, with separate command
subsets for:
- staging prioritisation, now separated between `default`, `priority`,
and `alone`,
- `default` means PRs are picked by an unspecified order when
creating a staging, if nothing better is available
- `priority` means PRs are picked first when staging, however if
`priority` PRs don't fill the staging the rest will be filled with
`default`, this mode did not previously exist
- `alone` means the PRs are picked first, before splits, and only
`alone` PRs can be part of the staging (which usually matches the
modename)
- `skipchecks` overrides both statuses and approval checks, for the
batch, something previously implied in `p=0`, but now
independent. Setting `skipchecks` basically makes the entire batch
`ready`.
For consistency this also sets the reviewer implicitly: since
skipchecks overrides both statuses *and approval*, whoever enables
this mode is essentially the reviewer.
- `cancel` cancels any ongoing staging when the marked PR becomes
ready again, previously this was also implied (in a more restricted
form) by setting `p=0`
FWBot removal
=============
While the "forwardport bot" still exists as an API level (to segregate
access rights between tokens) it has been removed as an interaction
point, as part of the modules merge plan. As a result,
fwbot stops responding
----------------------
Feedback messages are now always sent by the mergebot, the
forward-porting bot should not send any message or notification
anymore.
commands moved to the merge bot
-------------------------------
- `ignore`/`up to` simply changes bot
- `close` as well
- `skipci` is now a choice / flag of an `fw` command, which denotes
the forward-port policy,
- `fw=default` is the old `ci` and resets the policy to default,
that is wait for the PR to be merged to create forward ports, and
for the required statuses on each forward port to be received
before creating the next
- `fw=skipci` is the old `skipci`, it waits for the merge of the
base PR but then creates all the forward ports immediately (unless
it gets a conflict)
- `fw=skipmerge` immediately creates all the forward ports, without
even waiting for the PR to be merged
This is a completely new mode, and may be rather broken as until
now the 'bot has always assumed the source PR had been merged.
approval rework
---------------
Because of the previous section, there is no distinguishing feature
between `mergebot r+` = "merge this PR" and `forwardbot r+` = "merge
this PR and all its parent with different access rights".
As a result, the two have been merged under a single `mergebot r+`
with heuristics attempting to provide the best experience:
- if approving a non-forward port, the behavior does not change
- else, with review rights on the source, all ancestors are approved
- else, as author of the original, approves all ancestors which descend
from a merged PR
- else, approves all ancestors up to and including the oldest ancestor
to which we have review rights
Most notably, the source's author is not delegated on the source or
any of its descendants anymore. This might need to be revisited if it
provides too restrictive.
For the very specialized need of approving a forward-port *and none of
its ancestors*, `review=` can now take a comma (`,`) separated list of
pull request numbers (github numbers, not mergebot ids).
Computed State
==============
The `state` field of pull requests is now computed. Hopefully this
makes the status more consistent and predictable in the long run, and
importantly makes status management more reliable (because reference
datum get updated naturally flowing to the state).
For now however it makes things more complicated as some of the states
have to be separately signaled or updated:
- `closed` and `error` are now separate flags
- `merge_date` is pulled down from forwardport and becomes the
transition signal for ready -> merged
- `reviewed_by` becomes the transition signal for approval (might be a
good idea to rename it...)
- `status` is computed from the head's statuses and overrides, and
*that* becomes the validation state
Ideally, batch-level flags like `skipchecks` should be on, well, the
batch, and `state` should have a dependency on the batch. However
currently the batch is not a durable / permanent member of the system,
so it's a PR-level flag and a messy pile.
On notable change is that *forcing* the state to `ready` now does that
but also sets the reviewer, `skipchecks`, and overrides to ensure the
API-mediated readying does not get rolled back by e.g. the runbot
sending a status.
This is useful for a few types of automated / programmatic PRs
e.g. translation exports, where we set the state programmatically to
limit noise.
recursive dependency hack
-------------------------
Given a sequence of PRs with an override of the source, if one of the
PRs is updated its descendants should not have the override
anymore. However if the updated PR gets overridden, its descendants
should have *that* override.
This requires some unholy manipulations via an override of `modified`,
as the ORM supports recursive fields but not recursive
dependencies (on a different field).
unconditional followup scheduling
---------------------------------
Previously scheduling forward-port followup was contigent on the FW
policy, but it's not actually correct if the new PR is *immediately*
validated (which can happen now that the field is computed, if there
are no required statuses *or* all of the required statuses are
overridden by an ancestor) as nothing will trigger the state change
and thus scheduling of the fp followup.
The followup function checks all the properties of the batch to port,
so this should not result on incorrect ports. Although it's a bit more
expensive, and will lead to more spam.
Previously this would not happen because on creation of a PR the
validation task (commit -> PR) would still have to execute.
Misc Changes
============
- If a PR is marked as overriding / canceling stagings, it now does
so on retry not just when setting initially.
This was not handled at all previously, so a PR in P0 going into
error due to e.g. a non-deterministic bug would be retried and still
p=0, but a current staging would not get cancelled. Same when a PR
in p=0 goes into error because something was failed, then is updated
with a fix.
- Add tracking to a bunch of relevant PR fields.
Post-mortem analysis currently generally requires going through the
text logs to see what happened, which is annoying.
There is a nondeterminism / inconsistency in the tracking which
sometimes leads the admin user to trigger tracking before the bot
does, leading to the staging tracking being attributed to them
during tests, shove under the carpet by ignoring the user to whom
that tracking is attributed.
When multiple users update tracked fields in the same transaction
all the changes are attributed to the first one having triggered
tracking (?), I couldn't find why the admin sometimes takes over.
- added and leveraged support for enum-backed selection fields
- moved variuous fields from forwardport to runbot_merge
- fix a migration which had never worked and which never run (because
I forgot to bump the version on the module)
- remove some unnecessary intermediate de/serialisation
fixes #673, fixes #309, fixes #792, fixes #846 (probably)
2023-10-31 13:42:07 +07:00
|
|
|
|
|
|
|
staging = ensure_one(env['runbot_merge.stagings'].search([]))
|
|
|
|
assert staging.pr_ids == pr11 | pr12 | pr21
|
2024-05-17 15:51:16 +07:00
|
|
|
assert list(staging.batch_ids) == [
|
|
|
|
pr11.batch_id,
|
|
|
|
pr12.batch_id,
|
|
|
|
pr21.batch_id,
|
|
|
|
]
|
2018-03-14 16:37:46 +07:00
|
|
|
assert not pr22.staging_id
|
|
|
|
|
2024-06-25 03:16:43 +07:00
|
|
|
@pytest.mark.usefixtures("reviewer_admin")
|
2019-10-10 14:22:12 +07:00
|
|
|
def test_batching_urgent(self, env, repo, config):
|
|
|
|
with repo:
|
|
|
|
m = repo.make_commit(None, 'initial', None, tree={'a': 'some content'})
|
|
|
|
repo.make_ref('heads/master', m)
|
2018-03-14 16:37:46 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
pr11 = self._pr(repo, 'Pressing1', [{'x': 'x'}, {'y': 'y'}], user=config['role_user']['token'], reviewer=config['role_reviewer']['token'])
|
|
|
|
pr12 = self._pr(repo, 'Pressing2', [{'z': 'z'}, {'zz': 'zz'}], user=config['role_user']['token'], reviewer=config['role_reviewer']['token'])
|
[CHG] *: rewrite commands set, rework status management
This commit revisits the commands set in order to make it more
regular, and limit inconsistent command-sets, although it includes
pseudo-command aliases for common tasks now removed from the core set.
Hard Errors
===========
The previous iteration of the commands set would ignore any
non-command term in a command line. This has been changed to hard
error (and ignoring the entire thing) if any command is unknown or
invalid.
This fixes inconsistent / unexpected interpretations where a user
sends a command, then writes a novel on the same line some words of
which happen to *also* be commands, leading to merge states they did
not expect. They should now be told to fuck off.
Priority Restructuring
----------------------
The numerical priority system was pretty messy in that it confused
"staging priority" (in ways which were not entirely straightforward)
with overrides to other concerns.
This has now being split along all the axis, with separate command
subsets for:
- staging prioritisation, now separated between `default`, `priority`,
and `alone`,
- `default` means PRs are picked by an unspecified order when
creating a staging, if nothing better is available
- `priority` means PRs are picked first when staging, however if
`priority` PRs don't fill the staging the rest will be filled with
`default`, this mode did not previously exist
- `alone` means the PRs are picked first, before splits, and only
`alone` PRs can be part of the staging (which usually matches the
modename)
- `skipchecks` overrides both statuses and approval checks, for the
batch, something previously implied in `p=0`, but now
independent. Setting `skipchecks` basically makes the entire batch
`ready`.
For consistency this also sets the reviewer implicitly: since
skipchecks overrides both statuses *and approval*, whoever enables
this mode is essentially the reviewer.
- `cancel` cancels any ongoing staging when the marked PR becomes
ready again, previously this was also implied (in a more restricted
form) by setting `p=0`
FWBot removal
=============
While the "forwardport bot" still exists as an API level (to segregate
access rights between tokens) it has been removed as an interaction
point, as part of the modules merge plan. As a result,
fwbot stops responding
----------------------
Feedback messages are now always sent by the mergebot, the
forward-porting bot should not send any message or notification
anymore.
commands moved to the merge bot
-------------------------------
- `ignore`/`up to` simply changes bot
- `close` as well
- `skipci` is now a choice / flag of an `fw` command, which denotes
the forward-port policy,
- `fw=default` is the old `ci` and resets the policy to default,
that is wait for the PR to be merged to create forward ports, and
for the required statuses on each forward port to be received
before creating the next
- `fw=skipci` is the old `skipci`, it waits for the merge of the
base PR but then creates all the forward ports immediately (unless
it gets a conflict)
- `fw=skipmerge` immediately creates all the forward ports, without
even waiting for the PR to be merged
This is a completely new mode, and may be rather broken as until
now the 'bot has always assumed the source PR had been merged.
approval rework
---------------
Because of the previous section, there is no distinguishing feature
between `mergebot r+` = "merge this PR" and `forwardbot r+` = "merge
this PR and all its parent with different access rights".
As a result, the two have been merged under a single `mergebot r+`
with heuristics attempting to provide the best experience:
- if approving a non-forward port, the behavior does not change
- else, with review rights on the source, all ancestors are approved
- else, as author of the original, approves all ancestors which descend
from a merged PR
- else, approves all ancestors up to and including the oldest ancestor
to which we have review rights
Most notably, the source's author is not delegated on the source or
any of its descendants anymore. This might need to be revisited if it
provides too restrictive.
For the very specialized need of approving a forward-port *and none of
its ancestors*, `review=` can now take a comma (`,`) separated list of
pull request numbers (github numbers, not mergebot ids).
Computed State
==============
The `state` field of pull requests is now computed. Hopefully this
makes the status more consistent and predictable in the long run, and
importantly makes status management more reliable (because reference
datum get updated naturally flowing to the state).
For now however it makes things more complicated as some of the states
have to be separately signaled or updated:
- `closed` and `error` are now separate flags
- `merge_date` is pulled down from forwardport and becomes the
transition signal for ready -> merged
- `reviewed_by` becomes the transition signal for approval (might be a
good idea to rename it...)
- `status` is computed from the head's statuses and overrides, and
*that* becomes the validation state
Ideally, batch-level flags like `skipchecks` should be on, well, the
batch, and `state` should have a dependency on the batch. However
currently the batch is not a durable / permanent member of the system,
so it's a PR-level flag and a messy pile.
On notable change is that *forcing* the state to `ready` now does that
but also sets the reviewer, `skipchecks`, and overrides to ensure the
API-mediated readying does not get rolled back by e.g. the runbot
sending a status.
This is useful for a few types of automated / programmatic PRs
e.g. translation exports, where we set the state programmatically to
limit noise.
recursive dependency hack
-------------------------
Given a sequence of PRs with an override of the source, if one of the
PRs is updated its descendants should not have the override
anymore. However if the updated PR gets overridden, its descendants
should have *that* override.
This requires some unholy manipulations via an override of `modified`,
as the ORM supports recursive fields but not recursive
dependencies (on a different field).
unconditional followup scheduling
---------------------------------
Previously scheduling forward-port followup was contigent on the FW
policy, but it's not actually correct if the new PR is *immediately*
validated (which can happen now that the field is computed, if there
are no required statuses *or* all of the required statuses are
overridden by an ancestor) as nothing will trigger the state change
and thus scheduling of the fp followup.
The followup function checks all the properties of the batch to port,
so this should not result on incorrect ports. Although it's a bit more
expensive, and will lead to more spam.
Previously this would not happen because on creation of a PR the
validation task (commit -> PR) would still have to execute.
Misc Changes
============
- If a PR is marked as overriding / canceling stagings, it now does
so on retry not just when setting initially.
This was not handled at all previously, so a PR in P0 going into
error due to e.g. a non-deterministic bug would be retried and still
p=0, but a current staging would not get cancelled. Same when a PR
in p=0 goes into error because something was failed, then is updated
with a fix.
- Add tracking to a bunch of relevant PR fields.
Post-mortem analysis currently generally requires going through the
text logs to see what happened, which is annoying.
There is a nondeterminism / inconsistency in the tracking which
sometimes leads the admin user to trigger tracking before the bot
does, leading to the staging tracking being attributed to them
during tests, shove under the carpet by ignoring the user to whom
that tracking is attributed.
When multiple users update tracked fields in the same transaction
all the changes are attributed to the first one having triggered
tracking (?), I couldn't find why the admin sometimes takes over.
- added and leveraged support for enum-backed selection fields
- moved variuous fields from forwardport to runbot_merge
- fix a migration which had never worked and which never run (because
I forgot to bump the version on the module)
- remove some unnecessary intermediate de/serialisation
fixes #673, fixes #309, fixes #792, fixes #846 (probably)
2023-10-31 13:42:07 +07:00
|
|
|
pr11.post_comment('hansen NOW', config['role_reviewer']['token'])
|
|
|
|
pr12.post_comment('hansen NOW', config['role_reviewer']['token'])
|
2018-03-14 16:37:46 +07:00
|
|
|
|
[CHG] *: rewrite commands set, rework status management
This commit revisits the commands set in order to make it more
regular, and limit inconsistent command-sets, although it includes
pseudo-command aliases for common tasks now removed from the core set.
Hard Errors
===========
The previous iteration of the commands set would ignore any
non-command term in a command line. This has been changed to hard
error (and ignoring the entire thing) if any command is unknown or
invalid.
This fixes inconsistent / unexpected interpretations where a user
sends a command, then writes a novel on the same line some words of
which happen to *also* be commands, leading to merge states they did
not expect. They should now be told to fuck off.
Priority Restructuring
----------------------
The numerical priority system was pretty messy in that it confused
"staging priority" (in ways which were not entirely straightforward)
with overrides to other concerns.
This has now being split along all the axis, with separate command
subsets for:
- staging prioritisation, now separated between `default`, `priority`,
and `alone`,
- `default` means PRs are picked by an unspecified order when
creating a staging, if nothing better is available
- `priority` means PRs are picked first when staging, however if
`priority` PRs don't fill the staging the rest will be filled with
`default`, this mode did not previously exist
- `alone` means the PRs are picked first, before splits, and only
`alone` PRs can be part of the staging (which usually matches the
modename)
- `skipchecks` overrides both statuses and approval checks, for the
batch, something previously implied in `p=0`, but now
independent. Setting `skipchecks` basically makes the entire batch
`ready`.
For consistency this also sets the reviewer implicitly: since
skipchecks overrides both statuses *and approval*, whoever enables
this mode is essentially the reviewer.
- `cancel` cancels any ongoing staging when the marked PR becomes
ready again, previously this was also implied (in a more restricted
form) by setting `p=0`
FWBot removal
=============
While the "forwardport bot" still exists as an API level (to segregate
access rights between tokens) it has been removed as an interaction
point, as part of the modules merge plan. As a result,
fwbot stops responding
----------------------
Feedback messages are now always sent by the mergebot, the
forward-porting bot should not send any message or notification
anymore.
commands moved to the merge bot
-------------------------------
- `ignore`/`up to` simply changes bot
- `close` as well
- `skipci` is now a choice / flag of an `fw` command, which denotes
the forward-port policy,
- `fw=default` is the old `ci` and resets the policy to default,
that is wait for the PR to be merged to create forward ports, and
for the required statuses on each forward port to be received
before creating the next
- `fw=skipci` is the old `skipci`, it waits for the merge of the
base PR but then creates all the forward ports immediately (unless
it gets a conflict)
- `fw=skipmerge` immediately creates all the forward ports, without
even waiting for the PR to be merged
This is a completely new mode, and may be rather broken as until
now the 'bot has always assumed the source PR had been merged.
approval rework
---------------
Because of the previous section, there is no distinguishing feature
between `mergebot r+` = "merge this PR" and `forwardbot r+` = "merge
this PR and all its parent with different access rights".
As a result, the two have been merged under a single `mergebot r+`
with heuristics attempting to provide the best experience:
- if approving a non-forward port, the behavior does not change
- else, with review rights on the source, all ancestors are approved
- else, as author of the original, approves all ancestors which descend
from a merged PR
- else, approves all ancestors up to and including the oldest ancestor
to which we have review rights
Most notably, the source's author is not delegated on the source or
any of its descendants anymore. This might need to be revisited if it
provides too restrictive.
For the very specialized need of approving a forward-port *and none of
its ancestors*, `review=` can now take a comma (`,`) separated list of
pull request numbers (github numbers, not mergebot ids).
Computed State
==============
The `state` field of pull requests is now computed. Hopefully this
makes the status more consistent and predictable in the long run, and
importantly makes status management more reliable (because reference
datum get updated naturally flowing to the state).
For now however it makes things more complicated as some of the states
have to be separately signaled or updated:
- `closed` and `error` are now separate flags
- `merge_date` is pulled down from forwardport and becomes the
transition signal for ready -> merged
- `reviewed_by` becomes the transition signal for approval (might be a
good idea to rename it...)
- `status` is computed from the head's statuses and overrides, and
*that* becomes the validation state
Ideally, batch-level flags like `skipchecks` should be on, well, the
batch, and `state` should have a dependency on the batch. However
currently the batch is not a durable / permanent member of the system,
so it's a PR-level flag and a messy pile.
On notable change is that *forcing* the state to `ready` now does that
but also sets the reviewer, `skipchecks`, and overrides to ensure the
API-mediated readying does not get rolled back by e.g. the runbot
sending a status.
This is useful for a few types of automated / programmatic PRs
e.g. translation exports, where we set the state programmatically to
limit noise.
recursive dependency hack
-------------------------
Given a sequence of PRs with an override of the source, if one of the
PRs is updated its descendants should not have the override
anymore. However if the updated PR gets overridden, its descendants
should have *that* override.
This requires some unholy manipulations via an override of `modified`,
as the ORM supports recursive fields but not recursive
dependencies (on a different field).
unconditional followup scheduling
---------------------------------
Previously scheduling forward-port followup was contigent on the FW
policy, but it's not actually correct if the new PR is *immediately*
validated (which can happen now that the field is computed, if there
are no required statuses *or* all of the required statuses are
overridden by an ancestor) as nothing will trigger the state change
and thus scheduling of the fp followup.
The followup function checks all the properties of the batch to port,
so this should not result on incorrect ports. Although it's a bit more
expensive, and will lead to more spam.
Previously this would not happen because on creation of a PR the
validation task (commit -> PR) would still have to execute.
Misc Changes
============
- If a PR is marked as overriding / canceling stagings, it now does
so on retry not just when setting initially.
This was not handled at all previously, so a PR in P0 going into
error due to e.g. a non-deterministic bug would be retried and still
p=0, but a current staging would not get cancelled. Same when a PR
in p=0 goes into error because something was failed, then is updated
with a fix.
- Add tracking to a bunch of relevant PR fields.
Post-mortem analysis currently generally requires going through the
text logs to see what happened, which is annoying.
There is a nondeterminism / inconsistency in the tracking which
sometimes leads the admin user to trigger tracking before the bot
does, leading to the staging tracking being attributed to them
during tests, shove under the carpet by ignoring the user to whom
that tracking is attributed.
When multiple users update tracked fields in the same transaction
all the changes are attributed to the first one having triggered
tracking (?), I couldn't find why the admin sometimes takes over.
- added and leveraged support for enum-backed selection fields
- moved variuous fields from forwardport to runbot_merge
- fix a migration which had never worked and which never run (because
I forgot to bump the version on the module)
- remove some unnecessary intermediate de/serialisation
fixes #673, fixes #309, fixes #792, fixes #846 (probably)
2023-10-31 13:42:07 +07:00
|
|
|
# stage current PRs
|
2019-10-10 14:22:12 +07:00
|
|
|
env.run_crons()
|
[CHG] *: rewrite commands set, rework status management
This commit revisits the commands set in order to make it more
regular, and limit inconsistent command-sets, although it includes
pseudo-command aliases for common tasks now removed from the core set.
Hard Errors
===========
The previous iteration of the commands set would ignore any
non-command term in a command line. This has been changed to hard
error (and ignoring the entire thing) if any command is unknown or
invalid.
This fixes inconsistent / unexpected interpretations where a user
sends a command, then writes a novel on the same line some words of
which happen to *also* be commands, leading to merge states they did
not expect. They should now be told to fuck off.
Priority Restructuring
----------------------
The numerical priority system was pretty messy in that it confused
"staging priority" (in ways which were not entirely straightforward)
with overrides to other concerns.
This has now being split along all the axis, with separate command
subsets for:
- staging prioritisation, now separated between `default`, `priority`,
and `alone`,
- `default` means PRs are picked by an unspecified order when
creating a staging, if nothing better is available
- `priority` means PRs are picked first when staging, however if
`priority` PRs don't fill the staging the rest will be filled with
`default`, this mode did not previously exist
- `alone` means the PRs are picked first, before splits, and only
`alone` PRs can be part of the staging (which usually matches the
modename)
- `skipchecks` overrides both statuses and approval checks, for the
batch, something previously implied in `p=0`, but now
independent. Setting `skipchecks` basically makes the entire batch
`ready`.
For consistency this also sets the reviewer implicitly: since
skipchecks overrides both statuses *and approval*, whoever enables
this mode is essentially the reviewer.
- `cancel` cancels any ongoing staging when the marked PR becomes
ready again, previously this was also implied (in a more restricted
form) by setting `p=0`
FWBot removal
=============
While the "forwardport bot" still exists as an API level (to segregate
access rights between tokens) it has been removed as an interaction
point, as part of the modules merge plan. As a result,
fwbot stops responding
----------------------
Feedback messages are now always sent by the mergebot, the
forward-porting bot should not send any message or notification
anymore.
commands moved to the merge bot
-------------------------------
- `ignore`/`up to` simply changes bot
- `close` as well
- `skipci` is now a choice / flag of an `fw` command, which denotes
the forward-port policy,
- `fw=default` is the old `ci` and resets the policy to default,
that is wait for the PR to be merged to create forward ports, and
for the required statuses on each forward port to be received
before creating the next
- `fw=skipci` is the old `skipci`, it waits for the merge of the
base PR but then creates all the forward ports immediately (unless
it gets a conflict)
- `fw=skipmerge` immediately creates all the forward ports, without
even waiting for the PR to be merged
This is a completely new mode, and may be rather broken as until
now the 'bot has always assumed the source PR had been merged.
approval rework
---------------
Because of the previous section, there is no distinguishing feature
between `mergebot r+` = "merge this PR" and `forwardbot r+` = "merge
this PR and all its parent with different access rights".
As a result, the two have been merged under a single `mergebot r+`
with heuristics attempting to provide the best experience:
- if approving a non-forward port, the behavior does not change
- else, with review rights on the source, all ancestors are approved
- else, as author of the original, approves all ancestors which descend
from a merged PR
- else, approves all ancestors up to and including the oldest ancestor
to which we have review rights
Most notably, the source's author is not delegated on the source or
any of its descendants anymore. This might need to be revisited if it
provides too restrictive.
For the very specialized need of approving a forward-port *and none of
its ancestors*, `review=` can now take a comma (`,`) separated list of
pull request numbers (github numbers, not mergebot ids).
Computed State
==============
The `state` field of pull requests is now computed. Hopefully this
makes the status more consistent and predictable in the long run, and
importantly makes status management more reliable (because reference
datum get updated naturally flowing to the state).
For now however it makes things more complicated as some of the states
have to be separately signaled or updated:
- `closed` and `error` are now separate flags
- `merge_date` is pulled down from forwardport and becomes the
transition signal for ready -> merged
- `reviewed_by` becomes the transition signal for approval (might be a
good idea to rename it...)
- `status` is computed from the head's statuses and overrides, and
*that* becomes the validation state
Ideally, batch-level flags like `skipchecks` should be on, well, the
batch, and `state` should have a dependency on the batch. However
currently the batch is not a durable / permanent member of the system,
so it's a PR-level flag and a messy pile.
On notable change is that *forcing* the state to `ready` now does that
but also sets the reviewer, `skipchecks`, and overrides to ensure the
API-mediated readying does not get rolled back by e.g. the runbot
sending a status.
This is useful for a few types of automated / programmatic PRs
e.g. translation exports, where we set the state programmatically to
limit noise.
recursive dependency hack
-------------------------
Given a sequence of PRs with an override of the source, if one of the
PRs is updated its descendants should not have the override
anymore. However if the updated PR gets overridden, its descendants
should have *that* override.
This requires some unholy manipulations via an override of `modified`,
as the ORM supports recursive fields but not recursive
dependencies (on a different field).
unconditional followup scheduling
---------------------------------
Previously scheduling forward-port followup was contigent on the FW
policy, but it's not actually correct if the new PR is *immediately*
validated (which can happen now that the field is computed, if there
are no required statuses *or* all of the required statuses are
overridden by an ancestor) as nothing will trigger the state change
and thus scheduling of the fp followup.
The followup function checks all the properties of the batch to port,
so this should not result on incorrect ports. Although it's a bit more
expensive, and will lead to more spam.
Previously this would not happen because on creation of a PR the
validation task (commit -> PR) would still have to execute.
Misc Changes
============
- If a PR is marked as overriding / canceling stagings, it now does
so on retry not just when setting initially.
This was not handled at all previously, so a PR in P0 going into
error due to e.g. a non-deterministic bug would be retried and still
p=0, but a current staging would not get cancelled. Same when a PR
in p=0 goes into error because something was failed, then is updated
with a fix.
- Add tracking to a bunch of relevant PR fields.
Post-mortem analysis currently generally requires going through the
text logs to see what happened, which is annoying.
There is a nondeterminism / inconsistency in the tracking which
sometimes leads the admin user to trigger tracking before the bot
does, leading to the staging tracking being attributed to them
during tests, shove under the carpet by ignoring the user to whom
that tracking is attributed.
When multiple users update tracked fields in the same transaction
all the changes are attributed to the first one having triggered
tracking (?), I couldn't find why the admin sometimes takes over.
- added and leveraged support for enum-backed selection fields
- moved variuous fields from forwardport to runbot_merge
- fix a migration which had never worked and which never run (because
I forgot to bump the version on the module)
- remove some unnecessary intermediate de/serialisation
fixes #673, fixes #309, fixes #792, fixes #846 (probably)
2023-10-31 13:42:07 +07:00
|
|
|
p_11, p_12 = \
|
|
|
|
[to_pr(env, pr) for pr in [pr11, pr12]]
|
|
|
|
sm_all = p_11 | p_12
|
|
|
|
staging_1 = sm_all.staging_id
|
|
|
|
assert staging_1
|
|
|
|
assert len(staging_1) == 1
|
2024-05-17 15:51:16 +07:00
|
|
|
assert list(staging_1.batch_ids) == [
|
|
|
|
p_11.batch_id,
|
|
|
|
p_12.batch_id,
|
|
|
|
]
|
2018-03-27 18:33:04 +07:00
|
|
|
|
|
|
|
# no statuses run on PR0s
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
pr01 = self._pr(repo, 'Urgent1', [{'n': 'n'}, {'o': 'o'}], user=config['role_user']['token'], reviewer=None, statuses=[])
|
[CHG] *: rewrite commands set, rework status management
This commit revisits the commands set in order to make it more
regular, and limit inconsistent command-sets, although it includes
pseudo-command aliases for common tasks now removed from the core set.
Hard Errors
===========
The previous iteration of the commands set would ignore any
non-command term in a command line. This has been changed to hard
error (and ignoring the entire thing) if any command is unknown or
invalid.
This fixes inconsistent / unexpected interpretations where a user
sends a command, then writes a novel on the same line some words of
which happen to *also* be commands, leading to merge states they did
not expect. They should now be told to fuck off.
Priority Restructuring
----------------------
The numerical priority system was pretty messy in that it confused
"staging priority" (in ways which were not entirely straightforward)
with overrides to other concerns.
This has now being split along all the axis, with separate command
subsets for:
- staging prioritisation, now separated between `default`, `priority`,
and `alone`,
- `default` means PRs are picked by an unspecified order when
creating a staging, if nothing better is available
- `priority` means PRs are picked first when staging, however if
`priority` PRs don't fill the staging the rest will be filled with
`default`, this mode did not previously exist
- `alone` means the PRs are picked first, before splits, and only
`alone` PRs can be part of the staging (which usually matches the
modename)
- `skipchecks` overrides both statuses and approval checks, for the
batch, something previously implied in `p=0`, but now
independent. Setting `skipchecks` basically makes the entire batch
`ready`.
For consistency this also sets the reviewer implicitly: since
skipchecks overrides both statuses *and approval*, whoever enables
this mode is essentially the reviewer.
- `cancel` cancels any ongoing staging when the marked PR becomes
ready again, previously this was also implied (in a more restricted
form) by setting `p=0`
FWBot removal
=============
While the "forwardport bot" still exists as an API level (to segregate
access rights between tokens) it has been removed as an interaction
point, as part of the modules merge plan. As a result,
fwbot stops responding
----------------------
Feedback messages are now always sent by the mergebot, the
forward-porting bot should not send any message or notification
anymore.
commands moved to the merge bot
-------------------------------
- `ignore`/`up to` simply changes bot
- `close` as well
- `skipci` is now a choice / flag of an `fw` command, which denotes
the forward-port policy,
- `fw=default` is the old `ci` and resets the policy to default,
that is wait for the PR to be merged to create forward ports, and
for the required statuses on each forward port to be received
before creating the next
- `fw=skipci` is the old `skipci`, it waits for the merge of the
base PR but then creates all the forward ports immediately (unless
it gets a conflict)
- `fw=skipmerge` immediately creates all the forward ports, without
even waiting for the PR to be merged
This is a completely new mode, and may be rather broken as until
now the 'bot has always assumed the source PR had been merged.
approval rework
---------------
Because of the previous section, there is no distinguishing feature
between `mergebot r+` = "merge this PR" and `forwardbot r+` = "merge
this PR and all its parent with different access rights".
As a result, the two have been merged under a single `mergebot r+`
with heuristics attempting to provide the best experience:
- if approving a non-forward port, the behavior does not change
- else, with review rights on the source, all ancestors are approved
- else, as author of the original, approves all ancestors which descend
from a merged PR
- else, approves all ancestors up to and including the oldest ancestor
to which we have review rights
Most notably, the source's author is not delegated on the source or
any of its descendants anymore. This might need to be revisited if it
provides too restrictive.
For the very specialized need of approving a forward-port *and none of
its ancestors*, `review=` can now take a comma (`,`) separated list of
pull request numbers (github numbers, not mergebot ids).
Computed State
==============
The `state` field of pull requests is now computed. Hopefully this
makes the status more consistent and predictable in the long run, and
importantly makes status management more reliable (because reference
datum get updated naturally flowing to the state).
For now however it makes things more complicated as some of the states
have to be separately signaled or updated:
- `closed` and `error` are now separate flags
- `merge_date` is pulled down from forwardport and becomes the
transition signal for ready -> merged
- `reviewed_by` becomes the transition signal for approval (might be a
good idea to rename it...)
- `status` is computed from the head's statuses and overrides, and
*that* becomes the validation state
Ideally, batch-level flags like `skipchecks` should be on, well, the
batch, and `state` should have a dependency on the batch. However
currently the batch is not a durable / permanent member of the system,
so it's a PR-level flag and a messy pile.
On notable change is that *forcing* the state to `ready` now does that
but also sets the reviewer, `skipchecks`, and overrides to ensure the
API-mediated readying does not get rolled back by e.g. the runbot
sending a status.
This is useful for a few types of automated / programmatic PRs
e.g. translation exports, where we set the state programmatically to
limit noise.
recursive dependency hack
-------------------------
Given a sequence of PRs with an override of the source, if one of the
PRs is updated its descendants should not have the override
anymore. However if the updated PR gets overridden, its descendants
should have *that* override.
This requires some unholy manipulations via an override of `modified`,
as the ORM supports recursive fields but not recursive
dependencies (on a different field).
unconditional followup scheduling
---------------------------------
Previously scheduling forward-port followup was contigent on the FW
policy, but it's not actually correct if the new PR is *immediately*
validated (which can happen now that the field is computed, if there
are no required statuses *or* all of the required statuses are
overridden by an ancestor) as nothing will trigger the state change
and thus scheduling of the fp followup.
The followup function checks all the properties of the batch to port,
so this should not result on incorrect ports. Although it's a bit more
expensive, and will lead to more spam.
Previously this would not happen because on creation of a PR the
validation task (commit -> PR) would still have to execute.
Misc Changes
============
- If a PR is marked as overriding / canceling stagings, it now does
so on retry not just when setting initially.
This was not handled at all previously, so a PR in P0 going into
error due to e.g. a non-deterministic bug would be retried and still
p=0, but a current staging would not get cancelled. Same when a PR
in p=0 goes into error because something was failed, then is updated
with a fix.
- Add tracking to a bunch of relevant PR fields.
Post-mortem analysis currently generally requires going through the
text logs to see what happened, which is annoying.
There is a nondeterminism / inconsistency in the tracking which
sometimes leads the admin user to trigger tracking before the bot
does, leading to the staging tracking being attributed to them
during tests, shove under the carpet by ignoring the user to whom
that tracking is attributed.
When multiple users update tracked fields in the same transaction
all the changes are attributed to the first one having triggered
tracking (?), I couldn't find why the admin sometimes takes over.
- added and leveraged support for enum-backed selection fields
- moved variuous fields from forwardport to runbot_merge
- fix a migration which had never worked and which never run (because
I forgot to bump the version on the module)
- remove some unnecessary intermediate de/serialisation
fixes #673, fixes #309, fixes #792, fixes #846 (probably)
2023-10-31 13:42:07 +07:00
|
|
|
pr01.post_comment('hansen NOW! rebase-merge', config['role_reviewer']['token'])
|
2021-08-09 12:55:38 +07:00
|
|
|
p_01 = to_pr(env, pr01)
|
2024-05-24 13:36:19 +07:00
|
|
|
assert p_01.state == 'ready'
|
[CHG] *: rewrite commands set, rework status management
This commit revisits the commands set in order to make it more
regular, and limit inconsistent command-sets, although it includes
pseudo-command aliases for common tasks now removed from the core set.
Hard Errors
===========
The previous iteration of the commands set would ignore any
non-command term in a command line. This has been changed to hard
error (and ignoring the entire thing) if any command is unknown or
invalid.
This fixes inconsistent / unexpected interpretations where a user
sends a command, then writes a novel on the same line some words of
which happen to *also* be commands, leading to merge states they did
not expect. They should now be told to fuck off.
Priority Restructuring
----------------------
The numerical priority system was pretty messy in that it confused
"staging priority" (in ways which were not entirely straightforward)
with overrides to other concerns.
This has now being split along all the axis, with separate command
subsets for:
- staging prioritisation, now separated between `default`, `priority`,
and `alone`,
- `default` means PRs are picked by an unspecified order when
creating a staging, if nothing better is available
- `priority` means PRs are picked first when staging, however if
`priority` PRs don't fill the staging the rest will be filled with
`default`, this mode did not previously exist
- `alone` means the PRs are picked first, before splits, and only
`alone` PRs can be part of the staging (which usually matches the
modename)
- `skipchecks` overrides both statuses and approval checks, for the
batch, something previously implied in `p=0`, but now
independent. Setting `skipchecks` basically makes the entire batch
`ready`.
For consistency this also sets the reviewer implicitly: since
skipchecks overrides both statuses *and approval*, whoever enables
this mode is essentially the reviewer.
- `cancel` cancels any ongoing staging when the marked PR becomes
ready again, previously this was also implied (in a more restricted
form) by setting `p=0`
FWBot removal
=============
While the "forwardport bot" still exists as an API level (to segregate
access rights between tokens) it has been removed as an interaction
point, as part of the modules merge plan. As a result,
fwbot stops responding
----------------------
Feedback messages are now always sent by the mergebot, the
forward-porting bot should not send any message or notification
anymore.
commands moved to the merge bot
-------------------------------
- `ignore`/`up to` simply changes bot
- `close` as well
- `skipci` is now a choice / flag of an `fw` command, which denotes
the forward-port policy,
- `fw=default` is the old `ci` and resets the policy to default,
that is wait for the PR to be merged to create forward ports, and
for the required statuses on each forward port to be received
before creating the next
- `fw=skipci` is the old `skipci`, it waits for the merge of the
base PR but then creates all the forward ports immediately (unless
it gets a conflict)
- `fw=skipmerge` immediately creates all the forward ports, without
even waiting for the PR to be merged
This is a completely new mode, and may be rather broken as until
now the 'bot has always assumed the source PR had been merged.
approval rework
---------------
Because of the previous section, there is no distinguishing feature
between `mergebot r+` = "merge this PR" and `forwardbot r+` = "merge
this PR and all its parent with different access rights".
As a result, the two have been merged under a single `mergebot r+`
with heuristics attempting to provide the best experience:
- if approving a non-forward port, the behavior does not change
- else, with review rights on the source, all ancestors are approved
- else, as author of the original, approves all ancestors which descend
from a merged PR
- else, approves all ancestors up to and including the oldest ancestor
to which we have review rights
Most notably, the source's author is not delegated on the source or
any of its descendants anymore. This might need to be revisited if it
provides too restrictive.
For the very specialized need of approving a forward-port *and none of
its ancestors*, `review=` can now take a comma (`,`) separated list of
pull request numbers (github numbers, not mergebot ids).
Computed State
==============
The `state` field of pull requests is now computed. Hopefully this
makes the status more consistent and predictable in the long run, and
importantly makes status management more reliable (because reference
datum get updated naturally flowing to the state).
For now however it makes things more complicated as some of the states
have to be separately signaled or updated:
- `closed` and `error` are now separate flags
- `merge_date` is pulled down from forwardport and becomes the
transition signal for ready -> merged
- `reviewed_by` becomes the transition signal for approval (might be a
good idea to rename it...)
- `status` is computed from the head's statuses and overrides, and
*that* becomes the validation state
Ideally, batch-level flags like `skipchecks` should be on, well, the
batch, and `state` should have a dependency on the batch. However
currently the batch is not a durable / permanent member of the system,
so it's a PR-level flag and a messy pile.
On notable change is that *forcing* the state to `ready` now does that
but also sets the reviewer, `skipchecks`, and overrides to ensure the
API-mediated readying does not get rolled back by e.g. the runbot
sending a status.
This is useful for a few types of automated / programmatic PRs
e.g. translation exports, where we set the state programmatically to
limit noise.
recursive dependency hack
-------------------------
Given a sequence of PRs with an override of the source, if one of the
PRs is updated its descendants should not have the override
anymore. However if the updated PR gets overridden, its descendants
should have *that* override.
This requires some unholy manipulations via an override of `modified`,
as the ORM supports recursive fields but not recursive
dependencies (on a different field).
unconditional followup scheduling
---------------------------------
Previously scheduling forward-port followup was contigent on the FW
policy, but it's not actually correct if the new PR is *immediately*
validated (which can happen now that the field is computed, if there
are no required statuses *or* all of the required statuses are
overridden by an ancestor) as nothing will trigger the state change
and thus scheduling of the fp followup.
The followup function checks all the properties of the batch to port,
so this should not result on incorrect ports. Although it's a bit more
expensive, and will lead to more spam.
Previously this would not happen because on creation of a PR the
validation task (commit -> PR) would still have to execute.
Misc Changes
============
- If a PR is marked as overriding / canceling stagings, it now does
so on retry not just when setting initially.
This was not handled at all previously, so a PR in P0 going into
error due to e.g. a non-deterministic bug would be retried and still
p=0, but a current staging would not get cancelled. Same when a PR
in p=0 goes into error because something was failed, then is updated
with a fix.
- Add tracking to a bunch of relevant PR fields.
Post-mortem analysis currently generally requires going through the
text logs to see what happened, which is annoying.
There is a nondeterminism / inconsistency in the tracking which
sometimes leads the admin user to trigger tracking before the bot
does, leading to the staging tracking being attributed to them
during tests, shove under the carpet by ignoring the user to whom
that tracking is attributed.
When multiple users update tracked fields in the same transaction
all the changes are attributed to the first one having triggered
tracking (?), I couldn't find why the admin sometimes takes over.
- added and leveraged support for enum-backed selection fields
- moved variuous fields from forwardport to runbot_merge
- fix a migration which had never worked and which never run (because
I forgot to bump the version on the module)
- remove some unnecessary intermediate de/serialisation
fixes #673, fixes #309, fixes #792, fixes #846 (probably)
2023-10-31 13:42:07 +07:00
|
|
|
assert p_01.priority == 'alone'
|
|
|
|
assert p_01.skipchecks == True
|
2018-03-14 16:37:46 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
env.run_crons()
|
2018-03-27 18:33:04 +07:00
|
|
|
# first staging should be cancelled and PR0 should be staged
|
|
|
|
# regardless of CI (or lack thereof)
|
2018-06-18 17:59:57 +07:00
|
|
|
assert not staging_1.active
|
2018-03-27 18:33:04 +07:00
|
|
|
assert not p_11.staging_id and not p_12.staging_id
|
|
|
|
assert p_01.staging_id
|
2024-02-07 21:05:33 +07:00
|
|
|
assert p_11.state == 'ready'
|
|
|
|
assert p_12.state == 'ready'
|
2018-03-27 18:33:04 +07:00
|
|
|
|
[CHG] *: rewrite commands set, rework status management
This commit revisits the commands set in order to make it more
regular, and limit inconsistent command-sets, although it includes
pseudo-command aliases for common tasks now removed from the core set.
Hard Errors
===========
The previous iteration of the commands set would ignore any
non-command term in a command line. This has been changed to hard
error (and ignoring the entire thing) if any command is unknown or
invalid.
This fixes inconsistent / unexpected interpretations where a user
sends a command, then writes a novel on the same line some words of
which happen to *also* be commands, leading to merge states they did
not expect. They should now be told to fuck off.
Priority Restructuring
----------------------
The numerical priority system was pretty messy in that it confused
"staging priority" (in ways which were not entirely straightforward)
with overrides to other concerns.
This has now being split along all the axis, with separate command
subsets for:
- staging prioritisation, now separated between `default`, `priority`,
and `alone`,
- `default` means PRs are picked by an unspecified order when
creating a staging, if nothing better is available
- `priority` means PRs are picked first when staging, however if
`priority` PRs don't fill the staging the rest will be filled with
`default`, this mode did not previously exist
- `alone` means the PRs are picked first, before splits, and only
`alone` PRs can be part of the staging (which usually matches the
modename)
- `skipchecks` overrides both statuses and approval checks, for the
batch, something previously implied in `p=0`, but now
independent. Setting `skipchecks` basically makes the entire batch
`ready`.
For consistency this also sets the reviewer implicitly: since
skipchecks overrides both statuses *and approval*, whoever enables
this mode is essentially the reviewer.
- `cancel` cancels any ongoing staging when the marked PR becomes
ready again, previously this was also implied (in a more restricted
form) by setting `p=0`
FWBot removal
=============
While the "forwardport bot" still exists as an API level (to segregate
access rights between tokens) it has been removed as an interaction
point, as part of the modules merge plan. As a result,
fwbot stops responding
----------------------
Feedback messages are now always sent by the mergebot, the
forward-porting bot should not send any message or notification
anymore.
commands moved to the merge bot
-------------------------------
- `ignore`/`up to` simply changes bot
- `close` as well
- `skipci` is now a choice / flag of an `fw` command, which denotes
the forward-port policy,
- `fw=default` is the old `ci` and resets the policy to default,
that is wait for the PR to be merged to create forward ports, and
for the required statuses on each forward port to be received
before creating the next
- `fw=skipci` is the old `skipci`, it waits for the merge of the
base PR but then creates all the forward ports immediately (unless
it gets a conflict)
- `fw=skipmerge` immediately creates all the forward ports, without
even waiting for the PR to be merged
This is a completely new mode, and may be rather broken as until
now the 'bot has always assumed the source PR had been merged.
approval rework
---------------
Because of the previous section, there is no distinguishing feature
between `mergebot r+` = "merge this PR" and `forwardbot r+` = "merge
this PR and all its parent with different access rights".
As a result, the two have been merged under a single `mergebot r+`
with heuristics attempting to provide the best experience:
- if approving a non-forward port, the behavior does not change
- else, with review rights on the source, all ancestors are approved
- else, as author of the original, approves all ancestors which descend
from a merged PR
- else, approves all ancestors up to and including the oldest ancestor
to which we have review rights
Most notably, the source's author is not delegated on the source or
any of its descendants anymore. This might need to be revisited if it
provides too restrictive.
For the very specialized need of approving a forward-port *and none of
its ancestors*, `review=` can now take a comma (`,`) separated list of
pull request numbers (github numbers, not mergebot ids).
Computed State
==============
The `state` field of pull requests is now computed. Hopefully this
makes the status more consistent and predictable in the long run, and
importantly makes status management more reliable (because reference
datum get updated naturally flowing to the state).
For now however it makes things more complicated as some of the states
have to be separately signaled or updated:
- `closed` and `error` are now separate flags
- `merge_date` is pulled down from forwardport and becomes the
transition signal for ready -> merged
- `reviewed_by` becomes the transition signal for approval (might be a
good idea to rename it...)
- `status` is computed from the head's statuses and overrides, and
*that* becomes the validation state
Ideally, batch-level flags like `skipchecks` should be on, well, the
batch, and `state` should have a dependency on the batch. However
currently the batch is not a durable / permanent member of the system,
so it's a PR-level flag and a messy pile.
On notable change is that *forcing* the state to `ready` now does that
but also sets the reviewer, `skipchecks`, and overrides to ensure the
API-mediated readying does not get rolled back by e.g. the runbot
sending a status.
This is useful for a few types of automated / programmatic PRs
e.g. translation exports, where we set the state programmatically to
limit noise.
recursive dependency hack
-------------------------
Given a sequence of PRs with an override of the source, if one of the
PRs is updated its descendants should not have the override
anymore. However if the updated PR gets overridden, its descendants
should have *that* override.
This requires some unholy manipulations via an override of `modified`,
as the ORM supports recursive fields but not recursive
dependencies (on a different field).
unconditional followup scheduling
---------------------------------
Previously scheduling forward-port followup was contigent on the FW
policy, but it's not actually correct if the new PR is *immediately*
validated (which can happen now that the field is computed, if there
are no required statuses *or* all of the required statuses are
overridden by an ancestor) as nothing will trigger the state change
and thus scheduling of the fp followup.
The followup function checks all the properties of the batch to port,
so this should not result on incorrect ports. Although it's a bit more
expensive, and will lead to more spam.
Previously this would not happen because on creation of a PR the
validation task (commit -> PR) would still have to execute.
Misc Changes
============
- If a PR is marked as overriding / canceling stagings, it now does
so on retry not just when setting initially.
This was not handled at all previously, so a PR in P0 going into
error due to e.g. a non-deterministic bug would be retried and still
p=0, but a current staging would not get cancelled. Same when a PR
in p=0 goes into error because something was failed, then is updated
with a fix.
- Add tracking to a bunch of relevant PR fields.
Post-mortem analysis currently generally requires going through the
text logs to see what happened, which is annoying.
There is a nondeterminism / inconsistency in the tracking which
sometimes leads the admin user to trigger tracking before the bot
does, leading to the staging tracking being attributed to them
during tests, shove under the carpet by ignoring the user to whom
that tracking is attributed.
When multiple users update tracked fields in the same transaction
all the changes are attributed to the first one having triggered
tracking (?), I couldn't find why the admin sometimes takes over.
- added and leveraged support for enum-backed selection fields
- moved variuous fields from forwardport to runbot_merge
- fix a migration which had never worked and which never run (because
I forgot to bump the version on the module)
- remove some unnecessary intermediate de/serialisation
fixes #673, fixes #309, fixes #792, fixes #846 (probably)
2023-10-31 13:42:07 +07:00
|
|
|
# make the staging fail
|
|
|
|
with repo:
|
|
|
|
repo.post_status('staging.master', 'failure', 'ci/runbot')
|
|
|
|
env.run_crons()
|
2024-02-07 21:05:33 +07:00
|
|
|
assert p_01.error
|
|
|
|
assert p_01.batch_id.blocked
|
|
|
|
assert p_01.blocked
|
|
|
|
|
[CHG] *: rewrite commands set, rework status management
This commit revisits the commands set in order to make it more
regular, and limit inconsistent command-sets, although it includes
pseudo-command aliases for common tasks now removed from the core set.
Hard Errors
===========
The previous iteration of the commands set would ignore any
non-command term in a command line. This has been changed to hard
error (and ignoring the entire thing) if any command is unknown or
invalid.
This fixes inconsistent / unexpected interpretations where a user
sends a command, then writes a novel on the same line some words of
which happen to *also* be commands, leading to merge states they did
not expect. They should now be told to fuck off.
Priority Restructuring
----------------------
The numerical priority system was pretty messy in that it confused
"staging priority" (in ways which were not entirely straightforward)
with overrides to other concerns.
This has now being split along all the axis, with separate command
subsets for:
- staging prioritisation, now separated between `default`, `priority`,
and `alone`,
- `default` means PRs are picked by an unspecified order when
creating a staging, if nothing better is available
- `priority` means PRs are picked first when staging, however if
`priority` PRs don't fill the staging the rest will be filled with
`default`, this mode did not previously exist
- `alone` means the PRs are picked first, before splits, and only
`alone` PRs can be part of the staging (which usually matches the
modename)
- `skipchecks` overrides both statuses and approval checks, for the
batch, something previously implied in `p=0`, but now
independent. Setting `skipchecks` basically makes the entire batch
`ready`.
For consistency this also sets the reviewer implicitly: since
skipchecks overrides both statuses *and approval*, whoever enables
this mode is essentially the reviewer.
- `cancel` cancels any ongoing staging when the marked PR becomes
ready again, previously this was also implied (in a more restricted
form) by setting `p=0`
FWBot removal
=============
While the "forwardport bot" still exists as an API level (to segregate
access rights between tokens) it has been removed as an interaction
point, as part of the modules merge plan. As a result,
fwbot stops responding
----------------------
Feedback messages are now always sent by the mergebot, the
forward-porting bot should not send any message or notification
anymore.
commands moved to the merge bot
-------------------------------
- `ignore`/`up to` simply changes bot
- `close` as well
- `skipci` is now a choice / flag of an `fw` command, which denotes
the forward-port policy,
- `fw=default` is the old `ci` and resets the policy to default,
that is wait for the PR to be merged to create forward ports, and
for the required statuses on each forward port to be received
before creating the next
- `fw=skipci` is the old `skipci`, it waits for the merge of the
base PR but then creates all the forward ports immediately (unless
it gets a conflict)
- `fw=skipmerge` immediately creates all the forward ports, without
even waiting for the PR to be merged
This is a completely new mode, and may be rather broken as until
now the 'bot has always assumed the source PR had been merged.
approval rework
---------------
Because of the previous section, there is no distinguishing feature
between `mergebot r+` = "merge this PR" and `forwardbot r+` = "merge
this PR and all its parent with different access rights".
As a result, the two have been merged under a single `mergebot r+`
with heuristics attempting to provide the best experience:
- if approving a non-forward port, the behavior does not change
- else, with review rights on the source, all ancestors are approved
- else, as author of the original, approves all ancestors which descend
from a merged PR
- else, approves all ancestors up to and including the oldest ancestor
to which we have review rights
Most notably, the source's author is not delegated on the source or
any of its descendants anymore. This might need to be revisited if it
provides too restrictive.
For the very specialized need of approving a forward-port *and none of
its ancestors*, `review=` can now take a comma (`,`) separated list of
pull request numbers (github numbers, not mergebot ids).
Computed State
==============
The `state` field of pull requests is now computed. Hopefully this
makes the status more consistent and predictable in the long run, and
importantly makes status management more reliable (because reference
datum get updated naturally flowing to the state).
For now however it makes things more complicated as some of the states
have to be separately signaled or updated:
- `closed` and `error` are now separate flags
- `merge_date` is pulled down from forwardport and becomes the
transition signal for ready -> merged
- `reviewed_by` becomes the transition signal for approval (might be a
good idea to rename it...)
- `status` is computed from the head's statuses and overrides, and
*that* becomes the validation state
Ideally, batch-level flags like `skipchecks` should be on, well, the
batch, and `state` should have a dependency on the batch. However
currently the batch is not a durable / permanent member of the system,
so it's a PR-level flag and a messy pile.
On notable change is that *forcing* the state to `ready` now does that
but also sets the reviewer, `skipchecks`, and overrides to ensure the
API-mediated readying does not get rolled back by e.g. the runbot
sending a status.
This is useful for a few types of automated / programmatic PRs
e.g. translation exports, where we set the state programmatically to
limit noise.
recursive dependency hack
-------------------------
Given a sequence of PRs with an override of the source, if one of the
PRs is updated its descendants should not have the override
anymore. However if the updated PR gets overridden, its descendants
should have *that* override.
This requires some unholy manipulations via an override of `modified`,
as the ORM supports recursive fields but not recursive
dependencies (on a different field).
unconditional followup scheduling
---------------------------------
Previously scheduling forward-port followup was contigent on the FW
policy, but it's not actually correct if the new PR is *immediately*
validated (which can happen now that the field is computed, if there
are no required statuses *or* all of the required statuses are
overridden by an ancestor) as nothing will trigger the state change
and thus scheduling of the fp followup.
The followup function checks all the properties of the batch to port,
so this should not result on incorrect ports. Although it's a bit more
expensive, and will lead to more spam.
Previously this would not happen because on creation of a PR the
validation task (commit -> PR) would still have to execute.
Misc Changes
============
- If a PR is marked as overriding / canceling stagings, it now does
so on retry not just when setting initially.
This was not handled at all previously, so a PR in P0 going into
error due to e.g. a non-deterministic bug would be retried and still
p=0, but a current staging would not get cancelled. Same when a PR
in p=0 goes into error because something was failed, then is updated
with a fix.
- Add tracking to a bunch of relevant PR fields.
Post-mortem analysis currently generally requires going through the
text logs to see what happened, which is annoying.
There is a nondeterminism / inconsistency in the tracking which
sometimes leads the admin user to trigger tracking before the bot
does, leading to the staging tracking being attributed to them
during tests, shove under the carpet by ignoring the user to whom
that tracking is attributed.
When multiple users update tracked fields in the same transaction
all the changes are attributed to the first one having triggered
tracking (?), I couldn't find why the admin sometimes takes over.
- added and leveraged support for enum-backed selection fields
- moved variuous fields from forwardport to runbot_merge
- fix a migration which had never worked and which never run (because
I forgot to bump the version on the module)
- remove some unnecessary intermediate de/serialisation
fixes #673, fixes #309, fixes #792, fixes #846 (probably)
2023-10-31 13:42:07 +07:00
|
|
|
assert p_01.state == 'error'
|
|
|
|
assert not p_01.staging_id.active
|
|
|
|
staging_2 = ensure_one(sm_all.staging_id)
|
|
|
|
assert staging_2 != staging_1
|
|
|
|
|
|
|
|
with repo:
|
|
|
|
pr01.post_comment('hansen retry', config['role_reviewer']['token'])
|
|
|
|
env.run_crons()
|
|
|
|
# retry should have re-triggered cancel-staging
|
|
|
|
assert not staging_2.active
|
|
|
|
assert p_01.staging_id.active
|
|
|
|
|
|
|
|
# make the staging fail again
|
|
|
|
with repo:
|
|
|
|
repo.post_status('staging.master', 'failure', 'ci/runbot')
|
|
|
|
env.run_crons()
|
|
|
|
|
|
|
|
assert not p_01.staging_id.active
|
|
|
|
assert p_01.state == 'error'
|
|
|
|
staging_3 = ensure_one(sm_all.staging_id)
|
|
|
|
assert staging_3 != staging_2
|
|
|
|
|
|
|
|
# check that updating the PR resets it to ~ready
|
|
|
|
with repo:
|
|
|
|
repo.make_commits(
|
|
|
|
'heads/master',
|
|
|
|
Commit("urgent+", tree={'y': 'es'}),
|
|
|
|
ref="heads/Urgent1",
|
|
|
|
)
|
|
|
|
env.run_crons()
|
|
|
|
assert not staging_3.active
|
2024-05-24 13:36:19 +07:00
|
|
|
assert p_01.state == 'ready'
|
[CHG] *: rewrite commands set, rework status management
This commit revisits the commands set in order to make it more
regular, and limit inconsistent command-sets, although it includes
pseudo-command aliases for common tasks now removed from the core set.
Hard Errors
===========
The previous iteration of the commands set would ignore any
non-command term in a command line. This has been changed to hard
error (and ignoring the entire thing) if any command is unknown or
invalid.
This fixes inconsistent / unexpected interpretations where a user
sends a command, then writes a novel on the same line some words of
which happen to *also* be commands, leading to merge states they did
not expect. They should now be told to fuck off.
Priority Restructuring
----------------------
The numerical priority system was pretty messy in that it confused
"staging priority" (in ways which were not entirely straightforward)
with overrides to other concerns.
This has now being split along all the axis, with separate command
subsets for:
- staging prioritisation, now separated between `default`, `priority`,
and `alone`,
- `default` means PRs are picked by an unspecified order when
creating a staging, if nothing better is available
- `priority` means PRs are picked first when staging, however if
`priority` PRs don't fill the staging the rest will be filled with
`default`, this mode did not previously exist
- `alone` means the PRs are picked first, before splits, and only
`alone` PRs can be part of the staging (which usually matches the
modename)
- `skipchecks` overrides both statuses and approval checks, for the
batch, something previously implied in `p=0`, but now
independent. Setting `skipchecks` basically makes the entire batch
`ready`.
For consistency this also sets the reviewer implicitly: since
skipchecks overrides both statuses *and approval*, whoever enables
this mode is essentially the reviewer.
- `cancel` cancels any ongoing staging when the marked PR becomes
ready again, previously this was also implied (in a more restricted
form) by setting `p=0`
FWBot removal
=============
While the "forwardport bot" still exists as an API level (to segregate
access rights between tokens) it has been removed as an interaction
point, as part of the modules merge plan. As a result,
fwbot stops responding
----------------------
Feedback messages are now always sent by the mergebot, the
forward-porting bot should not send any message or notification
anymore.
commands moved to the merge bot
-------------------------------
- `ignore`/`up to` simply changes bot
- `close` as well
- `skipci` is now a choice / flag of an `fw` command, which denotes
the forward-port policy,
- `fw=default` is the old `ci` and resets the policy to default,
that is wait for the PR to be merged to create forward ports, and
for the required statuses on each forward port to be received
before creating the next
- `fw=skipci` is the old `skipci`, it waits for the merge of the
base PR but then creates all the forward ports immediately (unless
it gets a conflict)
- `fw=skipmerge` immediately creates all the forward ports, without
even waiting for the PR to be merged
This is a completely new mode, and may be rather broken as until
now the 'bot has always assumed the source PR had been merged.
approval rework
---------------
Because of the previous section, there is no distinguishing feature
between `mergebot r+` = "merge this PR" and `forwardbot r+` = "merge
this PR and all its parent with different access rights".
As a result, the two have been merged under a single `mergebot r+`
with heuristics attempting to provide the best experience:
- if approving a non-forward port, the behavior does not change
- else, with review rights on the source, all ancestors are approved
- else, as author of the original, approves all ancestors which descend
from a merged PR
- else, approves all ancestors up to and including the oldest ancestor
to which we have review rights
Most notably, the source's author is not delegated on the source or
any of its descendants anymore. This might need to be revisited if it
provides too restrictive.
For the very specialized need of approving a forward-port *and none of
its ancestors*, `review=` can now take a comma (`,`) separated list of
pull request numbers (github numbers, not mergebot ids).
Computed State
==============
The `state` field of pull requests is now computed. Hopefully this
makes the status more consistent and predictable in the long run, and
importantly makes status management more reliable (because reference
datum get updated naturally flowing to the state).
For now however it makes things more complicated as some of the states
have to be separately signaled or updated:
- `closed` and `error` are now separate flags
- `merge_date` is pulled down from forwardport and becomes the
transition signal for ready -> merged
- `reviewed_by` becomes the transition signal for approval (might be a
good idea to rename it...)
- `status` is computed from the head's statuses and overrides, and
*that* becomes the validation state
Ideally, batch-level flags like `skipchecks` should be on, well, the
batch, and `state` should have a dependency on the batch. However
currently the batch is not a durable / permanent member of the system,
so it's a PR-level flag and a messy pile.
On notable change is that *forcing* the state to `ready` now does that
but also sets the reviewer, `skipchecks`, and overrides to ensure the
API-mediated readying does not get rolled back by e.g. the runbot
sending a status.
This is useful for a few types of automated / programmatic PRs
e.g. translation exports, where we set the state programmatically to
limit noise.
recursive dependency hack
-------------------------
Given a sequence of PRs with an override of the source, if one of the
PRs is updated its descendants should not have the override
anymore. However if the updated PR gets overridden, its descendants
should have *that* override.
This requires some unholy manipulations via an override of `modified`,
as the ORM supports recursive fields but not recursive
dependencies (on a different field).
unconditional followup scheduling
---------------------------------
Previously scheduling forward-port followup was contigent on the FW
policy, but it's not actually correct if the new PR is *immediately*
validated (which can happen now that the field is computed, if there
are no required statuses *or* all of the required statuses are
overridden by an ancestor) as nothing will trigger the state change
and thus scheduling of the fp followup.
The followup function checks all the properties of the batch to port,
so this should not result on incorrect ports. Although it's a bit more
expensive, and will lead to more spam.
Previously this would not happen because on creation of a PR the
validation task (commit -> PR) would still have to execute.
Misc Changes
============
- If a PR is marked as overriding / canceling stagings, it now does
so on retry not just when setting initially.
This was not handled at all previously, so a PR in P0 going into
error due to e.g. a non-deterministic bug would be retried and still
p=0, but a current staging would not get cancelled. Same when a PR
in p=0 goes into error because something was failed, then is updated
with a fix.
- Add tracking to a bunch of relevant PR fields.
Post-mortem analysis currently generally requires going through the
text logs to see what happened, which is annoying.
There is a nondeterminism / inconsistency in the tracking which
sometimes leads the admin user to trigger tracking before the bot
does, leading to the staging tracking being attributed to them
during tests, shove under the carpet by ignoring the user to whom
that tracking is attributed.
When multiple users update tracked fields in the same transaction
all the changes are attributed to the first one having triggered
tracking (?), I couldn't find why the admin sometimes takes over.
- added and leveraged support for enum-backed selection fields
- moved variuous fields from forwardport to runbot_merge
- fix a migration which had never worked and which never run (because
I forgot to bump the version on the module)
- remove some unnecessary intermediate de/serialisation
fixes #673, fixes #309, fixes #792, fixes #846 (probably)
2023-10-31 13:42:07 +07:00
|
|
|
assert p_01.priority == 'alone'
|
|
|
|
assert p_01.skipchecks == True
|
|
|
|
assert p_01.staging_id.active
|
|
|
|
|
|
|
|
# r- should unstage, re-enable the checks and switch off staging
|
|
|
|
# cancellation, but leave the priority
|
|
|
|
with repo:
|
|
|
|
pr01.post_comment("hansen r-", config['role_reviewer']['token'])
|
|
|
|
env.run_crons()
|
|
|
|
|
|
|
|
staging_4 = ensure_one(sm_all.staging_id)
|
|
|
|
assert staging_4 != staging_3
|
|
|
|
|
|
|
|
assert not p_01.staging_id.active
|
|
|
|
assert p_01.state == 'opened'
|
|
|
|
assert p_01.priority == 'alone'
|
|
|
|
assert p_01.skipchecks == False
|
2024-02-12 14:46:44 +07:00
|
|
|
assert p_01.cancel_staging == True
|
[CHG] *: rewrite commands set, rework status management
This commit revisits the commands set in order to make it more
regular, and limit inconsistent command-sets, although it includes
pseudo-command aliases for common tasks now removed from the core set.
Hard Errors
===========
The previous iteration of the commands set would ignore any
non-command term in a command line. This has been changed to hard
error (and ignoring the entire thing) if any command is unknown or
invalid.
This fixes inconsistent / unexpected interpretations where a user
sends a command, then writes a novel on the same line some words of
which happen to *also* be commands, leading to merge states they did
not expect. They should now be told to fuck off.
Priority Restructuring
----------------------
The numerical priority system was pretty messy in that it confused
"staging priority" (in ways which were not entirely straightforward)
with overrides to other concerns.
This has now being split along all the axis, with separate command
subsets for:
- staging prioritisation, now separated between `default`, `priority`,
and `alone`,
- `default` means PRs are picked by an unspecified order when
creating a staging, if nothing better is available
- `priority` means PRs are picked first when staging, however if
`priority` PRs don't fill the staging the rest will be filled with
`default`, this mode did not previously exist
- `alone` means the PRs are picked first, before splits, and only
`alone` PRs can be part of the staging (which usually matches the
modename)
- `skipchecks` overrides both statuses and approval checks, for the
batch, something previously implied in `p=0`, but now
independent. Setting `skipchecks` basically makes the entire batch
`ready`.
For consistency this also sets the reviewer implicitly: since
skipchecks overrides both statuses *and approval*, whoever enables
this mode is essentially the reviewer.
- `cancel` cancels any ongoing staging when the marked PR becomes
ready again, previously this was also implied (in a more restricted
form) by setting `p=0`
FWBot removal
=============
While the "forwardport bot" still exists as an API level (to segregate
access rights between tokens) it has been removed as an interaction
point, as part of the modules merge plan. As a result,
fwbot stops responding
----------------------
Feedback messages are now always sent by the mergebot, the
forward-porting bot should not send any message or notification
anymore.
commands moved to the merge bot
-------------------------------
- `ignore`/`up to` simply changes bot
- `close` as well
- `skipci` is now a choice / flag of an `fw` command, which denotes
the forward-port policy,
- `fw=default` is the old `ci` and resets the policy to default,
that is wait for the PR to be merged to create forward ports, and
for the required statuses on each forward port to be received
before creating the next
- `fw=skipci` is the old `skipci`, it waits for the merge of the
base PR but then creates all the forward ports immediately (unless
it gets a conflict)
- `fw=skipmerge` immediately creates all the forward ports, without
even waiting for the PR to be merged
This is a completely new mode, and may be rather broken as until
now the 'bot has always assumed the source PR had been merged.
approval rework
---------------
Because of the previous section, there is no distinguishing feature
between `mergebot r+` = "merge this PR" and `forwardbot r+` = "merge
this PR and all its parent with different access rights".
As a result, the two have been merged under a single `mergebot r+`
with heuristics attempting to provide the best experience:
- if approving a non-forward port, the behavior does not change
- else, with review rights on the source, all ancestors are approved
- else, as author of the original, approves all ancestors which descend
from a merged PR
- else, approves all ancestors up to and including the oldest ancestor
to which we have review rights
Most notably, the source's author is not delegated on the source or
any of its descendants anymore. This might need to be revisited if it
provides too restrictive.
For the very specialized need of approving a forward-port *and none of
its ancestors*, `review=` can now take a comma (`,`) separated list of
pull request numbers (github numbers, not mergebot ids).
Computed State
==============
The `state` field of pull requests is now computed. Hopefully this
makes the status more consistent and predictable in the long run, and
importantly makes status management more reliable (because reference
datum get updated naturally flowing to the state).
For now however it makes things more complicated as some of the states
have to be separately signaled or updated:
- `closed` and `error` are now separate flags
- `merge_date` is pulled down from forwardport and becomes the
transition signal for ready -> merged
- `reviewed_by` becomes the transition signal for approval (might be a
good idea to rename it...)
- `status` is computed from the head's statuses and overrides, and
*that* becomes the validation state
Ideally, batch-level flags like `skipchecks` should be on, well, the
batch, and `state` should have a dependency on the batch. However
currently the batch is not a durable / permanent member of the system,
so it's a PR-level flag and a messy pile.
On notable change is that *forcing* the state to `ready` now does that
but also sets the reviewer, `skipchecks`, and overrides to ensure the
API-mediated readying does not get rolled back by e.g. the runbot
sending a status.
This is useful for a few types of automated / programmatic PRs
e.g. translation exports, where we set the state programmatically to
limit noise.
recursive dependency hack
-------------------------
Given a sequence of PRs with an override of the source, if one of the
PRs is updated its descendants should not have the override
anymore. However if the updated PR gets overridden, its descendants
should have *that* override.
This requires some unholy manipulations via an override of `modified`,
as the ORM supports recursive fields but not recursive
dependencies (on a different field).
unconditional followup scheduling
---------------------------------
Previously scheduling forward-port followup was contigent on the FW
policy, but it's not actually correct if the new PR is *immediately*
validated (which can happen now that the field is computed, if there
are no required statuses *or* all of the required statuses are
overridden by an ancestor) as nothing will trigger the state change
and thus scheduling of the fp followup.
The followup function checks all the properties of the batch to port,
so this should not result on incorrect ports. Although it's a bit more
expensive, and will lead to more spam.
Previously this would not happen because on creation of a PR the
validation task (commit -> PR) would still have to execute.
Misc Changes
============
- If a PR is marked as overriding / canceling stagings, it now does
so on retry not just when setting initially.
This was not handled at all previously, so a PR in P0 going into
error due to e.g. a non-deterministic bug would be retried and still
p=0, but a current staging would not get cancelled. Same when a PR
in p=0 goes into error because something was failed, then is updated
with a fix.
- Add tracking to a bunch of relevant PR fields.
Post-mortem analysis currently generally requires going through the
text logs to see what happened, which is annoying.
There is a nondeterminism / inconsistency in the tracking which
sometimes leads the admin user to trigger tracking before the bot
does, leading to the staging tracking being attributed to them
during tests, shove under the carpet by ignoring the user to whom
that tracking is attributed.
When multiple users update tracked fields in the same transaction
all the changes are attributed to the first one having triggered
tracking (?), I couldn't find why the admin sometimes takes over.
- added and leveraged support for enum-backed selection fields
- moved variuous fields from forwardport to runbot_merge
- fix a migration which had never worked and which never run (because
I forgot to bump the version on the module)
- remove some unnecessary intermediate de/serialisation
fixes #673, fixes #309, fixes #792, fixes #846 (probably)
2023-10-31 13:42:07 +07:00
|
|
|
|
2024-02-12 14:46:44 +07:00
|
|
|
assert staging_4.active, "staging should not be disabled"
|
[CHG] *: rewrite commands set, rework status management
This commit revisits the commands set in order to make it more
regular, and limit inconsistent command-sets, although it includes
pseudo-command aliases for common tasks now removed from the core set.
Hard Errors
===========
The previous iteration of the commands set would ignore any
non-command term in a command line. This has been changed to hard
error (and ignoring the entire thing) if any command is unknown or
invalid.
This fixes inconsistent / unexpected interpretations where a user
sends a command, then writes a novel on the same line some words of
which happen to *also* be commands, leading to merge states they did
not expect. They should now be told to fuck off.
Priority Restructuring
----------------------
The numerical priority system was pretty messy in that it confused
"staging priority" (in ways which were not entirely straightforward)
with overrides to other concerns.
This has now being split along all the axis, with separate command
subsets for:
- staging prioritisation, now separated between `default`, `priority`,
and `alone`,
- `default` means PRs are picked by an unspecified order when
creating a staging, if nothing better is available
- `priority` means PRs are picked first when staging, however if
`priority` PRs don't fill the staging the rest will be filled with
`default`, this mode did not previously exist
- `alone` means the PRs are picked first, before splits, and only
`alone` PRs can be part of the staging (which usually matches the
modename)
- `skipchecks` overrides both statuses and approval checks, for the
batch, something previously implied in `p=0`, but now
independent. Setting `skipchecks` basically makes the entire batch
`ready`.
For consistency this also sets the reviewer implicitly: since
skipchecks overrides both statuses *and approval*, whoever enables
this mode is essentially the reviewer.
- `cancel` cancels any ongoing staging when the marked PR becomes
ready again, previously this was also implied (in a more restricted
form) by setting `p=0`
FWBot removal
=============
While the "forwardport bot" still exists as an API level (to segregate
access rights between tokens) it has been removed as an interaction
point, as part of the modules merge plan. As a result,
fwbot stops responding
----------------------
Feedback messages are now always sent by the mergebot, the
forward-porting bot should not send any message or notification
anymore.
commands moved to the merge bot
-------------------------------
- `ignore`/`up to` simply changes bot
- `close` as well
- `skipci` is now a choice / flag of an `fw` command, which denotes
the forward-port policy,
- `fw=default` is the old `ci` and resets the policy to default,
that is wait for the PR to be merged to create forward ports, and
for the required statuses on each forward port to be received
before creating the next
- `fw=skipci` is the old `skipci`, it waits for the merge of the
base PR but then creates all the forward ports immediately (unless
it gets a conflict)
- `fw=skipmerge` immediately creates all the forward ports, without
even waiting for the PR to be merged
This is a completely new mode, and may be rather broken as until
now the 'bot has always assumed the source PR had been merged.
approval rework
---------------
Because of the previous section, there is no distinguishing feature
between `mergebot r+` = "merge this PR" and `forwardbot r+` = "merge
this PR and all its parent with different access rights".
As a result, the two have been merged under a single `mergebot r+`
with heuristics attempting to provide the best experience:
- if approving a non-forward port, the behavior does not change
- else, with review rights on the source, all ancestors are approved
- else, as author of the original, approves all ancestors which descend
from a merged PR
- else, approves all ancestors up to and including the oldest ancestor
to which we have review rights
Most notably, the source's author is not delegated on the source or
any of its descendants anymore. This might need to be revisited if it
provides too restrictive.
For the very specialized need of approving a forward-port *and none of
its ancestors*, `review=` can now take a comma (`,`) separated list of
pull request numbers (github numbers, not mergebot ids).
Computed State
==============
The `state` field of pull requests is now computed. Hopefully this
makes the status more consistent and predictable in the long run, and
importantly makes status management more reliable (because reference
datum get updated naturally flowing to the state).
For now however it makes things more complicated as some of the states
have to be separately signaled or updated:
- `closed` and `error` are now separate flags
- `merge_date` is pulled down from forwardport and becomes the
transition signal for ready -> merged
- `reviewed_by` becomes the transition signal for approval (might be a
good idea to rename it...)
- `status` is computed from the head's statuses and overrides, and
*that* becomes the validation state
Ideally, batch-level flags like `skipchecks` should be on, well, the
batch, and `state` should have a dependency on the batch. However
currently the batch is not a durable / permanent member of the system,
so it's a PR-level flag and a messy pile.
On notable change is that *forcing* the state to `ready` now does that
but also sets the reviewer, `skipchecks`, and overrides to ensure the
API-mediated readying does not get rolled back by e.g. the runbot
sending a status.
This is useful for a few types of automated / programmatic PRs
e.g. translation exports, where we set the state programmatically to
limit noise.
recursive dependency hack
-------------------------
Given a sequence of PRs with an override of the source, if one of the
PRs is updated its descendants should not have the override
anymore. However if the updated PR gets overridden, its descendants
should have *that* override.
This requires some unholy manipulations via an override of `modified`,
as the ORM supports recursive fields but not recursive
dependencies (on a different field).
unconditional followup scheduling
---------------------------------
Previously scheduling forward-port followup was contigent on the FW
policy, but it's not actually correct if the new PR is *immediately*
validated (which can happen now that the field is computed, if there
are no required statuses *or* all of the required statuses are
overridden by an ancestor) as nothing will trigger the state change
and thus scheduling of the fp followup.
The followup function checks all the properties of the batch to port,
so this should not result on incorrect ports. Although it's a bit more
expensive, and will lead to more spam.
Previously this would not happen because on creation of a PR the
validation task (commit -> PR) would still have to execute.
Misc Changes
============
- If a PR is marked as overriding / canceling stagings, it now does
so on retry not just when setting initially.
This was not handled at all previously, so a PR in P0 going into
error due to e.g. a non-deterministic bug would be retried and still
p=0, but a current staging would not get cancelled. Same when a PR
in p=0 goes into error because something was failed, then is updated
with a fix.
- Add tracking to a bunch of relevant PR fields.
Post-mortem analysis currently generally requires going through the
text logs to see what happened, which is annoying.
There is a nondeterminism / inconsistency in the tracking which
sometimes leads the admin user to trigger tracking before the bot
does, leading to the staging tracking being attributed to them
during tests, shove under the carpet by ignoring the user to whom
that tracking is attributed.
When multiple users update tracked fields in the same transaction
all the changes are attributed to the first one having triggered
tracking (?), I couldn't find why the admin sometimes takes over.
- added and leveraged support for enum-backed selection fields
- moved variuous fields from forwardport to runbot_merge
- fix a migration which had never worked and which never run (because
I forgot to bump the version on the module)
- remove some unnecessary intermediate de/serialisation
fixes #673, fixes #309, fixes #792, fixes #846 (probably)
2023-10-31 13:42:07 +07:00
|
|
|
|
|
|
|
# cause the PR to become ready the normal way
|
|
|
|
with repo:
|
|
|
|
pr01.post_comment("hansen r+", config['role_reviewer']['token'])
|
|
|
|
repo.post_status(p_01.head, 'success', 'legal/cla')
|
|
|
|
repo.post_status(p_01.head, 'success', 'ci/runbot')
|
|
|
|
env.run_crons()
|
|
|
|
|
|
|
|
# a cancel_staging pr becoming ready should have cancelled the staging,
|
|
|
|
# and because the PR is `alone` it should... have been restaged alone,
|
|
|
|
# without the ready non-alone PRs
|
|
|
|
assert not sm_all.staging_id.active
|
|
|
|
assert p_01.staging_id.active
|
|
|
|
assert p_01.state == 'ready'
|
|
|
|
assert p_01.priority == 'alone'
|
|
|
|
assert p_01.skipchecks == False
|
|
|
|
assert p_01.cancel_staging == True
|
2018-03-27 18:33:04 +07:00
|
|
|
|
2024-06-25 03:16:43 +07:00
|
|
|
@pytest.mark.usefixtures("reviewer_admin")
|
2019-10-10 14:22:12 +07:00
|
|
|
def test_batching_urgenter_than_split(self, env, repo, config):
|
[CHG] *: rewrite commands set, rework status management
This commit revisits the commands set in order to make it more
regular, and limit inconsistent command-sets, although it includes
pseudo-command aliases for common tasks now removed from the core set.
Hard Errors
===========
The previous iteration of the commands set would ignore any
non-command term in a command line. This has been changed to hard
error (and ignoring the entire thing) if any command is unknown or
invalid.
This fixes inconsistent / unexpected interpretations where a user
sends a command, then writes a novel on the same line some words of
which happen to *also* be commands, leading to merge states they did
not expect. They should now be told to fuck off.
Priority Restructuring
----------------------
The numerical priority system was pretty messy in that it confused
"staging priority" (in ways which were not entirely straightforward)
with overrides to other concerns.
This has now being split along all the axis, with separate command
subsets for:
- staging prioritisation, now separated between `default`, `priority`,
and `alone`,
- `default` means PRs are picked by an unspecified order when
creating a staging, if nothing better is available
- `priority` means PRs are picked first when staging, however if
`priority` PRs don't fill the staging the rest will be filled with
`default`, this mode did not previously exist
- `alone` means the PRs are picked first, before splits, and only
`alone` PRs can be part of the staging (which usually matches the
modename)
- `skipchecks` overrides both statuses and approval checks, for the
batch, something previously implied in `p=0`, but now
independent. Setting `skipchecks` basically makes the entire batch
`ready`.
For consistency this also sets the reviewer implicitly: since
skipchecks overrides both statuses *and approval*, whoever enables
this mode is essentially the reviewer.
- `cancel` cancels any ongoing staging when the marked PR becomes
ready again, previously this was also implied (in a more restricted
form) by setting `p=0`
FWBot removal
=============
While the "forwardport bot" still exists as an API level (to segregate
access rights between tokens) it has been removed as an interaction
point, as part of the modules merge plan. As a result,
fwbot stops responding
----------------------
Feedback messages are now always sent by the mergebot, the
forward-porting bot should not send any message or notification
anymore.
commands moved to the merge bot
-------------------------------
- `ignore`/`up to` simply changes bot
- `close` as well
- `skipci` is now a choice / flag of an `fw` command, which denotes
the forward-port policy,
- `fw=default` is the old `ci` and resets the policy to default,
that is wait for the PR to be merged to create forward ports, and
for the required statuses on each forward port to be received
before creating the next
- `fw=skipci` is the old `skipci`, it waits for the merge of the
base PR but then creates all the forward ports immediately (unless
it gets a conflict)
- `fw=skipmerge` immediately creates all the forward ports, without
even waiting for the PR to be merged
This is a completely new mode, and may be rather broken as until
now the 'bot has always assumed the source PR had been merged.
approval rework
---------------
Because of the previous section, there is no distinguishing feature
between `mergebot r+` = "merge this PR" and `forwardbot r+` = "merge
this PR and all its parent with different access rights".
As a result, the two have been merged under a single `mergebot r+`
with heuristics attempting to provide the best experience:
- if approving a non-forward port, the behavior does not change
- else, with review rights on the source, all ancestors are approved
- else, as author of the original, approves all ancestors which descend
from a merged PR
- else, approves all ancestors up to and including the oldest ancestor
to which we have review rights
Most notably, the source's author is not delegated on the source or
any of its descendants anymore. This might need to be revisited if it
provides too restrictive.
For the very specialized need of approving a forward-port *and none of
its ancestors*, `review=` can now take a comma (`,`) separated list of
pull request numbers (github numbers, not mergebot ids).
Computed State
==============
The `state` field of pull requests is now computed. Hopefully this
makes the status more consistent and predictable in the long run, and
importantly makes status management more reliable (because reference
datum get updated naturally flowing to the state).
For now however it makes things more complicated as some of the states
have to be separately signaled or updated:
- `closed` and `error` are now separate flags
- `merge_date` is pulled down from forwardport and becomes the
transition signal for ready -> merged
- `reviewed_by` becomes the transition signal for approval (might be a
good idea to rename it...)
- `status` is computed from the head's statuses and overrides, and
*that* becomes the validation state
Ideally, batch-level flags like `skipchecks` should be on, well, the
batch, and `state` should have a dependency on the batch. However
currently the batch is not a durable / permanent member of the system,
so it's a PR-level flag and a messy pile.
On notable change is that *forcing* the state to `ready` now does that
but also sets the reviewer, `skipchecks`, and overrides to ensure the
API-mediated readying does not get rolled back by e.g. the runbot
sending a status.
This is useful for a few types of automated / programmatic PRs
e.g. translation exports, where we set the state programmatically to
limit noise.
recursive dependency hack
-------------------------
Given a sequence of PRs with an override of the source, if one of the
PRs is updated its descendants should not have the override
anymore. However if the updated PR gets overridden, its descendants
should have *that* override.
This requires some unholy manipulations via an override of `modified`,
as the ORM supports recursive fields but not recursive
dependencies (on a different field).
unconditional followup scheduling
---------------------------------
Previously scheduling forward-port followup was contigent on the FW
policy, but it's not actually correct if the new PR is *immediately*
validated (which can happen now that the field is computed, if there
are no required statuses *or* all of the required statuses are
overridden by an ancestor) as nothing will trigger the state change
and thus scheduling of the fp followup.
The followup function checks all the properties of the batch to port,
so this should not result on incorrect ports. Although it's a bit more
expensive, and will lead to more spam.
Previously this would not happen because on creation of a PR the
validation task (commit -> PR) would still have to execute.
Misc Changes
============
- If a PR is marked as overriding / canceling stagings, it now does
so on retry not just when setting initially.
This was not handled at all previously, so a PR in P0 going into
error due to e.g. a non-deterministic bug would be retried and still
p=0, but a current staging would not get cancelled. Same when a PR
in p=0 goes into error because something was failed, then is updated
with a fix.
- Add tracking to a bunch of relevant PR fields.
Post-mortem analysis currently generally requires going through the
text logs to see what happened, which is annoying.
There is a nondeterminism / inconsistency in the tracking which
sometimes leads the admin user to trigger tracking before the bot
does, leading to the staging tracking being attributed to them
during tests, shove under the carpet by ignoring the user to whom
that tracking is attributed.
When multiple users update tracked fields in the same transaction
all the changes are attributed to the first one having triggered
tracking (?), I couldn't find why the admin sometimes takes over.
- added and leveraged support for enum-backed selection fields
- moved variuous fields from forwardport to runbot_merge
- fix a migration which had never worked and which never run (because
I forgot to bump the version on the module)
- remove some unnecessary intermediate de/serialisation
fixes #673, fixes #309, fixes #792, fixes #846 (probably)
2023-10-31 13:42:07 +07:00
|
|
|
""" p=alone PRs should take priority over split stagings (processing
|
2018-03-27 18:33:04 +07:00
|
|
|
of a staging having CI-failed and being split into sub-stagings)
|
|
|
|
"""
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
m = repo.make_commit(None, 'initial', None, tree={'a': 'some content'})
|
|
|
|
repo.make_ref('heads/master', m)
|
2018-03-27 18:33:04 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
pr1 = self._pr(repo, 'PR1', [{'a': 'AAA'}, {'b': 'BBB'}], user=config['role_user']['token'], reviewer=config['role_reviewer']['token'])
|
|
|
|
pr2 = self._pr(repo, 'PR2', [{'a': 'some content', 'c': 'CCC'}, {'d': 'DDD'}], user=config['role_user']['token'], reviewer=config['role_reviewer']['token'])
|
|
|
|
env.run_crons()
|
2018-03-14 16:37:46 +07:00
|
|
|
|
2022-08-05 18:59:39 +07:00
|
|
|
p_1 = to_pr(env, pr1)
|
|
|
|
p_2 = to_pr(env, pr2)
|
2018-03-27 18:33:04 +07:00
|
|
|
st = env['runbot_merge.stagings'].search([])
|
2022-08-05 18:59:39 +07:00
|
|
|
|
2018-03-27 18:33:04 +07:00
|
|
|
# both prs should be part of the staging
|
|
|
|
assert st.mapped('batch_ids.prs') == p_1 | p_2
|
2022-08-05 18:59:39 +07:00
|
|
|
|
2018-03-27 18:33:04 +07:00
|
|
|
# add CI failure
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
repo.post_status('heads/staging.master', 'failure', 'ci/runbot')
|
|
|
|
repo.post_status('heads/staging.master', 'success', 'legal/cla')
|
|
|
|
env.run_crons()
|
2018-03-27 18:33:04 +07:00
|
|
|
|
|
|
|
# should have staged the first half
|
|
|
|
assert p_1.staging_id.heads
|
|
|
|
assert not p_2.staging_id.heads
|
|
|
|
|
|
|
|
# during restaging of pr1, create urgent PR
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
pr0 = self._pr(repo, 'urgent', [{'a': 'a', 'b': 'b'}], user=config['role_user']['token'], reviewer=None, statuses=[])
|
[CHG] *: rewrite commands set, rework status management
This commit revisits the commands set in order to make it more
regular, and limit inconsistent command-sets, although it includes
pseudo-command aliases for common tasks now removed from the core set.
Hard Errors
===========
The previous iteration of the commands set would ignore any
non-command term in a command line. This has been changed to hard
error (and ignoring the entire thing) if any command is unknown or
invalid.
This fixes inconsistent / unexpected interpretations where a user
sends a command, then writes a novel on the same line some words of
which happen to *also* be commands, leading to merge states they did
not expect. They should now be told to fuck off.
Priority Restructuring
----------------------
The numerical priority system was pretty messy in that it confused
"staging priority" (in ways which were not entirely straightforward)
with overrides to other concerns.
This has now being split along all the axis, with separate command
subsets for:
- staging prioritisation, now separated between `default`, `priority`,
and `alone`,
- `default` means PRs are picked by an unspecified order when
creating a staging, if nothing better is available
- `priority` means PRs are picked first when staging, however if
`priority` PRs don't fill the staging the rest will be filled with
`default`, this mode did not previously exist
- `alone` means the PRs are picked first, before splits, and only
`alone` PRs can be part of the staging (which usually matches the
modename)
- `skipchecks` overrides both statuses and approval checks, for the
batch, something previously implied in `p=0`, but now
independent. Setting `skipchecks` basically makes the entire batch
`ready`.
For consistency this also sets the reviewer implicitly: since
skipchecks overrides both statuses *and approval*, whoever enables
this mode is essentially the reviewer.
- `cancel` cancels any ongoing staging when the marked PR becomes
ready again, previously this was also implied (in a more restricted
form) by setting `p=0`
FWBot removal
=============
While the "forwardport bot" still exists as an API level (to segregate
access rights between tokens) it has been removed as an interaction
point, as part of the modules merge plan. As a result,
fwbot stops responding
----------------------
Feedback messages are now always sent by the mergebot, the
forward-porting bot should not send any message or notification
anymore.
commands moved to the merge bot
-------------------------------
- `ignore`/`up to` simply changes bot
- `close` as well
- `skipci` is now a choice / flag of an `fw` command, which denotes
the forward-port policy,
- `fw=default` is the old `ci` and resets the policy to default,
that is wait for the PR to be merged to create forward ports, and
for the required statuses on each forward port to be received
before creating the next
- `fw=skipci` is the old `skipci`, it waits for the merge of the
base PR but then creates all the forward ports immediately (unless
it gets a conflict)
- `fw=skipmerge` immediately creates all the forward ports, without
even waiting for the PR to be merged
This is a completely new mode, and may be rather broken as until
now the 'bot has always assumed the source PR had been merged.
approval rework
---------------
Because of the previous section, there is no distinguishing feature
between `mergebot r+` = "merge this PR" and `forwardbot r+` = "merge
this PR and all its parent with different access rights".
As a result, the two have been merged under a single `mergebot r+`
with heuristics attempting to provide the best experience:
- if approving a non-forward port, the behavior does not change
- else, with review rights on the source, all ancestors are approved
- else, as author of the original, approves all ancestors which descend
from a merged PR
- else, approves all ancestors up to and including the oldest ancestor
to which we have review rights
Most notably, the source's author is not delegated on the source or
any of its descendants anymore. This might need to be revisited if it
provides too restrictive.
For the very specialized need of approving a forward-port *and none of
its ancestors*, `review=` can now take a comma (`,`) separated list of
pull request numbers (github numbers, not mergebot ids).
Computed State
==============
The `state` field of pull requests is now computed. Hopefully this
makes the status more consistent and predictable in the long run, and
importantly makes status management more reliable (because reference
datum get updated naturally flowing to the state).
For now however it makes things more complicated as some of the states
have to be separately signaled or updated:
- `closed` and `error` are now separate flags
- `merge_date` is pulled down from forwardport and becomes the
transition signal for ready -> merged
- `reviewed_by` becomes the transition signal for approval (might be a
good idea to rename it...)
- `status` is computed from the head's statuses and overrides, and
*that* becomes the validation state
Ideally, batch-level flags like `skipchecks` should be on, well, the
batch, and `state` should have a dependency on the batch. However
currently the batch is not a durable / permanent member of the system,
so it's a PR-level flag and a messy pile.
On notable change is that *forcing* the state to `ready` now does that
but also sets the reviewer, `skipchecks`, and overrides to ensure the
API-mediated readying does not get rolled back by e.g. the runbot
sending a status.
This is useful for a few types of automated / programmatic PRs
e.g. translation exports, where we set the state programmatically to
limit noise.
recursive dependency hack
-------------------------
Given a sequence of PRs with an override of the source, if one of the
PRs is updated its descendants should not have the override
anymore. However if the updated PR gets overridden, its descendants
should have *that* override.
This requires some unholy manipulations via an override of `modified`,
as the ORM supports recursive fields but not recursive
dependencies (on a different field).
unconditional followup scheduling
---------------------------------
Previously scheduling forward-port followup was contigent on the FW
policy, but it's not actually correct if the new PR is *immediately*
validated (which can happen now that the field is computed, if there
are no required statuses *or* all of the required statuses are
overridden by an ancestor) as nothing will trigger the state change
and thus scheduling of the fp followup.
The followup function checks all the properties of the batch to port,
so this should not result on incorrect ports. Although it's a bit more
expensive, and will lead to more spam.
Previously this would not happen because on creation of a PR the
validation task (commit -> PR) would still have to execute.
Misc Changes
============
- If a PR is marked as overriding / canceling stagings, it now does
so on retry not just when setting initially.
This was not handled at all previously, so a PR in P0 going into
error due to e.g. a non-deterministic bug would be retried and still
p=0, but a current staging would not get cancelled. Same when a PR
in p=0 goes into error because something was failed, then is updated
with a fix.
- Add tracking to a bunch of relevant PR fields.
Post-mortem analysis currently generally requires going through the
text logs to see what happened, which is annoying.
There is a nondeterminism / inconsistency in the tracking which
sometimes leads the admin user to trigger tracking before the bot
does, leading to the staging tracking being attributed to them
during tests, shove under the carpet by ignoring the user to whom
that tracking is attributed.
When multiple users update tracked fields in the same transaction
all the changes are attributed to the first one having triggered
tracking (?), I couldn't find why the admin sometimes takes over.
- added and leveraged support for enum-backed selection fields
- moved variuous fields from forwardport to runbot_merge
- fix a migration which had never worked and which never run (because
I forgot to bump the version on the module)
- remove some unnecessary intermediate de/serialisation
fixes #673, fixes #309, fixes #792, fixes #846 (probably)
2023-10-31 13:42:07 +07:00
|
|
|
pr0.post_comment('hansen NOW!', config['role_reviewer']['token'])
|
2019-10-10 14:22:12 +07:00
|
|
|
env.run_crons()
|
2018-03-27 18:33:04 +07:00
|
|
|
|
|
|
|
assert not p_1.staging_id
|
2021-08-09 12:55:38 +07:00
|
|
|
assert to_pr(env, pr0).staging_id
|
2018-03-27 18:33:04 +07:00
|
|
|
|
2024-06-25 03:16:43 +07:00
|
|
|
@pytest.mark.usefixtures("reviewer_admin")
|
2019-10-10 14:22:12 +07:00
|
|
|
def test_urgent_failed(self, env, repo, config):
|
2018-04-03 21:28:45 +07:00
|
|
|
""" Ensure pr[p=0,state=failed] don't get picked up
|
|
|
|
"""
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
m = repo.make_commit(None, 'initial', None, tree={'a': 'some content'})
|
|
|
|
repo.make_ref('heads/master', m)
|
2018-04-03 21:28:45 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
pr21 = self._pr(repo, 'PR1', [{'a': 'AAA'}, {'b': 'BBB'}], user=config['role_user']['token'], reviewer=config['role_reviewer']['token'])
|
2018-04-03 21:28:45 +07:00
|
|
|
|
2021-08-09 12:55:38 +07:00
|
|
|
p_21 = to_pr(env, pr21)
|
2018-03-14 16:37:46 +07:00
|
|
|
|
2018-04-03 21:28:45 +07:00
|
|
|
# no statuses run on PR0s
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
pr01 = self._pr(repo, 'Urgent1', [{'n': 'n'}, {'o': 'o'}], user=config['role_user']['token'], reviewer=None, statuses=[])
|
[CHG] *: rewrite commands set, rework status management
This commit revisits the commands set in order to make it more
regular, and limit inconsistent command-sets, although it includes
pseudo-command aliases for common tasks now removed from the core set.
Hard Errors
===========
The previous iteration of the commands set would ignore any
non-command term in a command line. This has been changed to hard
error (and ignoring the entire thing) if any command is unknown or
invalid.
This fixes inconsistent / unexpected interpretations where a user
sends a command, then writes a novel on the same line some words of
which happen to *also* be commands, leading to merge states they did
not expect. They should now be told to fuck off.
Priority Restructuring
----------------------
The numerical priority system was pretty messy in that it confused
"staging priority" (in ways which were not entirely straightforward)
with overrides to other concerns.
This has now being split along all the axis, with separate command
subsets for:
- staging prioritisation, now separated between `default`, `priority`,
and `alone`,
- `default` means PRs are picked by an unspecified order when
creating a staging, if nothing better is available
- `priority` means PRs are picked first when staging, however if
`priority` PRs don't fill the staging the rest will be filled with
`default`, this mode did not previously exist
- `alone` means the PRs are picked first, before splits, and only
`alone` PRs can be part of the staging (which usually matches the
modename)
- `skipchecks` overrides both statuses and approval checks, for the
batch, something previously implied in `p=0`, but now
independent. Setting `skipchecks` basically makes the entire batch
`ready`.
For consistency this also sets the reviewer implicitly: since
skipchecks overrides both statuses *and approval*, whoever enables
this mode is essentially the reviewer.
- `cancel` cancels any ongoing staging when the marked PR becomes
ready again, previously this was also implied (in a more restricted
form) by setting `p=0`
FWBot removal
=============
While the "forwardport bot" still exists as an API level (to segregate
access rights between tokens) it has been removed as an interaction
point, as part of the modules merge plan. As a result,
fwbot stops responding
----------------------
Feedback messages are now always sent by the mergebot, the
forward-porting bot should not send any message or notification
anymore.
commands moved to the merge bot
-------------------------------
- `ignore`/`up to` simply changes bot
- `close` as well
- `skipci` is now a choice / flag of an `fw` command, which denotes
the forward-port policy,
- `fw=default` is the old `ci` and resets the policy to default,
that is wait for the PR to be merged to create forward ports, and
for the required statuses on each forward port to be received
before creating the next
- `fw=skipci` is the old `skipci`, it waits for the merge of the
base PR but then creates all the forward ports immediately (unless
it gets a conflict)
- `fw=skipmerge` immediately creates all the forward ports, without
even waiting for the PR to be merged
This is a completely new mode, and may be rather broken as until
now the 'bot has always assumed the source PR had been merged.
approval rework
---------------
Because of the previous section, there is no distinguishing feature
between `mergebot r+` = "merge this PR" and `forwardbot r+` = "merge
this PR and all its parent with different access rights".
As a result, the two have been merged under a single `mergebot r+`
with heuristics attempting to provide the best experience:
- if approving a non-forward port, the behavior does not change
- else, with review rights on the source, all ancestors are approved
- else, as author of the original, approves all ancestors which descend
from a merged PR
- else, approves all ancestors up to and including the oldest ancestor
to which we have review rights
Most notably, the source's author is not delegated on the source or
any of its descendants anymore. This might need to be revisited if it
provides too restrictive.
For the very specialized need of approving a forward-port *and none of
its ancestors*, `review=` can now take a comma (`,`) separated list of
pull request numbers (github numbers, not mergebot ids).
Computed State
==============
The `state` field of pull requests is now computed. Hopefully this
makes the status more consistent and predictable in the long run, and
importantly makes status management more reliable (because reference
datum get updated naturally flowing to the state).
For now however it makes things more complicated as some of the states
have to be separately signaled or updated:
- `closed` and `error` are now separate flags
- `merge_date` is pulled down from forwardport and becomes the
transition signal for ready -> merged
- `reviewed_by` becomes the transition signal for approval (might be a
good idea to rename it...)
- `status` is computed from the head's statuses and overrides, and
*that* becomes the validation state
Ideally, batch-level flags like `skipchecks` should be on, well, the
batch, and `state` should have a dependency on the batch. However
currently the batch is not a durable / permanent member of the system,
so it's a PR-level flag and a messy pile.
On notable change is that *forcing* the state to `ready` now does that
but also sets the reviewer, `skipchecks`, and overrides to ensure the
API-mediated readying does not get rolled back by e.g. the runbot
sending a status.
This is useful for a few types of automated / programmatic PRs
e.g. translation exports, where we set the state programmatically to
limit noise.
recursive dependency hack
-------------------------
Given a sequence of PRs with an override of the source, if one of the
PRs is updated its descendants should not have the override
anymore. However if the updated PR gets overridden, its descendants
should have *that* override.
This requires some unholy manipulations via an override of `modified`,
as the ORM supports recursive fields but not recursive
dependencies (on a different field).
unconditional followup scheduling
---------------------------------
Previously scheduling forward-port followup was contigent on the FW
policy, but it's not actually correct if the new PR is *immediately*
validated (which can happen now that the field is computed, if there
are no required statuses *or* all of the required statuses are
overridden by an ancestor) as nothing will trigger the state change
and thus scheduling of the fp followup.
The followup function checks all the properties of the batch to port,
so this should not result on incorrect ports. Although it's a bit more
expensive, and will lead to more spam.
Previously this would not happen because on creation of a PR the
validation task (commit -> PR) would still have to execute.
Misc Changes
============
- If a PR is marked as overriding / canceling stagings, it now does
so on retry not just when setting initially.
This was not handled at all previously, so a PR in P0 going into
error due to e.g. a non-deterministic bug would be retried and still
p=0, but a current staging would not get cancelled. Same when a PR
in p=0 goes into error because something was failed, then is updated
with a fix.
- Add tracking to a bunch of relevant PR fields.
Post-mortem analysis currently generally requires going through the
text logs to see what happened, which is annoying.
There is a nondeterminism / inconsistency in the tracking which
sometimes leads the admin user to trigger tracking before the bot
does, leading to the staging tracking being attributed to them
during tests, shove under the carpet by ignoring the user to whom
that tracking is attributed.
When multiple users update tracked fields in the same transaction
all the changes are attributed to the first one having triggered
tracking (?), I couldn't find why the admin sometimes takes over.
- added and leveraged support for enum-backed selection fields
- moved variuous fields from forwardport to runbot_merge
- fix a migration which had never worked and which never run (because
I forgot to bump the version on the module)
- remove some unnecessary intermediate de/serialisation
fixes #673, fixes #309, fixes #792, fixes #846 (probably)
2023-10-31 13:42:07 +07:00
|
|
|
pr01.post_comment('hansen NOW!', config['role_reviewer']['token'])
|
2021-08-09 12:55:38 +07:00
|
|
|
p_01 = to_pr(env, pr01)
|
2024-06-11 20:31:35 +07:00
|
|
|
p_01.error = True
|
2018-04-03 21:28:45 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
env.run_crons()
|
2018-04-03 21:28:45 +07:00
|
|
|
assert not p_01.staging_id, "p_01 should not be picked up as it's failed"
|
|
|
|
assert p_21.staging_id, "p_21 should have been staged"
|
2018-03-14 16:37:46 +07:00
|
|
|
|
2024-02-08 18:02:34 +07:00
|
|
|
def test_urgent_split(self, env, repo, config):
|
|
|
|
"""Ensure that urgent (alone) PRs which get split don't get
|
|
|
|
double-merged
|
|
|
|
"""
|
|
|
|
with repo:
|
|
|
|
repo.make_commits(
|
|
|
|
None,
|
|
|
|
Commit("initial", tree={'a': '1'}),
|
|
|
|
ref="heads/master"
|
|
|
|
)
|
|
|
|
|
|
|
|
pr01 = self._pr(
|
|
|
|
repo, "PR1", [{'b': '1'}],
|
|
|
|
user=config['role_user']['token'],
|
|
|
|
reviewer=None,
|
|
|
|
)
|
|
|
|
pr01.post_comment('hansen alone r+', config['role_reviewer']['token'])
|
|
|
|
pr02 = self._pr(
|
|
|
|
repo, "PR2", [{'c': '1'}],
|
|
|
|
user=config['role_user']['token'],
|
|
|
|
reviewer=None,
|
|
|
|
)
|
|
|
|
pr02.post_comment('hansen alone r+', config['role_reviewer']['token'])
|
2024-07-30 18:42:00 +07:00
|
|
|
env.run_crons(None)
|
2024-02-08 18:02:34 +07:00
|
|
|
pr01_id = to_pr(env, pr01)
|
|
|
|
assert pr01_id.blocked is False
|
|
|
|
pr02_id = to_pr(env, pr02)
|
|
|
|
assert pr01_id.blocked is False
|
|
|
|
|
|
|
|
env.run_crons()
|
|
|
|
st = pr01_id.staging_id
|
|
|
|
assert st and pr02_id.staging_id == st
|
|
|
|
with repo:
|
|
|
|
repo.post_status('staging.master', 'failure', 'ci/runbot')
|
|
|
|
env.run_crons()
|
|
|
|
# should have cancelled the staging, split it, and re-staged the first
|
|
|
|
# half of the split
|
|
|
|
assert st.state == 'failure'
|
|
|
|
assert pr01_id.staging_id and pr01_id.staging_id != st
|
|
|
|
assert not pr02_id.staging_id
|
|
|
|
split_prs = env['runbot_merge.split'].search([]).batch_ids.prs
|
|
|
|
assert split_prs == pr02_id, \
|
|
|
|
f"only the unstaged PR {pr02_id} should be in a split, found {split_prs}"
|
|
|
|
|
2018-03-14 16:37:46 +07:00
|
|
|
@pytest.mark.skip(reason="Maybe nothing to do, the PR is just skipped and put in error?")
|
2018-06-07 19:53:31 +07:00
|
|
|
def test_batching_merge_failure(self):
|
2018-03-14 16:37:46 +07:00
|
|
|
pass
|
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
def test_staging_ci_failure_batch(self, env, repo, config):
|
2018-03-14 16:37:46 +07:00
|
|
|
""" on failure split batch & requeue
|
|
|
|
"""
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
m = repo.make_commit(None, 'initial', None, tree={'a': 'some content'})
|
|
|
|
repo.make_ref('heads/master', m)
|
2018-03-14 16:37:46 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
pr1 = self._pr(repo, 'PR1', [{'a': 'AAA'}, {'b': 'BBB'}], user=config['role_user']['token'], reviewer=config['role_reviewer']['token'])
|
|
|
|
pr2 = self._pr(repo, 'PR2', [{'a': 'some content', 'c': 'CCC'}, {'d': 'DDD'}], user=config['role_user']['token'], reviewer=config['role_reviewer']['token'])
|
|
|
|
env.run_crons()
|
2018-03-14 16:37:46 +07:00
|
|
|
|
|
|
|
st = env['runbot_merge.stagings'].search([])
|
|
|
|
# both prs should be part of the staging
|
|
|
|
assert len(st.mapped('batch_ids.prs')) == 2
|
|
|
|
# add CI failure
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
repo.post_status('heads/staging.master', 'failure', 'ci/runbot')
|
|
|
|
repo.post_status('heads/staging.master', 'success', 'legal/cla')
|
2018-03-14 16:37:46 +07:00
|
|
|
|
2024-12-02 20:17:28 +07:00
|
|
|
pr1 = to_pr(env, pr1)
|
|
|
|
pr2 = to_pr(env, pr2)
|
2018-03-14 16:37:46 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
env.run_crons()
|
2018-06-18 20:23:23 +07:00
|
|
|
# should have split the existing batch into two, with one of the
|
|
|
|
# splits having been immediately restaged
|
|
|
|
st = env['runbot_merge.stagings'].search([])
|
|
|
|
assert len(st) == 1
|
|
|
|
assert pr1.staging_id and pr1.staging_id == st
|
|
|
|
|
|
|
|
sp = env['runbot_merge.split'].search([])
|
|
|
|
assert len(sp) == 1
|
2018-03-14 16:37:46 +07:00
|
|
|
|
|
|
|
# This is the failing PR!
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
2024-06-03 17:59:24 +07:00
|
|
|
repo.post_status('staging.master', 'failure', 'ci/runbot')
|
|
|
|
repo.post_status('staging.master', 'success', 'legal/cla')
|
2019-10-10 14:22:12 +07:00
|
|
|
env.run_crons()
|
2018-03-14 16:37:46 +07:00
|
|
|
assert pr1.state == 'error'
|
2018-06-18 20:23:23 +07:00
|
|
|
|
|
|
|
assert pr2.staging_id
|
2018-03-14 16:37:46 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
2024-06-03 17:59:24 +07:00
|
|
|
repo.post_status('staging.master', 'success', 'ci/runbot')
|
|
|
|
repo.post_status('staging.master', 'success', 'legal/cla')
|
2024-08-01 15:15:32 +07:00
|
|
|
env.run_crons(None)
|
2018-03-14 16:37:46 +07:00
|
|
|
assert pr2.state == 'merged'
|
|
|
|
|
[CHG] *: rewrite commands set, rework status management
This commit revisits the commands set in order to make it more
regular, and limit inconsistent command-sets, although it includes
pseudo-command aliases for common tasks now removed from the core set.
Hard Errors
===========
The previous iteration of the commands set would ignore any
non-command term in a command line. This has been changed to hard
error (and ignoring the entire thing) if any command is unknown or
invalid.
This fixes inconsistent / unexpected interpretations where a user
sends a command, then writes a novel on the same line some words of
which happen to *also* be commands, leading to merge states they did
not expect. They should now be told to fuck off.
Priority Restructuring
----------------------
The numerical priority system was pretty messy in that it confused
"staging priority" (in ways which were not entirely straightforward)
with overrides to other concerns.
This has now being split along all the axis, with separate command
subsets for:
- staging prioritisation, now separated between `default`, `priority`,
and `alone`,
- `default` means PRs are picked by an unspecified order when
creating a staging, if nothing better is available
- `priority` means PRs are picked first when staging, however if
`priority` PRs don't fill the staging the rest will be filled with
`default`, this mode did not previously exist
- `alone` means the PRs are picked first, before splits, and only
`alone` PRs can be part of the staging (which usually matches the
modename)
- `skipchecks` overrides both statuses and approval checks, for the
batch, something previously implied in `p=0`, but now
independent. Setting `skipchecks` basically makes the entire batch
`ready`.
For consistency this also sets the reviewer implicitly: since
skipchecks overrides both statuses *and approval*, whoever enables
this mode is essentially the reviewer.
- `cancel` cancels any ongoing staging when the marked PR becomes
ready again, previously this was also implied (in a more restricted
form) by setting `p=0`
FWBot removal
=============
While the "forwardport bot" still exists as an API level (to segregate
access rights between tokens) it has been removed as an interaction
point, as part of the modules merge plan. As a result,
fwbot stops responding
----------------------
Feedback messages are now always sent by the mergebot, the
forward-porting bot should not send any message or notification
anymore.
commands moved to the merge bot
-------------------------------
- `ignore`/`up to` simply changes bot
- `close` as well
- `skipci` is now a choice / flag of an `fw` command, which denotes
the forward-port policy,
- `fw=default` is the old `ci` and resets the policy to default,
that is wait for the PR to be merged to create forward ports, and
for the required statuses on each forward port to be received
before creating the next
- `fw=skipci` is the old `skipci`, it waits for the merge of the
base PR but then creates all the forward ports immediately (unless
it gets a conflict)
- `fw=skipmerge` immediately creates all the forward ports, without
even waiting for the PR to be merged
This is a completely new mode, and may be rather broken as until
now the 'bot has always assumed the source PR had been merged.
approval rework
---------------
Because of the previous section, there is no distinguishing feature
between `mergebot r+` = "merge this PR" and `forwardbot r+` = "merge
this PR and all its parent with different access rights".
As a result, the two have been merged under a single `mergebot r+`
with heuristics attempting to provide the best experience:
- if approving a non-forward port, the behavior does not change
- else, with review rights on the source, all ancestors are approved
- else, as author of the original, approves all ancestors which descend
from a merged PR
- else, approves all ancestors up to and including the oldest ancestor
to which we have review rights
Most notably, the source's author is not delegated on the source or
any of its descendants anymore. This might need to be revisited if it
provides too restrictive.
For the very specialized need of approving a forward-port *and none of
its ancestors*, `review=` can now take a comma (`,`) separated list of
pull request numbers (github numbers, not mergebot ids).
Computed State
==============
The `state` field of pull requests is now computed. Hopefully this
makes the status more consistent and predictable in the long run, and
importantly makes status management more reliable (because reference
datum get updated naturally flowing to the state).
For now however it makes things more complicated as some of the states
have to be separately signaled or updated:
- `closed` and `error` are now separate flags
- `merge_date` is pulled down from forwardport and becomes the
transition signal for ready -> merged
- `reviewed_by` becomes the transition signal for approval (might be a
good idea to rename it...)
- `status` is computed from the head's statuses and overrides, and
*that* becomes the validation state
Ideally, batch-level flags like `skipchecks` should be on, well, the
batch, and `state` should have a dependency on the batch. However
currently the batch is not a durable / permanent member of the system,
so it's a PR-level flag and a messy pile.
On notable change is that *forcing* the state to `ready` now does that
but also sets the reviewer, `skipchecks`, and overrides to ensure the
API-mediated readying does not get rolled back by e.g. the runbot
sending a status.
This is useful for a few types of automated / programmatic PRs
e.g. translation exports, where we set the state programmatically to
limit noise.
recursive dependency hack
-------------------------
Given a sequence of PRs with an override of the source, if one of the
PRs is updated its descendants should not have the override
anymore. However if the updated PR gets overridden, its descendants
should have *that* override.
This requires some unholy manipulations via an override of `modified`,
as the ORM supports recursive fields but not recursive
dependencies (on a different field).
unconditional followup scheduling
---------------------------------
Previously scheduling forward-port followup was contigent on the FW
policy, but it's not actually correct if the new PR is *immediately*
validated (which can happen now that the field is computed, if there
are no required statuses *or* all of the required statuses are
overridden by an ancestor) as nothing will trigger the state change
and thus scheduling of the fp followup.
The followup function checks all the properties of the batch to port,
so this should not result on incorrect ports. Although it's a bit more
expensive, and will lead to more spam.
Previously this would not happen because on creation of a PR the
validation task (commit -> PR) would still have to execute.
Misc Changes
============
- If a PR is marked as overriding / canceling stagings, it now does
so on retry not just when setting initially.
This was not handled at all previously, so a PR in P0 going into
error due to e.g. a non-deterministic bug would be retried and still
p=0, but a current staging would not get cancelled. Same when a PR
in p=0 goes into error because something was failed, then is updated
with a fix.
- Add tracking to a bunch of relevant PR fields.
Post-mortem analysis currently generally requires going through the
text logs to see what happened, which is annoying.
There is a nondeterminism / inconsistency in the tracking which
sometimes leads the admin user to trigger tracking before the bot
does, leading to the staging tracking being attributed to them
during tests, shove under the carpet by ignoring the user to whom
that tracking is attributed.
When multiple users update tracked fields in the same transaction
all the changes are attributed to the first one having triggered
tracking (?), I couldn't find why the admin sometimes takes over.
- added and leveraged support for enum-backed selection fields
- moved variuous fields from forwardport to runbot_merge
- fix a migration which had never worked and which never run (because
I forgot to bump the version on the module)
- remove some unnecessary intermediate de/serialisation
fixes #673, fixes #309, fixes #792, fixes #846 (probably)
2023-10-31 13:42:07 +07:00
|
|
|
class TestReviewing:
|
2019-10-10 14:22:12 +07:00
|
|
|
def test_reviewer_rights(self, env, repo, users, config):
|
2018-03-14 16:37:46 +07:00
|
|
|
"""Only users with review rights will have their r+ (and other
|
|
|
|
attributes) taken in account
|
|
|
|
"""
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
m = repo.make_commit(None, 'initial', None, tree={'m': 'm'})
|
|
|
|
m2 = repo.make_commit(m, 'second', None, tree={'m': 'm', 'm2': 'm2'})
|
|
|
|
repo.make_ref('heads/master', m2)
|
|
|
|
|
|
|
|
c1 = repo.make_commit(m, 'first', None, tree={'m': 'c1'})
|
|
|
|
prx = repo.make_pr(title='title', body='body', target='master', head=c1)
|
|
|
|
repo.post_status(prx.head, 'success', 'legal/cla')
|
|
|
|
repo.post_status(prx.head, 'success', 'ci/runbot')
|
|
|
|
prx.post_comment('hansen r+', config['role_other']['token'])
|
|
|
|
env.run_crons()
|
2018-03-14 16:37:46 +07:00
|
|
|
|
2024-12-02 20:17:28 +07:00
|
|
|
assert to_pr(env, prx).state == 'validated'
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
prx.post_comment('hansen r+', config['role_reviewer']['token'])
|
2024-12-02 20:17:28 +07:00
|
|
|
assert to_pr(env, prx).state == 'ready'
|
2018-10-19 16:35:31 +07:00
|
|
|
# second r+ to check warning
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
prx.post_comment('hansen r+', config['role_reviewer']['token'])
|
2018-03-14 16:37:46 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
env.run_crons()
|
2018-10-16 17:40:45 +07:00
|
|
|
assert prx.comments == [
|
|
|
|
(users['other'], 'hansen r+'),
|
2020-11-17 21:21:21 +07:00
|
|
|
seen(env, prx, users),
|
2019-03-05 14:01:38 +07:00
|
|
|
(users['user'], "I'm sorry, @{}. I'm afraid I can't do that.".format(users['other'])),
|
2018-10-16 17:40:45 +07:00
|
|
|
(users['reviewer'], 'hansen r+'),
|
2018-10-19 16:35:31 +07:00
|
|
|
(users['reviewer'], 'hansen r+'),
|
2024-06-21 19:38:47 +07:00
|
|
|
(users['user'], "This PR is already reviewed, reviewing it again is useless.".format(
|
2018-10-19 16:35:31 +07:00
|
|
|
users['reviewer'])),
|
2018-10-16 17:40:45 +07:00
|
|
|
]
|
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
def test_self_review_fail(self, env, repo, users, config):
|
2018-03-14 16:37:46 +07:00
|
|
|
""" Normal reviewers can't self-review
|
|
|
|
"""
|
2024-01-16 21:07:25 +07:00
|
|
|
reviewer = config['role_reviewer']['token']
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
2024-01-16 21:07:25 +07:00
|
|
|
[m, _] = repo.make_commits(None, Commit('initial', tree={'m': 'm'}), Commit('second', tree={'m2': 'm2'}), ref='heads/master')
|
|
|
|
with repo.fork(token=reviewer) as f:
|
|
|
|
f.make_commits(m, Commit('first', tree={'m': 'c1'}), ref='heads/change')
|
|
|
|
prx = repo.make_pr(title='title', body='body', target='master', head=f'{f.owner}:change', token=reviewer)
|
2019-10-10 14:22:12 +07:00
|
|
|
repo.post_status(prx.head, 'success', 'legal/cla')
|
|
|
|
repo.post_status(prx.head, 'success', 'ci/runbot')
|
2024-01-16 21:07:25 +07:00
|
|
|
prx.post_comment('hansen r+', reviewer)
|
2019-10-10 14:22:12 +07:00
|
|
|
env.run_crons()
|
2018-03-14 16:37:46 +07:00
|
|
|
|
2018-06-07 19:53:31 +07:00
|
|
|
assert prx.user == users['reviewer']
|
2024-12-02 20:17:28 +07:00
|
|
|
assert to_pr(env, prx).state == 'validated'
|
2018-03-14 16:37:46 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
env.run_crons()
|
2018-10-16 17:40:45 +07:00
|
|
|
assert prx.comments == [
|
|
|
|
(users['reviewer'], 'hansen r+'),
|
2020-11-17 21:21:21 +07:00
|
|
|
seen(env, prx, users),
|
2024-08-05 14:09:23 +07:00
|
|
|
(users['user'], "@{} you can't review+.".format(users['reviewer'])),
|
2018-10-16 17:40:45 +07:00
|
|
|
]
|
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
def test_self_review_success(self, env, repo, users, config):
|
2018-03-14 16:37:46 +07:00
|
|
|
""" Some users are allowed to self-review
|
|
|
|
"""
|
2024-01-16 21:07:25 +07:00
|
|
|
self_reviewer = config['role_self_reviewer']['token']
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
2024-01-16 21:07:25 +07:00
|
|
|
[m, _] = repo.make_commits(None, Commit('initial', tree={'m': 'm'}), Commit('second', tree={'m': 'm', 'm2': 'm2'}), ref='heads/master')
|
|
|
|
with repo.fork(token=self_reviewer) as f:
|
|
|
|
f.make_commits(m, Commit('first', tree={'m': 'c1'}), ref='heads/change')
|
|
|
|
prx = repo.make_pr(title='title', body='body', target='master', head=f'{f.owner}:change', token=self_reviewer)
|
2019-10-10 14:22:12 +07:00
|
|
|
repo.post_status(prx.head, 'success', 'legal/cla')
|
|
|
|
repo.post_status(prx.head, 'success', 'ci/runbot')
|
2024-01-16 21:07:25 +07:00
|
|
|
prx.post_comment('hansen r+', self_reviewer)
|
2019-10-10 14:22:12 +07:00
|
|
|
env.run_crons()
|
2018-03-14 16:37:46 +07:00
|
|
|
|
2018-06-07 19:53:31 +07:00
|
|
|
assert prx.user == users['self_reviewer']
|
2024-12-02 20:17:28 +07:00
|
|
|
assert to_pr(env, prx).state == 'ready'
|
2018-03-14 16:37:46 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
def test_delegate_review(self, env, repo, users, config):
|
2018-03-14 16:37:46 +07:00
|
|
|
"""Users should be able to delegate review to either the creator of
|
|
|
|
the PR or an other user without review rights
|
|
|
|
"""
|
2021-10-06 18:06:53 +07:00
|
|
|
env['res.partner'].create({
|
|
|
|
'name': users['user'],
|
|
|
|
'github_login': users['user'],
|
|
|
|
'email': users['user'] + '@example.org',
|
|
|
|
})
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
m = repo.make_commit(None, 'initial', None, tree={'m': 'm'})
|
|
|
|
m2 = repo.make_commit(m, 'second', None, tree={'m': 'm', 'm2': 'm2'})
|
|
|
|
repo.make_ref('heads/master', m2)
|
|
|
|
|
|
|
|
c1 = repo.make_commit(m, 'first', None, tree={'m': 'c1'})
|
|
|
|
prx = repo.make_pr(title='title', body='body', target='master', head=c1)
|
|
|
|
repo.post_status(prx.head, 'success', 'legal/cla')
|
|
|
|
repo.post_status(prx.head, 'success', 'ci/runbot')
|
|
|
|
prx.post_comment('hansen delegate+', config['role_reviewer']['token'])
|
|
|
|
prx.post_comment('hansen r+', config['role_user']['token'])
|
|
|
|
env.run_crons()
|
2018-03-14 16:37:46 +07:00
|
|
|
|
2018-06-07 19:53:31 +07:00
|
|
|
assert prx.user == users['user']
|
2021-10-06 18:06:53 +07:00
|
|
|
assert to_pr(env, prx).state == 'ready'
|
2018-03-14 16:37:46 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
def test_delegate_review_thirdparty(self, env, repo, users, config):
|
2018-03-14 16:37:46 +07:00
|
|
|
"""Users should be able to delegate review to either the creator of
|
|
|
|
the PR or an other user without review rights
|
|
|
|
"""
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
m = repo.make_commit(None, 'initial', None, tree={'m': 'm'})
|
|
|
|
m2 = repo.make_commit(m, 'second', None, tree={'m': 'm', 'm2': 'm2'})
|
|
|
|
repo.make_ref('heads/master', m2)
|
|
|
|
|
|
|
|
c1 = repo.make_commit(m, 'first', None, tree={'m': 'c1'})
|
|
|
|
prx = repo.make_pr(title='title', body='body', target='master', head=c1)
|
|
|
|
repo.post_status(prx.head, 'success', 'legal/cla')
|
|
|
|
repo.post_status(prx.head, 'success', 'ci/runbot')
|
2020-02-11 14:46:31 +07:00
|
|
|
# flip case to check that github login is case-insensitive
|
|
|
|
other = ''.join(c.lower() if c.isupper() else c.upper() for c in users['other'])
|
|
|
|
prx.post_comment('hansen delegate=%s' % other, config['role_reviewer']['token'])
|
2019-10-10 14:22:12 +07:00
|
|
|
env.run_crons()
|
2021-10-06 18:06:53 +07:00
|
|
|
env['res.partner'].search([('github_login', '=', other)]).email = f'{other}@example.org'
|
2018-03-14 16:37:46 +07:00
|
|
|
|
2020-02-11 14:46:31 +07:00
|
|
|
with repo:
|
|
|
|
# check this is ignored
|
|
|
|
prx.post_comment('hansen r+', config['role_user']['token'])
|
2018-06-07 19:53:31 +07:00
|
|
|
assert prx.user == users['user']
|
2021-10-06 18:06:53 +07:00
|
|
|
prx_id = to_pr(env, prx)
|
|
|
|
assert prx_id.state == 'validated'
|
2018-03-14 16:37:46 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
2020-02-11 14:46:31 +07:00
|
|
|
# check this works
|
2019-10-10 14:22:12 +07:00
|
|
|
prx.post_comment('hansen r+', config['role_other']['token'])
|
2021-10-06 18:06:53 +07:00
|
|
|
assert prx_id.state == 'ready'
|
2018-03-27 21:39:29 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
def test_delegate_prefixes(self, env, repo, config):
|
|
|
|
with repo:
|
|
|
|
m = repo.make_commit(None, 'initial', None, tree={'m': 'm'})
|
|
|
|
repo.make_ref('heads/master', m)
|
2018-09-25 21:42:56 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
c = repo.make_commit(m, 'first', None, tree={'m': 'c'})
|
|
|
|
prx = repo.make_pr(title='title', body=None, target='master', head=c)
|
|
|
|
prx.post_comment('hansen delegate=foo,@bar,#baz', config['role_reviewer']['token'])
|
2018-09-25 21:42:56 +07:00
|
|
|
|
2024-12-02 20:17:28 +07:00
|
|
|
pr = to_pr(env, prx)
|
2018-09-25 21:42:56 +07:00
|
|
|
|
|
|
|
assert {d.github_login for d in pr.delegates} == {'foo', 'bar', 'baz'}
|
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
def test_actual_review(self, env, repo, config):
|
2018-09-25 19:05:41 +07:00
|
|
|
""" treat github reviews as regular comments
|
|
|
|
"""
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
m = repo.make_commit(None, 'initial', None, tree={'m': 'm'})
|
|
|
|
m2 = repo.make_commit(m, 'second', None, tree={'m': 'm', 'm2': 'm2'})
|
|
|
|
repo.make_ref('heads/master', m2)
|
2018-03-27 21:39:29 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
c1 = repo.make_commit(m, 'first', None, tree={'m': 'c1'})
|
|
|
|
prx = repo.make_pr(title='title', body='body', target='master', head=c1)
|
2024-12-02 20:17:28 +07:00
|
|
|
pr = to_pr(env, prx)
|
2018-03-27 21:39:29 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
[CHG] *: rewrite commands set, rework status management
This commit revisits the commands set in order to make it more
regular, and limit inconsistent command-sets, although it includes
pseudo-command aliases for common tasks now removed from the core set.
Hard Errors
===========
The previous iteration of the commands set would ignore any
non-command term in a command line. This has been changed to hard
error (and ignoring the entire thing) if any command is unknown or
invalid.
This fixes inconsistent / unexpected interpretations where a user
sends a command, then writes a novel on the same line some words of
which happen to *also* be commands, leading to merge states they did
not expect. They should now be told to fuck off.
Priority Restructuring
----------------------
The numerical priority system was pretty messy in that it confused
"staging priority" (in ways which were not entirely straightforward)
with overrides to other concerns.
This has now being split along all the axis, with separate command
subsets for:
- staging prioritisation, now separated between `default`, `priority`,
and `alone`,
- `default` means PRs are picked by an unspecified order when
creating a staging, if nothing better is available
- `priority` means PRs are picked first when staging, however if
`priority` PRs don't fill the staging the rest will be filled with
`default`, this mode did not previously exist
- `alone` means the PRs are picked first, before splits, and only
`alone` PRs can be part of the staging (which usually matches the
modename)
- `skipchecks` overrides both statuses and approval checks, for the
batch, something previously implied in `p=0`, but now
independent. Setting `skipchecks` basically makes the entire batch
`ready`.
For consistency this also sets the reviewer implicitly: since
skipchecks overrides both statuses *and approval*, whoever enables
this mode is essentially the reviewer.
- `cancel` cancels any ongoing staging when the marked PR becomes
ready again, previously this was also implied (in a more restricted
form) by setting `p=0`
FWBot removal
=============
While the "forwardport bot" still exists as an API level (to segregate
access rights between tokens) it has been removed as an interaction
point, as part of the modules merge plan. As a result,
fwbot stops responding
----------------------
Feedback messages are now always sent by the mergebot, the
forward-porting bot should not send any message or notification
anymore.
commands moved to the merge bot
-------------------------------
- `ignore`/`up to` simply changes bot
- `close` as well
- `skipci` is now a choice / flag of an `fw` command, which denotes
the forward-port policy,
- `fw=default` is the old `ci` and resets the policy to default,
that is wait for the PR to be merged to create forward ports, and
for the required statuses on each forward port to be received
before creating the next
- `fw=skipci` is the old `skipci`, it waits for the merge of the
base PR but then creates all the forward ports immediately (unless
it gets a conflict)
- `fw=skipmerge` immediately creates all the forward ports, without
even waiting for the PR to be merged
This is a completely new mode, and may be rather broken as until
now the 'bot has always assumed the source PR had been merged.
approval rework
---------------
Because of the previous section, there is no distinguishing feature
between `mergebot r+` = "merge this PR" and `forwardbot r+` = "merge
this PR and all its parent with different access rights".
As a result, the two have been merged under a single `mergebot r+`
with heuristics attempting to provide the best experience:
- if approving a non-forward port, the behavior does not change
- else, with review rights on the source, all ancestors are approved
- else, as author of the original, approves all ancestors which descend
from a merged PR
- else, approves all ancestors up to and including the oldest ancestor
to which we have review rights
Most notably, the source's author is not delegated on the source or
any of its descendants anymore. This might need to be revisited if it
provides too restrictive.
For the very specialized need of approving a forward-port *and none of
its ancestors*, `review=` can now take a comma (`,`) separated list of
pull request numbers (github numbers, not mergebot ids).
Computed State
==============
The `state` field of pull requests is now computed. Hopefully this
makes the status more consistent and predictable in the long run, and
importantly makes status management more reliable (because reference
datum get updated naturally flowing to the state).
For now however it makes things more complicated as some of the states
have to be separately signaled or updated:
- `closed` and `error` are now separate flags
- `merge_date` is pulled down from forwardport and becomes the
transition signal for ready -> merged
- `reviewed_by` becomes the transition signal for approval (might be a
good idea to rename it...)
- `status` is computed from the head's statuses and overrides, and
*that* becomes the validation state
Ideally, batch-level flags like `skipchecks` should be on, well, the
batch, and `state` should have a dependency on the batch. However
currently the batch is not a durable / permanent member of the system,
so it's a PR-level flag and a messy pile.
On notable change is that *forcing* the state to `ready` now does that
but also sets the reviewer, `skipchecks`, and overrides to ensure the
API-mediated readying does not get rolled back by e.g. the runbot
sending a status.
This is useful for a few types of automated / programmatic PRs
e.g. translation exports, where we set the state programmatically to
limit noise.
recursive dependency hack
-------------------------
Given a sequence of PRs with an override of the source, if one of the
PRs is updated its descendants should not have the override
anymore. However if the updated PR gets overridden, its descendants
should have *that* override.
This requires some unholy manipulations via an override of `modified`,
as the ORM supports recursive fields but not recursive
dependencies (on a different field).
unconditional followup scheduling
---------------------------------
Previously scheduling forward-port followup was contigent on the FW
policy, but it's not actually correct if the new PR is *immediately*
validated (which can happen now that the field is computed, if there
are no required statuses *or* all of the required statuses are
overridden by an ancestor) as nothing will trigger the state change
and thus scheduling of the fp followup.
The followup function checks all the properties of the batch to port,
so this should not result on incorrect ports. Although it's a bit more
expensive, and will lead to more spam.
Previously this would not happen because on creation of a PR the
validation task (commit -> PR) would still have to execute.
Misc Changes
============
- If a PR is marked as overriding / canceling stagings, it now does
so on retry not just when setting initially.
This was not handled at all previously, so a PR in P0 going into
error due to e.g. a non-deterministic bug would be retried and still
p=0, but a current staging would not get cancelled. Same when a PR
in p=0 goes into error because something was failed, then is updated
with a fix.
- Add tracking to a bunch of relevant PR fields.
Post-mortem analysis currently generally requires going through the
text logs to see what happened, which is annoying.
There is a nondeterminism / inconsistency in the tracking which
sometimes leads the admin user to trigger tracking before the bot
does, leading to the staging tracking being attributed to them
during tests, shove under the carpet by ignoring the user to whom
that tracking is attributed.
When multiple users update tracked fields in the same transaction
all the changes are attributed to the first one having triggered
tracking (?), I couldn't find why the admin sometimes takes over.
- added and leveraged support for enum-backed selection fields
- moved variuous fields from forwardport to runbot_merge
- fix a migration which had never worked and which never run (because
I forgot to bump the version on the module)
- remove some unnecessary intermediate de/serialisation
fixes #673, fixes #309, fixes #792, fixes #846 (probably)
2023-10-31 13:42:07 +07:00
|
|
|
prx.post_review('COMMENT', "hansen priority", config['role_reviewer']['token'])
|
|
|
|
assert pr.priority == 'priority'
|
2018-03-27 21:39:29 +07:00
|
|
|
assert pr.state == 'opened'
|
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
[CHG] *: rewrite commands set, rework status management
This commit revisits the commands set in order to make it more
regular, and limit inconsistent command-sets, although it includes
pseudo-command aliases for common tasks now removed from the core set.
Hard Errors
===========
The previous iteration of the commands set would ignore any
non-command term in a command line. This has been changed to hard
error (and ignoring the entire thing) if any command is unknown or
invalid.
This fixes inconsistent / unexpected interpretations where a user
sends a command, then writes a novel on the same line some words of
which happen to *also* be commands, leading to merge states they did
not expect. They should now be told to fuck off.
Priority Restructuring
----------------------
The numerical priority system was pretty messy in that it confused
"staging priority" (in ways which were not entirely straightforward)
with overrides to other concerns.
This has now being split along all the axis, with separate command
subsets for:
- staging prioritisation, now separated between `default`, `priority`,
and `alone`,
- `default` means PRs are picked by an unspecified order when
creating a staging, if nothing better is available
- `priority` means PRs are picked first when staging, however if
`priority` PRs don't fill the staging the rest will be filled with
`default`, this mode did not previously exist
- `alone` means the PRs are picked first, before splits, and only
`alone` PRs can be part of the staging (which usually matches the
modename)
- `skipchecks` overrides both statuses and approval checks, for the
batch, something previously implied in `p=0`, but now
independent. Setting `skipchecks` basically makes the entire batch
`ready`.
For consistency this also sets the reviewer implicitly: since
skipchecks overrides both statuses *and approval*, whoever enables
this mode is essentially the reviewer.
- `cancel` cancels any ongoing staging when the marked PR becomes
ready again, previously this was also implied (in a more restricted
form) by setting `p=0`
FWBot removal
=============
While the "forwardport bot" still exists as an API level (to segregate
access rights between tokens) it has been removed as an interaction
point, as part of the modules merge plan. As a result,
fwbot stops responding
----------------------
Feedback messages are now always sent by the mergebot, the
forward-porting bot should not send any message or notification
anymore.
commands moved to the merge bot
-------------------------------
- `ignore`/`up to` simply changes bot
- `close` as well
- `skipci` is now a choice / flag of an `fw` command, which denotes
the forward-port policy,
- `fw=default` is the old `ci` and resets the policy to default,
that is wait for the PR to be merged to create forward ports, and
for the required statuses on each forward port to be received
before creating the next
- `fw=skipci` is the old `skipci`, it waits for the merge of the
base PR but then creates all the forward ports immediately (unless
it gets a conflict)
- `fw=skipmerge` immediately creates all the forward ports, without
even waiting for the PR to be merged
This is a completely new mode, and may be rather broken as until
now the 'bot has always assumed the source PR had been merged.
approval rework
---------------
Because of the previous section, there is no distinguishing feature
between `mergebot r+` = "merge this PR" and `forwardbot r+` = "merge
this PR and all its parent with different access rights".
As a result, the two have been merged under a single `mergebot r+`
with heuristics attempting to provide the best experience:
- if approving a non-forward port, the behavior does not change
- else, with review rights on the source, all ancestors are approved
- else, as author of the original, approves all ancestors which descend
from a merged PR
- else, approves all ancestors up to and including the oldest ancestor
to which we have review rights
Most notably, the source's author is not delegated on the source or
any of its descendants anymore. This might need to be revisited if it
provides too restrictive.
For the very specialized need of approving a forward-port *and none of
its ancestors*, `review=` can now take a comma (`,`) separated list of
pull request numbers (github numbers, not mergebot ids).
Computed State
==============
The `state` field of pull requests is now computed. Hopefully this
makes the status more consistent and predictable in the long run, and
importantly makes status management more reliable (because reference
datum get updated naturally flowing to the state).
For now however it makes things more complicated as some of the states
have to be separately signaled or updated:
- `closed` and `error` are now separate flags
- `merge_date` is pulled down from forwardport and becomes the
transition signal for ready -> merged
- `reviewed_by` becomes the transition signal for approval (might be a
good idea to rename it...)
- `status` is computed from the head's statuses and overrides, and
*that* becomes the validation state
Ideally, batch-level flags like `skipchecks` should be on, well, the
batch, and `state` should have a dependency on the batch. However
currently the batch is not a durable / permanent member of the system,
so it's a PR-level flag and a messy pile.
On notable change is that *forcing* the state to `ready` now does that
but also sets the reviewer, `skipchecks`, and overrides to ensure the
API-mediated readying does not get rolled back by e.g. the runbot
sending a status.
This is useful for a few types of automated / programmatic PRs
e.g. translation exports, where we set the state programmatically to
limit noise.
recursive dependency hack
-------------------------
Given a sequence of PRs with an override of the source, if one of the
PRs is updated its descendants should not have the override
anymore. However if the updated PR gets overridden, its descendants
should have *that* override.
This requires some unholy manipulations via an override of `modified`,
as the ORM supports recursive fields but not recursive
dependencies (on a different field).
unconditional followup scheduling
---------------------------------
Previously scheduling forward-port followup was contigent on the FW
policy, but it's not actually correct if the new PR is *immediately*
validated (which can happen now that the field is computed, if there
are no required statuses *or* all of the required statuses are
overridden by an ancestor) as nothing will trigger the state change
and thus scheduling of the fp followup.
The followup function checks all the properties of the batch to port,
so this should not result on incorrect ports. Although it's a bit more
expensive, and will lead to more spam.
Previously this would not happen because on creation of a PR the
validation task (commit -> PR) would still have to execute.
Misc Changes
============
- If a PR is marked as overriding / canceling stagings, it now does
so on retry not just when setting initially.
This was not handled at all previously, so a PR in P0 going into
error due to e.g. a non-deterministic bug would be retried and still
p=0, but a current staging would not get cancelled. Same when a PR
in p=0 goes into error because something was failed, then is updated
with a fix.
- Add tracking to a bunch of relevant PR fields.
Post-mortem analysis currently generally requires going through the
text logs to see what happened, which is annoying.
There is a nondeterminism / inconsistency in the tracking which
sometimes leads the admin user to trigger tracking before the bot
does, leading to the staging tracking being attributed to them
during tests, shove under the carpet by ignoring the user to whom
that tracking is attributed.
When multiple users update tracked fields in the same transaction
all the changes are attributed to the first one having triggered
tracking (?), I couldn't find why the admin sometimes takes over.
- added and leveraged support for enum-backed selection fields
- moved variuous fields from forwardport to runbot_merge
- fix a migration which had never worked and which never run (because
I forgot to bump the version on the module)
- remove some unnecessary intermediate de/serialisation
fixes #673, fixes #309, fixes #792, fixes #846 (probably)
2023-10-31 13:42:07 +07:00
|
|
|
prx.post_review('APPROVE', "hansen default", config['role_reviewer']['token'])
|
|
|
|
assert pr.priority == 'default'
|
2018-09-25 19:05:41 +07:00
|
|
|
assert pr.state == 'opened'
|
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
[CHG] *: rewrite commands set, rework status management
This commit revisits the commands set in order to make it more
regular, and limit inconsistent command-sets, although it includes
pseudo-command aliases for common tasks now removed from the core set.
Hard Errors
===========
The previous iteration of the commands set would ignore any
non-command term in a command line. This has been changed to hard
error (and ignoring the entire thing) if any command is unknown or
invalid.
This fixes inconsistent / unexpected interpretations where a user
sends a command, then writes a novel on the same line some words of
which happen to *also* be commands, leading to merge states they did
not expect. They should now be told to fuck off.
Priority Restructuring
----------------------
The numerical priority system was pretty messy in that it confused
"staging priority" (in ways which were not entirely straightforward)
with overrides to other concerns.
This has now being split along all the axis, with separate command
subsets for:
- staging prioritisation, now separated between `default`, `priority`,
and `alone`,
- `default` means PRs are picked by an unspecified order when
creating a staging, if nothing better is available
- `priority` means PRs are picked first when staging, however if
`priority` PRs don't fill the staging the rest will be filled with
`default`, this mode did not previously exist
- `alone` means the PRs are picked first, before splits, and only
`alone` PRs can be part of the staging (which usually matches the
modename)
- `skipchecks` overrides both statuses and approval checks, for the
batch, something previously implied in `p=0`, but now
independent. Setting `skipchecks` basically makes the entire batch
`ready`.
For consistency this also sets the reviewer implicitly: since
skipchecks overrides both statuses *and approval*, whoever enables
this mode is essentially the reviewer.
- `cancel` cancels any ongoing staging when the marked PR becomes
ready again, previously this was also implied (in a more restricted
form) by setting `p=0`
FWBot removal
=============
While the "forwardport bot" still exists as an API level (to segregate
access rights between tokens) it has been removed as an interaction
point, as part of the modules merge plan. As a result,
fwbot stops responding
----------------------
Feedback messages are now always sent by the mergebot, the
forward-porting bot should not send any message or notification
anymore.
commands moved to the merge bot
-------------------------------
- `ignore`/`up to` simply changes bot
- `close` as well
- `skipci` is now a choice / flag of an `fw` command, which denotes
the forward-port policy,
- `fw=default` is the old `ci` and resets the policy to default,
that is wait for the PR to be merged to create forward ports, and
for the required statuses on each forward port to be received
before creating the next
- `fw=skipci` is the old `skipci`, it waits for the merge of the
base PR but then creates all the forward ports immediately (unless
it gets a conflict)
- `fw=skipmerge` immediately creates all the forward ports, without
even waiting for the PR to be merged
This is a completely new mode, and may be rather broken as until
now the 'bot has always assumed the source PR had been merged.
approval rework
---------------
Because of the previous section, there is no distinguishing feature
between `mergebot r+` = "merge this PR" and `forwardbot r+` = "merge
this PR and all its parent with different access rights".
As a result, the two have been merged under a single `mergebot r+`
with heuristics attempting to provide the best experience:
- if approving a non-forward port, the behavior does not change
- else, with review rights on the source, all ancestors are approved
- else, as author of the original, approves all ancestors which descend
from a merged PR
- else, approves all ancestors up to and including the oldest ancestor
to which we have review rights
Most notably, the source's author is not delegated on the source or
any of its descendants anymore. This might need to be revisited if it
provides too restrictive.
For the very specialized need of approving a forward-port *and none of
its ancestors*, `review=` can now take a comma (`,`) separated list of
pull request numbers (github numbers, not mergebot ids).
Computed State
==============
The `state` field of pull requests is now computed. Hopefully this
makes the status more consistent and predictable in the long run, and
importantly makes status management more reliable (because reference
datum get updated naturally flowing to the state).
For now however it makes things more complicated as some of the states
have to be separately signaled or updated:
- `closed` and `error` are now separate flags
- `merge_date` is pulled down from forwardport and becomes the
transition signal for ready -> merged
- `reviewed_by` becomes the transition signal for approval (might be a
good idea to rename it...)
- `status` is computed from the head's statuses and overrides, and
*that* becomes the validation state
Ideally, batch-level flags like `skipchecks` should be on, well, the
batch, and `state` should have a dependency on the batch. However
currently the batch is not a durable / permanent member of the system,
so it's a PR-level flag and a messy pile.
On notable change is that *forcing* the state to `ready` now does that
but also sets the reviewer, `skipchecks`, and overrides to ensure the
API-mediated readying does not get rolled back by e.g. the runbot
sending a status.
This is useful for a few types of automated / programmatic PRs
e.g. translation exports, where we set the state programmatically to
limit noise.
recursive dependency hack
-------------------------
Given a sequence of PRs with an override of the source, if one of the
PRs is updated its descendants should not have the override
anymore. However if the updated PR gets overridden, its descendants
should have *that* override.
This requires some unholy manipulations via an override of `modified`,
as the ORM supports recursive fields but not recursive
dependencies (on a different field).
unconditional followup scheduling
---------------------------------
Previously scheduling forward-port followup was contigent on the FW
policy, but it's not actually correct if the new PR is *immediately*
validated (which can happen now that the field is computed, if there
are no required statuses *or* all of the required statuses are
overridden by an ancestor) as nothing will trigger the state change
and thus scheduling of the fp followup.
The followup function checks all the properties of the batch to port,
so this should not result on incorrect ports. Although it's a bit more
expensive, and will lead to more spam.
Previously this would not happen because on creation of a PR the
validation task (commit -> PR) would still have to execute.
Misc Changes
============
- If a PR is marked as overriding / canceling stagings, it now does
so on retry not just when setting initially.
This was not handled at all previously, so a PR in P0 going into
error due to e.g. a non-deterministic bug would be retried and still
p=0, but a current staging would not get cancelled. Same when a PR
in p=0 goes into error because something was failed, then is updated
with a fix.
- Add tracking to a bunch of relevant PR fields.
Post-mortem analysis currently generally requires going through the
text logs to see what happened, which is annoying.
There is a nondeterminism / inconsistency in the tracking which
sometimes leads the admin user to trigger tracking before the bot
does, leading to the staging tracking being attributed to them
during tests, shove under the carpet by ignoring the user to whom
that tracking is attributed.
When multiple users update tracked fields in the same transaction
all the changes are attributed to the first one having triggered
tracking (?), I couldn't find why the admin sometimes takes over.
- added and leveraged support for enum-backed selection fields
- moved variuous fields from forwardport to runbot_merge
- fix a migration which had never worked and which never run (because
I forgot to bump the version on the module)
- remove some unnecessary intermediate de/serialisation
fixes #673, fixes #309, fixes #792, fixes #846 (probably)
2023-10-31 13:42:07 +07:00
|
|
|
prx.post_review('REQUEST_CHANGES', 'hansen priority', config['role_reviewer']['token'])
|
|
|
|
assert pr.priority == 'priority'
|
2018-09-25 19:05:41 +07:00
|
|
|
assert pr.state == 'opened'
|
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
prx.post_review('COMMENT', 'hansen r+', config['role_reviewer']['token'])
|
[CHG] *: rewrite commands set, rework status management
This commit revisits the commands set in order to make it more
regular, and limit inconsistent command-sets, although it includes
pseudo-command aliases for common tasks now removed from the core set.
Hard Errors
===========
The previous iteration of the commands set would ignore any
non-command term in a command line. This has been changed to hard
error (and ignoring the entire thing) if any command is unknown or
invalid.
This fixes inconsistent / unexpected interpretations where a user
sends a command, then writes a novel on the same line some words of
which happen to *also* be commands, leading to merge states they did
not expect. They should now be told to fuck off.
Priority Restructuring
----------------------
The numerical priority system was pretty messy in that it confused
"staging priority" (in ways which were not entirely straightforward)
with overrides to other concerns.
This has now being split along all the axis, with separate command
subsets for:
- staging prioritisation, now separated between `default`, `priority`,
and `alone`,
- `default` means PRs are picked by an unspecified order when
creating a staging, if nothing better is available
- `priority` means PRs are picked first when staging, however if
`priority` PRs don't fill the staging the rest will be filled with
`default`, this mode did not previously exist
- `alone` means the PRs are picked first, before splits, and only
`alone` PRs can be part of the staging (which usually matches the
modename)
- `skipchecks` overrides both statuses and approval checks, for the
batch, something previously implied in `p=0`, but now
independent. Setting `skipchecks` basically makes the entire batch
`ready`.
For consistency this also sets the reviewer implicitly: since
skipchecks overrides both statuses *and approval*, whoever enables
this mode is essentially the reviewer.
- `cancel` cancels any ongoing staging when the marked PR becomes
ready again, previously this was also implied (in a more restricted
form) by setting `p=0`
FWBot removal
=============
While the "forwardport bot" still exists as an API level (to segregate
access rights between tokens) it has been removed as an interaction
point, as part of the modules merge plan. As a result,
fwbot stops responding
----------------------
Feedback messages are now always sent by the mergebot, the
forward-porting bot should not send any message or notification
anymore.
commands moved to the merge bot
-------------------------------
- `ignore`/`up to` simply changes bot
- `close` as well
- `skipci` is now a choice / flag of an `fw` command, which denotes
the forward-port policy,
- `fw=default` is the old `ci` and resets the policy to default,
that is wait for the PR to be merged to create forward ports, and
for the required statuses on each forward port to be received
before creating the next
- `fw=skipci` is the old `skipci`, it waits for the merge of the
base PR but then creates all the forward ports immediately (unless
it gets a conflict)
- `fw=skipmerge` immediately creates all the forward ports, without
even waiting for the PR to be merged
This is a completely new mode, and may be rather broken as until
now the 'bot has always assumed the source PR had been merged.
approval rework
---------------
Because of the previous section, there is no distinguishing feature
between `mergebot r+` = "merge this PR" and `forwardbot r+` = "merge
this PR and all its parent with different access rights".
As a result, the two have been merged under a single `mergebot r+`
with heuristics attempting to provide the best experience:
- if approving a non-forward port, the behavior does not change
- else, with review rights on the source, all ancestors are approved
- else, as author of the original, approves all ancestors which descend
from a merged PR
- else, approves all ancestors up to and including the oldest ancestor
to which we have review rights
Most notably, the source's author is not delegated on the source or
any of its descendants anymore. This might need to be revisited if it
provides too restrictive.
For the very specialized need of approving a forward-port *and none of
its ancestors*, `review=` can now take a comma (`,`) separated list of
pull request numbers (github numbers, not mergebot ids).
Computed State
==============
The `state` field of pull requests is now computed. Hopefully this
makes the status more consistent and predictable in the long run, and
importantly makes status management more reliable (because reference
datum get updated naturally flowing to the state).
For now however it makes things more complicated as some of the states
have to be separately signaled or updated:
- `closed` and `error` are now separate flags
- `merge_date` is pulled down from forwardport and becomes the
transition signal for ready -> merged
- `reviewed_by` becomes the transition signal for approval (might be a
good idea to rename it...)
- `status` is computed from the head's statuses and overrides, and
*that* becomes the validation state
Ideally, batch-level flags like `skipchecks` should be on, well, the
batch, and `state` should have a dependency on the batch. However
currently the batch is not a durable / permanent member of the system,
so it's a PR-level flag and a messy pile.
On notable change is that *forcing* the state to `ready` now does that
but also sets the reviewer, `skipchecks`, and overrides to ensure the
API-mediated readying does not get rolled back by e.g. the runbot
sending a status.
This is useful for a few types of automated / programmatic PRs
e.g. translation exports, where we set the state programmatically to
limit noise.
recursive dependency hack
-------------------------
Given a sequence of PRs with an override of the source, if one of the
PRs is updated its descendants should not have the override
anymore. However if the updated PR gets overridden, its descendants
should have *that* override.
This requires some unholy manipulations via an override of `modified`,
as the ORM supports recursive fields but not recursive
dependencies (on a different field).
unconditional followup scheduling
---------------------------------
Previously scheduling forward-port followup was contigent on the FW
policy, but it's not actually correct if the new PR is *immediately*
validated (which can happen now that the field is computed, if there
are no required statuses *or* all of the required statuses are
overridden by an ancestor) as nothing will trigger the state change
and thus scheduling of the fp followup.
The followup function checks all the properties of the batch to port,
so this should not result on incorrect ports. Although it's a bit more
expensive, and will lead to more spam.
Previously this would not happen because on creation of a PR the
validation task (commit -> PR) would still have to execute.
Misc Changes
============
- If a PR is marked as overriding / canceling stagings, it now does
so on retry not just when setting initially.
This was not handled at all previously, so a PR in P0 going into
error due to e.g. a non-deterministic bug would be retried and still
p=0, but a current staging would not get cancelled. Same when a PR
in p=0 goes into error because something was failed, then is updated
with a fix.
- Add tracking to a bunch of relevant PR fields.
Post-mortem analysis currently generally requires going through the
text logs to see what happened, which is annoying.
There is a nondeterminism / inconsistency in the tracking which
sometimes leads the admin user to trigger tracking before the bot
does, leading to the staging tracking being attributed to them
during tests, shove under the carpet by ignoring the user to whom
that tracking is attributed.
When multiple users update tracked fields in the same transaction
all the changes are attributed to the first one having triggered
tracking (?), I couldn't find why the admin sometimes takes over.
- added and leveraged support for enum-backed selection fields
- moved variuous fields from forwardport to runbot_merge
- fix a migration which had never worked and which never run (because
I forgot to bump the version on the module)
- remove some unnecessary intermediate de/serialisation
fixes #673, fixes #309, fixes #792, fixes #846 (probably)
2023-10-31 13:42:07 +07:00
|
|
|
assert pr.priority == 'priority'
|
2018-03-27 21:39:29 +07:00
|
|
|
assert pr.state == 'approved'
|
2018-06-21 14:55:14 +07:00
|
|
|
|
2021-10-06 18:06:53 +07:00
|
|
|
def test_no_email(self, env, repo, users, config, partners):
|
|
|
|
"""A review should be rejected if the reviewer doesn't have an email
|
|
|
|
configured, otherwise the email address will show up
|
|
|
|
@users.noreply.github.com which is *weird*.
|
|
|
|
"""
|
|
|
|
with repo:
|
|
|
|
[m] = repo.make_commits(
|
|
|
|
None,
|
|
|
|
Commit('initial', tree={'m': '1'}),
|
|
|
|
ref='heads/master'
|
|
|
|
)
|
|
|
|
[c] = repo.make_commits(m, Commit('first', tree={'m': '2'}))
|
|
|
|
pr = repo.make_pr(target='master', head=c)
|
|
|
|
env.run_crons()
|
|
|
|
with repo:
|
|
|
|
pr.post_comment('hansen delegate+', config['role_reviewer']['token'])
|
|
|
|
pr.post_comment('hansen r+', config['role_user']['token'])
|
|
|
|
env.run_crons()
|
|
|
|
|
|
|
|
user_partner = env['res.partner'].search([('github_login', '=', users['user'])])
|
|
|
|
assert user_partner.email is False
|
|
|
|
assert pr.comments == [
|
|
|
|
seen(env, pr, users),
|
|
|
|
(users['reviewer'], 'hansen delegate+'),
|
|
|
|
(users['user'], 'hansen r+'),
|
2023-10-16 15:46:29 +07:00
|
|
|
(users['user'], f"@{users['user']} I must know your email before you can review PRs. Please contact an administrator."),
|
2021-10-06 18:06:53 +07:00
|
|
|
]
|
|
|
|
user_partner.fetch_github_email()
|
|
|
|
assert user_partner.email
|
|
|
|
with repo:
|
|
|
|
pr.post_comment('hansen r+', config['role_user']['token'])
|
|
|
|
env.run_crons()
|
|
|
|
assert to_pr(env, pr).state == 'approved'
|
|
|
|
|
2024-06-25 03:16:43 +07:00
|
|
|
@pytest.mark.usefixtures("reviewer_admin")
|
[CHG] *: rewrite commands set, rework status management
This commit revisits the commands set in order to make it more
regular, and limit inconsistent command-sets, although it includes
pseudo-command aliases for common tasks now removed from the core set.
Hard Errors
===========
The previous iteration of the commands set would ignore any
non-command term in a command line. This has been changed to hard
error (and ignoring the entire thing) if any command is unknown or
invalid.
This fixes inconsistent / unexpected interpretations where a user
sends a command, then writes a novel on the same line some words of
which happen to *also* be commands, leading to merge states they did
not expect. They should now be told to fuck off.
Priority Restructuring
----------------------
The numerical priority system was pretty messy in that it confused
"staging priority" (in ways which were not entirely straightforward)
with overrides to other concerns.
This has now being split along all the axis, with separate command
subsets for:
- staging prioritisation, now separated between `default`, `priority`,
and `alone`,
- `default` means PRs are picked by an unspecified order when
creating a staging, if nothing better is available
- `priority` means PRs are picked first when staging, however if
`priority` PRs don't fill the staging the rest will be filled with
`default`, this mode did not previously exist
- `alone` means the PRs are picked first, before splits, and only
`alone` PRs can be part of the staging (which usually matches the
modename)
- `skipchecks` overrides both statuses and approval checks, for the
batch, something previously implied in `p=0`, but now
independent. Setting `skipchecks` basically makes the entire batch
`ready`.
For consistency this also sets the reviewer implicitly: since
skipchecks overrides both statuses *and approval*, whoever enables
this mode is essentially the reviewer.
- `cancel` cancels any ongoing staging when the marked PR becomes
ready again, previously this was also implied (in a more restricted
form) by setting `p=0`
FWBot removal
=============
While the "forwardport bot" still exists as an API level (to segregate
access rights between tokens) it has been removed as an interaction
point, as part of the modules merge plan. As a result,
fwbot stops responding
----------------------
Feedback messages are now always sent by the mergebot, the
forward-porting bot should not send any message or notification
anymore.
commands moved to the merge bot
-------------------------------
- `ignore`/`up to` simply changes bot
- `close` as well
- `skipci` is now a choice / flag of an `fw` command, which denotes
the forward-port policy,
- `fw=default` is the old `ci` and resets the policy to default,
that is wait for the PR to be merged to create forward ports, and
for the required statuses on each forward port to be received
before creating the next
- `fw=skipci` is the old `skipci`, it waits for the merge of the
base PR but then creates all the forward ports immediately (unless
it gets a conflict)
- `fw=skipmerge` immediately creates all the forward ports, without
even waiting for the PR to be merged
This is a completely new mode, and may be rather broken as until
now the 'bot has always assumed the source PR had been merged.
approval rework
---------------
Because of the previous section, there is no distinguishing feature
between `mergebot r+` = "merge this PR" and `forwardbot r+` = "merge
this PR and all its parent with different access rights".
As a result, the two have been merged under a single `mergebot r+`
with heuristics attempting to provide the best experience:
- if approving a non-forward port, the behavior does not change
- else, with review rights on the source, all ancestors are approved
- else, as author of the original, approves all ancestors which descend
from a merged PR
- else, approves all ancestors up to and including the oldest ancestor
to which we have review rights
Most notably, the source's author is not delegated on the source or
any of its descendants anymore. This might need to be revisited if it
provides too restrictive.
For the very specialized need of approving a forward-port *and none of
its ancestors*, `review=` can now take a comma (`,`) separated list of
pull request numbers (github numbers, not mergebot ids).
Computed State
==============
The `state` field of pull requests is now computed. Hopefully this
makes the status more consistent and predictable in the long run, and
importantly makes status management more reliable (because reference
datum get updated naturally flowing to the state).
For now however it makes things more complicated as some of the states
have to be separately signaled or updated:
- `closed` and `error` are now separate flags
- `merge_date` is pulled down from forwardport and becomes the
transition signal for ready -> merged
- `reviewed_by` becomes the transition signal for approval (might be a
good idea to rename it...)
- `status` is computed from the head's statuses and overrides, and
*that* becomes the validation state
Ideally, batch-level flags like `skipchecks` should be on, well, the
batch, and `state` should have a dependency on the batch. However
currently the batch is not a durable / permanent member of the system,
so it's a PR-level flag and a messy pile.
On notable change is that *forcing* the state to `ready` now does that
but also sets the reviewer, `skipchecks`, and overrides to ensure the
API-mediated readying does not get rolled back by e.g. the runbot
sending a status.
This is useful for a few types of automated / programmatic PRs
e.g. translation exports, where we set the state programmatically to
limit noise.
recursive dependency hack
-------------------------
Given a sequence of PRs with an override of the source, if one of the
PRs is updated its descendants should not have the override
anymore. However if the updated PR gets overridden, its descendants
should have *that* override.
This requires some unholy manipulations via an override of `modified`,
as the ORM supports recursive fields but not recursive
dependencies (on a different field).
unconditional followup scheduling
---------------------------------
Previously scheduling forward-port followup was contigent on the FW
policy, but it's not actually correct if the new PR is *immediately*
validated (which can happen now that the field is computed, if there
are no required statuses *or* all of the required statuses are
overridden by an ancestor) as nothing will trigger the state change
and thus scheduling of the fp followup.
The followup function checks all the properties of the batch to port,
so this should not result on incorrect ports. Although it's a bit more
expensive, and will lead to more spam.
Previously this would not happen because on creation of a PR the
validation task (commit -> PR) would still have to execute.
Misc Changes
============
- If a PR is marked as overriding / canceling stagings, it now does
so on retry not just when setting initially.
This was not handled at all previously, so a PR in P0 going into
error due to e.g. a non-deterministic bug would be retried and still
p=0, but a current staging would not get cancelled. Same when a PR
in p=0 goes into error because something was failed, then is updated
with a fix.
- Add tracking to a bunch of relevant PR fields.
Post-mortem analysis currently generally requires going through the
text logs to see what happened, which is annoying.
There is a nondeterminism / inconsistency in the tracking which
sometimes leads the admin user to trigger tracking before the bot
does, leading to the staging tracking being attributed to them
during tests, shove under the carpet by ignoring the user to whom
that tracking is attributed.
When multiple users update tracked fields in the same transaction
all the changes are attributed to the first one having triggered
tracking (?), I couldn't find why the admin sometimes takes over.
- added and leveraged support for enum-backed selection fields
- moved variuous fields from forwardport to runbot_merge
- fix a migration which had never worked and which never run (because
I forgot to bump the version on the module)
- remove some unnecessary intermediate de/serialisation
fixes #673, fixes #309, fixes #792, fixes #846 (probably)
2023-10-31 13:42:07 +07:00
|
|
|
def test_skipchecks(self, env, repo, users, config):
|
|
|
|
"""Skipcheck makes the PR immediately ready (if it's not in error or
|
|
|
|
something)
|
|
|
|
"""
|
|
|
|
with repo:
|
|
|
|
[m, _] = repo.make_commits(
|
|
|
|
None,
|
|
|
|
Commit("initial", tree={'m': 'm'}),
|
|
|
|
Commit("second", tree={"m2": "m2"}),
|
|
|
|
ref="heads/master"
|
|
|
|
)
|
|
|
|
|
|
|
|
[c1] = repo.make_commits(m, Commit('first', tree={'m': 'c1'}))
|
|
|
|
pr = repo.make_pr(title='title', target='master', head=c1)
|
|
|
|
pr.post_comment('hansen skipchecks', config['role_reviewer']['token'])
|
|
|
|
env.run_crons()
|
|
|
|
|
|
|
|
pr_id = to_pr(env, pr)
|
|
|
|
# assert pr_id.state == 'ready'
|
|
|
|
assert not pr_id.blocked
|
|
|
|
# since the pr is not blocked it should have been staged by the relevant cron
|
|
|
|
assert pr_id.staging_id
|
2021-10-06 18:06:53 +07:00
|
|
|
|
2018-06-21 14:55:14 +07:00
|
|
|
class TestUnknownPR:
|
|
|
|
""" Sync PRs initially looked excellent but aside from the v4 API not
|
|
|
|
being stable yet, it seems to have greatly regressed in performances to
|
|
|
|
the extent that it's almost impossible to sync odoo/odoo today: trying to
|
|
|
|
fetch more than 2 PRs per query will fail semi-randomly at one point, so
|
|
|
|
fetching all 15000 PRs takes hours
|
|
|
|
|
|
|
|
=> instead, create PRs on the fly when getting notifications related to
|
|
|
|
valid but unknown PRs
|
|
|
|
"""
|
[FIX] runbot_merge: cancel approval (r+) when fetching PRs
When retrieving unknown PRs, the process would apply all comments,
thereby applying eventual r+ without taking in account their
relationship to a force push. This means it was possible for a
mergebot-unknown PR to be r+'d, updated, retargeted, and the mergetbot
would consider it good to go.
The possible damage would be somewhat limited but still, not great.
Sadly Github simply doesn't provide access to the entire event stream
of the PR, so there is no way to even know whether the PR was updated,
let alone when in relation to comments. Therefore just resync the PR
after having applied comments: we still want to apply the merge method
& al, we just want to reset back to un-approved.
An other minor fix (for something we never actually hit but could):
reviews are treated more or less as comments, but separate at github's
level. The job would apply all comments then all reviews, so the
relative order of comments and reviews would be wrong.
Combine and order comments and reviews so they are applied
in (hopefully) the correct order of their creation / submission.
Closes #416
2020-11-10 22:13:08 +07:00
|
|
|
def test_rplus_unknown(self, repo, env, config, users):
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
m = repo.make_commit(None, 'initial', None, tree={'m': 'm'})
|
|
|
|
m2 = repo.make_commit(m, 'second', None, tree={'m': 'm', 'm2': 'm2'})
|
|
|
|
repo.make_ref('heads/master', m2)
|
|
|
|
|
|
|
|
c1 = repo.make_commit(m, 'first', None, tree={'m': 'c1'})
|
|
|
|
prx = repo.make_pr(title='title', body='body', target='master', head=c1)
|
|
|
|
repo.post_status(prx.head, 'success', 'legal/cla')
|
|
|
|
repo.post_status(prx.head, 'success', 'ci/runbot', target_url="http://example.org/wheee")
|
|
|
|
env.run_crons()
|
2018-09-17 16:04:31 +07:00
|
|
|
|
2018-06-21 14:55:14 +07:00
|
|
|
# assume an unknown but ready PR: we don't know the PR or its head commit
|
2024-12-02 20:17:28 +07:00
|
|
|
to_pr(env, prx).unlink()
|
2018-06-21 14:55:14 +07:00
|
|
|
env['runbot_merge.commit'].search([('sha', '=', prx.head)]).unlink()
|
|
|
|
|
|
|
|
# reviewer reviewers
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
prx.post_comment('hansen r+', config['role_reviewer']['token'])
|
[FIX] runbot_merge: cancel approval (r+) when fetching PRs
When retrieving unknown PRs, the process would apply all comments,
thereby applying eventual r+ without taking in account their
relationship to a force push. This means it was possible for a
mergebot-unknown PR to be r+'d, updated, retargeted, and the mergetbot
would consider it good to go.
The possible damage would be somewhat limited but still, not great.
Sadly Github simply doesn't provide access to the entire event stream
of the PR, so there is no way to even know whether the PR was updated,
let alone when in relation to comments. Therefore just resync the PR
after having applied comments: we still want to apply the merge method
& al, we just want to reset back to un-approved.
An other minor fix (for something we never actually hit but could):
reviews are treated more or less as comments, but separate at github's
level. The job would apply all comments then all reviews, so the
relative order of comments and reviews would be wrong.
Combine and order comments and reviews so they are applied
in (hopefully) the correct order of their creation / submission.
Closes #416
2020-11-10 22:13:08 +07:00
|
|
|
with repo:
|
|
|
|
prx.post_review('REQUEST_CHANGES', 'hansen r-', config['role_reviewer']['token'])
|
|
|
|
with repo:
|
|
|
|
prx.post_comment('hansen r+', config['role_reviewer']['token'])
|
2018-06-21 14:55:14 +07:00
|
|
|
|
|
|
|
Fetch = env['runbot_merge.fetch_job']
|
[FIX] runbot_merge: cancel approval (r+) when fetching PRs
When retrieving unknown PRs, the process would apply all comments,
thereby applying eventual r+ without taking in account their
relationship to a force push. This means it was possible for a
mergebot-unknown PR to be r+'d, updated, retargeted, and the mergetbot
would consider it good to go.
The possible damage would be somewhat limited but still, not great.
Sadly Github simply doesn't provide access to the entire event stream
of the PR, so there is no way to even know whether the PR was updated,
let alone when in relation to comments. Therefore just resync the PR
after having applied comments: we still want to apply the merge method
& al, we just want to reset back to un-approved.
An other minor fix (for something we never actually hit but could):
reviews are treated more or less as comments, but separate at github's
level. The job would apply all comments then all reviews, so the
relative order of comments and reviews would be wrong.
Combine and order comments and reviews so they are applied
in (hopefully) the correct order of their creation / submission.
Closes #416
2020-11-10 22:13:08 +07:00
|
|
|
fetches = Fetch.search([('repository', '=', repo.name), ('number', '=', prx.number)])
|
|
|
|
assert len(fetches) == 1, f"expected one fetch for {prx.number}, found {len(fetches)}"
|
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
env.run_crons('runbot_merge.fetch_prs_cron')
|
|
|
|
env.run_crons()
|
2018-06-21 14:55:14 +07:00
|
|
|
assert not Fetch.search([('repository', '=', repo.name), ('number', '=', prx.number)])
|
|
|
|
|
2018-09-17 16:04:31 +07:00
|
|
|
c = env['runbot_merge.commit'].search([('sha', '=', prx.head)])
|
|
|
|
assert json.loads(c.statuses) == {
|
2024-09-20 17:17:17 +07:00
|
|
|
'legal/cla': {'state': 'success', 'target_url': None, 'description': None, 'updated_at': matches("$$")},
|
|
|
|
'ci/runbot': {'state': 'success', 'target_url': 'http://example.org/wheee', 'description': None, 'updated_at': matches("$$")}
|
2018-09-17 16:04:31 +07:00
|
|
|
}
|
[FIX] runbot_merge: cancel approval (r+) when fetching PRs
When retrieving unknown PRs, the process would apply all comments,
thereby applying eventual r+ without taking in account their
relationship to a force push. This means it was possible for a
mergebot-unknown PR to be r+'d, updated, retargeted, and the mergetbot
would consider it good to go.
The possible damage would be somewhat limited but still, not great.
Sadly Github simply doesn't provide access to the entire event stream
of the PR, so there is no way to even know whether the PR was updated,
let alone when in relation to comments. Therefore just resync the PR
after having applied comments: we still want to apply the merge method
& al, we just want to reset back to un-approved.
An other minor fix (for something we never actually hit but could):
reviews are treated more or less as comments, but separate at github's
level. The job would apply all comments then all reviews, so the
relative order of comments and reviews would be wrong.
Combine and order comments and reviews so they are applied
in (hopefully) the correct order of their creation / submission.
Closes #416
2020-11-10 22:13:08 +07:00
|
|
|
assert prx.comments == [
|
2020-11-17 21:21:21 +07:00
|
|
|
seen(env, prx, users),
|
[FIX] runbot_merge: cancel approval (r+) when fetching PRs
When retrieving unknown PRs, the process would apply all comments,
thereby applying eventual r+ without taking in account their
relationship to a force push. This means it was possible for a
mergebot-unknown PR to be r+'d, updated, retargeted, and the mergetbot
would consider it good to go.
The possible damage would be somewhat limited but still, not great.
Sadly Github simply doesn't provide access to the entire event stream
of the PR, so there is no way to even know whether the PR was updated,
let alone when in relation to comments. Therefore just resync the PR
after having applied comments: we still want to apply the merge method
& al, we just want to reset back to un-approved.
An other minor fix (for something we never actually hit but could):
reviews are treated more or less as comments, but separate at github's
level. The job would apply all comments then all reviews, so the
relative order of comments and reviews would be wrong.
Combine and order comments and reviews so they are applied
in (hopefully) the correct order of their creation / submission.
Closes #416
2020-11-10 22:13:08 +07:00
|
|
|
(users['reviewer'], 'hansen r+'),
|
|
|
|
(users['reviewer'], 'hansen r+'),
|
2023-06-12 19:41:42 +07:00
|
|
|
seen(env, prx, users),
|
2024-11-18 19:52:27 +07:00
|
|
|
(users['user'], f"@{users['reviewer']} I didn't know about this PR and had to "
|
[FIX] runbot_merge: cancel approval (r+) when fetching PRs
When retrieving unknown PRs, the process would apply all comments,
thereby applying eventual r+ without taking in account their
relationship to a force push. This means it was possible for a
mergebot-unknown PR to be r+'d, updated, retargeted, and the mergetbot
would consider it good to go.
The possible damage would be somewhat limited but still, not great.
Sadly Github simply doesn't provide access to the entire event stream
of the PR, so there is no way to even know whether the PR was updated,
let alone when in relation to comments. Therefore just resync the PR
after having applied comments: we still want to apply the merge method
& al, we just want to reset back to un-approved.
An other minor fix (for something we never actually hit but could):
reviews are treated more or less as comments, but separate at github's
level. The job would apply all comments then all reviews, so the
relative order of comments and reviews would be wrong.
Combine and order comments and reviews so they are applied
in (hopefully) the correct order of their creation / submission.
Closes #416
2020-11-10 22:13:08 +07:00
|
|
|
"retrieve its information, you may have to "
|
2022-06-23 19:25:07 +07:00
|
|
|
"re-approve it as I didn't see previous commands."),
|
[FIX] runbot_merge: cancel approval (r+) when fetching PRs
When retrieving unknown PRs, the process would apply all comments,
thereby applying eventual r+ without taking in account their
relationship to a force push. This means it was possible for a
mergebot-unknown PR to be r+'d, updated, retargeted, and the mergetbot
would consider it good to go.
The possible damage would be somewhat limited but still, not great.
Sadly Github simply doesn't provide access to the entire event stream
of the PR, so there is no way to even know whether the PR was updated,
let alone when in relation to comments. Therefore just resync the PR
after having applied comments: we still want to apply the merge method
& al, we just want to reset back to un-approved.
An other minor fix (for something we never actually hit but could):
reviews are treated more or less as comments, but separate at github's
level. The job would apply all comments then all reviews, so the
relative order of comments and reviews would be wrong.
Combine and order comments and reviews so they are applied
in (hopefully) the correct order of their creation / submission.
Closes #416
2020-11-10 22:13:08 +07:00
|
|
|
]
|
2018-09-17 16:04:31 +07:00
|
|
|
|
2024-12-02 20:17:28 +07:00
|
|
|
pr = to_pr(env, prx)
|
[FIX] runbot_merge: cancel approval (r+) when fetching PRs
When retrieving unknown PRs, the process would apply all comments,
thereby applying eventual r+ without taking in account their
relationship to a force push. This means it was possible for a
mergebot-unknown PR to be r+'d, updated, retargeted, and the mergetbot
would consider it good to go.
The possible damage would be somewhat limited but still, not great.
Sadly Github simply doesn't provide access to the entire event stream
of the PR, so there is no way to even know whether the PR was updated,
let alone when in relation to comments. Therefore just resync the PR
after having applied comments: we still want to apply the merge method
& al, we just want to reset back to un-approved.
An other minor fix (for something we never actually hit but could):
reviews are treated more or less as comments, but separate at github's
level. The job would apply all comments then all reviews, so the
relative order of comments and reviews would be wrong.
Combine and order comments and reviews so they are applied
in (hopefully) the correct order of their creation / submission.
Closes #416
2020-11-10 22:13:08 +07:00
|
|
|
assert pr.state == 'validated'
|
2018-06-21 14:55:14 +07:00
|
|
|
|
[FIX] runbot_merge: cancel approval (r+) when fetching PRs
When retrieving unknown PRs, the process would apply all comments,
thereby applying eventual r+ without taking in account their
relationship to a force push. This means it was possible for a
mergebot-unknown PR to be r+'d, updated, retargeted, and the mergetbot
would consider it good to go.
The possible damage would be somewhat limited but still, not great.
Sadly Github simply doesn't provide access to the entire event stream
of the PR, so there is no way to even know whether the PR was updated,
let alone when in relation to comments. Therefore just resync the PR
after having applied comments: we still want to apply the merge method
& al, we just want to reset back to un-approved.
An other minor fix (for something we never actually hit but could):
reviews are treated more or less as comments, but separate at github's
level. The job would apply all comments then all reviews, so the
relative order of comments and reviews would be wrong.
Combine and order comments and reviews so they are applied
in (hopefully) the correct order of their creation / submission.
Closes #416
2020-11-10 22:13:08 +07:00
|
|
|
with repo:
|
|
|
|
prx.post_comment('hansen r+', config['role_reviewer']['token'])
|
|
|
|
assert pr.state == 'ready'
|
2018-08-28 20:42:28 +07:00
|
|
|
|
2021-07-30 14:20:57 +07:00
|
|
|
def test_fetch_closed(self, env, repo, users, config):
|
|
|
|
""" If an "unknown PR" is fetched while closed, it should be saved as
|
|
|
|
closed
|
|
|
|
"""
|
|
|
|
with repo:
|
|
|
|
m, _ = repo.make_commits(
|
|
|
|
None,
|
|
|
|
Commit('initial', tree={'m': 'm'}),
|
|
|
|
Commit('second', tree={'m2': 'm2'}),
|
|
|
|
ref='heads/master')
|
|
|
|
|
|
|
|
[c1] = repo.make_commits(m, Commit('first', tree={'m': 'c1'}))
|
|
|
|
pr = repo.make_pr(title='title', body='body', target='master', head=c1)
|
|
|
|
env.run_crons()
|
|
|
|
with repo:
|
|
|
|
pr.close()
|
|
|
|
|
|
|
|
# assume an unknown but ready PR: we don't know the PR or its head commit
|
|
|
|
to_pr(env, pr).unlink()
|
|
|
|
env['runbot_merge.commit'].search([('sha', '=', pr.head)]).unlink()
|
|
|
|
|
|
|
|
# reviewer reviewers
|
|
|
|
with repo:
|
|
|
|
pr.post_comment('hansen r+', config['role_reviewer']['token'])
|
|
|
|
|
|
|
|
Fetch = env['runbot_merge.fetch_job']
|
|
|
|
fetches = Fetch.search([('repository', '=', repo.name), ('number', '=', pr.number)])
|
|
|
|
assert len(fetches) == 1, f"expected one fetch for {pr.number}, found {len(fetches)}"
|
|
|
|
|
|
|
|
env.run_crons('runbot_merge.fetch_prs_cron')
|
|
|
|
env.run_crons()
|
|
|
|
assert not Fetch.search([('repository', '=', repo.name), ('number', '=', pr.number)])
|
|
|
|
|
|
|
|
assert to_pr(env, pr).state == 'closed'
|
|
|
|
assert pr.comments == [
|
|
|
|
seen(env, pr, users),
|
|
|
|
(users['reviewer'], 'hansen r+'),
|
2023-06-12 19:41:42 +07:00
|
|
|
seen(env, pr, users),
|
|
|
|
# reviewer is set because fetch replays all the comments (thus
|
|
|
|
# setting r+ and reviewer) but then syncs the head commit thus
|
|
|
|
# unsetting r+ but leaving the reviewer
|
2024-11-18 19:52:27 +07:00
|
|
|
(users['user'], f"@{users['reviewer']} I didn't know about this PR and had to retrieve "
|
2022-06-23 19:25:07 +07:00
|
|
|
"its information, you may have to re-approve it "
|
|
|
|
"as I didn't see previous commands."),
|
2021-07-30 14:20:57 +07:00
|
|
|
]
|
|
|
|
|
2024-03-12 18:17:30 +07:00
|
|
|
def test_close_unknown_unmanaged(self, env, repo, users, config):
|
|
|
|
"""If an "unknown PR" is *closed*, it should be saved as closed but not
|
|
|
|
commented on, because that's unnecessary spam.
|
|
|
|
"""
|
|
|
|
with repo:
|
|
|
|
m, _ = repo.make_commits(
|
|
|
|
None,
|
|
|
|
Commit('initial', tree={'m': 'm'}),
|
|
|
|
Commit('second', tree={'m2': 'm2'}),
|
|
|
|
ref='heads/master')
|
|
|
|
|
|
|
|
[c1] = repo.make_commits(m, Commit('first', tree={'m': 'c1'}))
|
|
|
|
pr = repo.make_pr(title='title', body='body', target='master', head=c1)
|
|
|
|
env.run_crons()
|
|
|
|
assert pr.comments == [seen(env, pr, users)]
|
|
|
|
|
|
|
|
to_pr(env, pr).unlink()
|
|
|
|
env['runbot_merge.commit'].search([('sha', '=', pr.head)]).unlink()
|
|
|
|
|
|
|
|
with repo:
|
|
|
|
pr.close()
|
|
|
|
|
|
|
|
Fetch = env['runbot_merge.fetch_job']
|
|
|
|
fetches = Fetch.search([('repository', '=', repo.name), ('number', '=', pr.number)])
|
|
|
|
assert len(fetches) == 1, f"expected one fetch for {pr.number}, found {len(fetches)}"
|
|
|
|
|
|
|
|
env.run_crons('runbot_merge.fetch_prs_cron')
|
|
|
|
env.run_crons()
|
|
|
|
assert not Fetch.search([('repository', '=', repo.name), ('number', '=', pr.number)])
|
|
|
|
|
|
|
|
assert to_pr(env, pr).state == 'closed'
|
|
|
|
assert pr.comments == [seen(env, pr, users)]
|
|
|
|
|
|
|
|
|
|
|
|
def test_close_unknown_disabled(self, env, repo, users, config):
|
|
|
|
"""If an "unknown PR" on an disabled branch is *closed*, it should be
|
|
|
|
saved as closed but not commented on, because that's unnecessary spam.
|
|
|
|
"""
|
|
|
|
with repo:
|
|
|
|
m, _ = repo.make_commits(
|
|
|
|
None,
|
|
|
|
Commit('initial', tree={'m': 'm'}),
|
|
|
|
Commit('second', tree={'m2': 'm2'}),
|
|
|
|
ref='heads/master')
|
|
|
|
|
|
|
|
[c1] = repo.make_commits(m, Commit('first', tree={'m': 'c1'}))
|
|
|
|
pr = repo.make_pr(title='title', body='body', target='master', head=c1)
|
|
|
|
env.run_crons()
|
|
|
|
assert pr.comments == [seen(env, pr, users)]
|
|
|
|
|
|
|
|
to_pr(env, pr).unlink()
|
|
|
|
env['runbot_merge.commit'].search([('sha', '=', pr.head)]).unlink()
|
|
|
|
env['runbot_merge.branch'].search([('name', '=', 'master')]).active = False
|
|
|
|
|
|
|
|
with repo:
|
|
|
|
pr.close()
|
|
|
|
|
|
|
|
Fetch = env['runbot_merge.fetch_job']
|
|
|
|
fetches = Fetch.search([('repository', '=', repo.name), ('number', '=', pr.number)])
|
|
|
|
assert len(fetches) == 1, f"expected one fetch for {pr.number}, found {len(fetches)}"
|
|
|
|
|
|
|
|
env.run_crons('runbot_merge.fetch_prs_cron')
|
|
|
|
env.run_crons()
|
|
|
|
assert not Fetch.search([('repository', '=', repo.name), ('number', '=', pr.number)])
|
|
|
|
|
|
|
|
assert to_pr(env, pr).state == 'closed'
|
|
|
|
assert pr.comments == [seen(env, pr, users)]
|
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
def test_rplus_unmanaged(self, env, repo, users, config):
|
2018-10-16 17:40:45 +07:00
|
|
|
""" r+ on an unmanaged target should notify about
|
|
|
|
"""
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
m = repo.make_commit(None, 'initial', None, tree={'m': 'm'})
|
|
|
|
m2 = repo.make_commit(m, 'second', None, tree={'m': 'm', 'm2': 'm2'})
|
|
|
|
repo.make_ref('heads/branch', m2)
|
|
|
|
|
|
|
|
c1 = repo.make_commit(m, 'first', None, tree={'m': 'c1'})
|
|
|
|
prx = repo.make_pr(title='title', body='body', target='branch', head=c1)
|
|
|
|
repo.post_status(prx.head, 'success', 'legal/cla')
|
|
|
|
repo.post_status(prx.head, 'success', 'ci/runbot')
|
|
|
|
|
|
|
|
prx.post_comment('hansen r+', config['role_reviewer']['token'])
|
|
|
|
env.run_crons(
|
|
|
|
'runbot_merge.fetch_prs_cron',
|
|
|
|
)
|
2018-10-16 17:40:45 +07:00
|
|
|
|
|
|
|
assert prx.comments == [
|
|
|
|
(users['reviewer'], 'hansen r+'),
|
2022-07-29 17:37:23 +07:00
|
|
|
(users['user'], "This PR targets the un-managed branch %s:branch, it needs to be retargeted before it can be merged." % repo.name),
|
2022-06-23 19:25:07 +07:00
|
|
|
(users['user'], "Branch `branch` is not within my remit, imma just ignore it."),
|
2018-10-16 17:40:45 +07:00
|
|
|
]
|
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
def test_rplus_review_unmanaged(self, env, repo, users, config):
|
2018-10-16 17:40:45 +07:00
|
|
|
""" r+ reviews can take a different path than comments
|
|
|
|
"""
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
m = repo.make_commit(None, 'initial', None, tree={'m': 'm'})
|
|
|
|
m2 = repo.make_commit(m, 'second', None, tree={'m': 'm', 'm2': 'm2'})
|
|
|
|
repo.make_ref('heads/branch', m2)
|
|
|
|
|
|
|
|
c1 = repo.make_commit(m, 'first', None, tree={'m': 'c1'})
|
|
|
|
prx = repo.make_pr(title='title', body='body', target='branch', head=c1)
|
|
|
|
repo.post_status(prx.head, 'success', 'legal/cla')
|
|
|
|
repo.post_status(prx.head, 'success', 'ci/runbot')
|
|
|
|
|
|
|
|
prx.post_review('APPROVE', 'hansen r+', config['role_reviewer']['token'])
|
|
|
|
env.run_crons(
|
|
|
|
'runbot_merge.fetch_prs_cron',
|
|
|
|
)
|
2018-10-16 17:40:45 +07:00
|
|
|
|
|
|
|
# FIXME: either split out reviews in local or merge reviews & comments in remote
|
|
|
|
assert prx.comments[-1:] == [
|
|
|
|
(users['user'], "I'm sorry. Branch `branch` is not within my remit."),
|
|
|
|
]
|
|
|
|
|
2019-08-26 18:41:33 +07:00
|
|
|
class TestRecognizeCommands:
|
|
|
|
@pytest.mark.parametrize('botname', ['hansen', 'Hansen', 'HANSEN', 'HanSen', 'hAnSeN'])
|
2019-10-10 14:22:12 +07:00
|
|
|
def test_botname_casing(self, repo, env, botname, config):
|
2019-08-26 18:41:33 +07:00
|
|
|
""" Test that the botname is case-insensitive as people might write
|
|
|
|
bot names capitalised or titlecased or uppercased or whatever
|
|
|
|
"""
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
m = repo.make_commit(None, 'initial', None, tree={'m': 'm'})
|
|
|
|
repo.make_ref('heads/master', m)
|
2019-08-26 18:41:33 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
c = repo.make_commit(m, 'first', None, tree={'m': 'c'})
|
|
|
|
prx = repo.make_pr(title='title', body=None, target='master', head=c)
|
2019-08-26 18:41:33 +07:00
|
|
|
|
2024-12-02 20:17:28 +07:00
|
|
|
pr = to_pr(env, prx)
|
2019-08-26 18:41:33 +07:00
|
|
|
assert pr.state == 'opened'
|
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
prx.post_comment('%s r+' % botname, config['role_reviewer']['token'])
|
2019-08-26 18:41:33 +07:00
|
|
|
assert pr.state == 'approved'
|
|
|
|
|
|
|
|
@pytest.mark.parametrize('indent', ['', '\N{SPACE}', '\N{SPACE}'*4, '\N{TAB}'])
|
2019-10-10 14:22:12 +07:00
|
|
|
def test_botname_indented(self, repo, env, indent, config):
|
2019-08-26 18:41:33 +07:00
|
|
|
""" matching botname should ignore leading whitespaces
|
|
|
|
"""
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
2024-12-02 20:17:28 +07:00
|
|
|
m, c = repo.make_commits(
|
|
|
|
None,
|
|
|
|
Commit('initial', tree={'m': 'm'}),
|
|
|
|
Commit('first', tree={'m': 'c'}),
|
|
|
|
)
|
2019-10-10 14:22:12 +07:00
|
|
|
repo.make_ref('heads/master', m)
|
2024-12-02 20:17:28 +07:00
|
|
|
prx = repo.make_pr(title='title', target='master', head=c)
|
2019-08-26 18:41:33 +07:00
|
|
|
|
2024-12-02 20:17:28 +07:00
|
|
|
pr = to_pr(env, prx)
|
2019-08-26 18:41:33 +07:00
|
|
|
assert pr.state == 'opened'
|
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
2024-12-02 20:17:28 +07:00
|
|
|
prx.post_comment(f'{indent}hansen r+', config['role_reviewer']['token'])
|
2019-08-26 18:41:33 +07:00
|
|
|
assert pr.state == 'approved'
|
|
|
|
|
2020-07-22 16:56:33 +07:00
|
|
|
def test_unknown_commands(self, repo, env, config, users):
|
|
|
|
with repo:
|
|
|
|
m = repo.make_commit(None, 'initial', None, tree={'m': 'm'})
|
|
|
|
repo.make_ref('heads/master', m)
|
|
|
|
|
|
|
|
c = repo.make_commit(m, 'first', None, tree={'m': 'c'})
|
|
|
|
pr = repo.make_pr(title='title', body=None, target='master', head=c)
|
|
|
|
pr.post_comment("hansen do the thing", config['role_reviewer']['token'])
|
|
|
|
pr.post_comment('hansen @bobby-b r+ :+1:', config['role_reviewer']['token'])
|
|
|
|
env.run_crons()
|
|
|
|
|
|
|
|
assert pr.comments == [
|
|
|
|
(users['reviewer'], "hansen do the thing"),
|
|
|
|
(users['reviewer'], "hansen @bobby-b r+ :+1:"),
|
2020-11-17 21:21:21 +07:00
|
|
|
seen(env, pr, users),
|
2024-06-25 03:16:43 +07:00
|
|
|
(users['user'], """\
|
|
|
|
@{reviewer} unknown command 'do'.
|
|
|
|
|
|
|
|
For your own safety I've ignored *everything in your entire comment*.
|
|
|
|
|
|
|
|
Currently available commands:
|
|
|
|
|
|
|
|
|command||
|
|
|
|
|-|-|
|
|
|
|
|`help`|displays this help|
|
|
|
|
|`r(eview)+`|approves the PR, if it's a forwardport also approves all non-detached parents|
|
|
|
|
|`r(eview)=<number>`|only approves the specified parents|
|
|
|
|
|`fw=no`|does not forward-port this PR|
|
|
|
|
|`fw=default`|forward-ports this PR normally|
|
|
|
|
|`fw=skipci`|does not wait for a forward-port's statuses to succeed before creating the next one|
|
|
|
|
|`up to <branch>`|only ports this PR forward to the specified branch (included)|
|
|
|
|
|`merge`|integrate the PR with a simple merge commit, using the PR description as message|
|
|
|
|
|`rebase-merge`|rebases the PR on top of the target branch the integrates with a merge commit, using the PR description as message|
|
|
|
|
|`rebase-ff`|rebases the PR on top of the target branch, then fast-forwards|
|
|
|
|
|`squash`|squashes the PR as a single commit on the target branch, using the PR description as message|
|
|
|
|
|`delegate+`|grants approval rights to the PR author|
|
|
|
|
|`delegate=<...>`|grants approval rights on this PR to the specified github users|
|
|
|
|
|`default`|stages the PR normally|
|
|
|
|
|`priority`|tries to stage this PR first, then adds `default` PRs if the staging has room|
|
|
|
|
|`alone`|stages this PR only with other PRs of the same priority|
|
|
|
|
|`cancel=staging`|automatically cancels the current staging when this PR becomes ready|
|
|
|
|
|`check`|fetches or refreshes PR metadata, resets mergebot state|
|
|
|
|
|
|
|
|
Note: this help text is dynamic and will change with the state of the PR.
|
|
|
|
""".format_map(users)),
|
|
|
|
(users['user'], """\
|
|
|
|
@{reviewer} unknown command '@bobby-b'.
|
|
|
|
|
|
|
|
For your own safety I've ignored *everything in your entire comment*.
|
|
|
|
|
|
|
|
Currently available commands:
|
|
|
|
|
|
|
|
|command||
|
|
|
|
|-|-|
|
|
|
|
|`help`|displays this help|
|
|
|
|
|`r(eview)+`|approves the PR, if it's a forwardport also approves all non-detached parents|
|
|
|
|
|`r(eview)=<number>`|only approves the specified parents|
|
|
|
|
|`fw=no`|does not forward-port this PR|
|
|
|
|
|`fw=default`|forward-ports this PR normally|
|
|
|
|
|`fw=skipci`|does not wait for a forward-port's statuses to succeed before creating the next one|
|
|
|
|
|`up to <branch>`|only ports this PR forward to the specified branch (included)|
|
|
|
|
|`merge`|integrate the PR with a simple merge commit, using the PR description as message|
|
|
|
|
|`rebase-merge`|rebases the PR on top of the target branch the integrates with a merge commit, using the PR description as message|
|
|
|
|
|`rebase-ff`|rebases the PR on top of the target branch, then fast-forwards|
|
|
|
|
|`squash`|squashes the PR as a single commit on the target branch, using the PR description as message|
|
|
|
|
|`delegate+`|grants approval rights to the PR author|
|
|
|
|
|`delegate=<...>`|grants approval rights on this PR to the specified github users|
|
|
|
|
|`default`|stages the PR normally|
|
|
|
|
|`priority`|tries to stage this PR first, then adds `default` PRs if the staging has room|
|
|
|
|
|`alone`|stages this PR only with other PRs of the same priority|
|
|
|
|
|`cancel=staging`|automatically cancels the current staging when this PR becomes ready|
|
|
|
|
|`check`|fetches or refreshes PR metadata, resets mergebot state|
|
|
|
|
|
|
|
|
Note: this help text is dynamic and will change with the state of the PR.
|
|
|
|
""".format_map(users)),
|
2020-07-22 16:56:33 +07:00
|
|
|
]
|
|
|
|
|
2018-09-25 20:04:31 +07:00
|
|
|
class TestRMinus:
|
2019-10-10 14:22:12 +07:00
|
|
|
def test_rminus_approved(self, repo, env, config):
|
2018-09-25 20:04:31 +07:00
|
|
|
""" approved -> r- -> opened
|
|
|
|
"""
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
m = repo.make_commit(None, 'initial', None, tree={'m': 'm'})
|
|
|
|
repo.make_ref('heads/master', m)
|
2018-09-25 20:04:31 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
c = repo.make_commit(m, 'first', None, tree={'m': 'c'})
|
|
|
|
prx = repo.make_pr(title='title', body=None, target='master', head=c)
|
2018-09-25 20:04:31 +07:00
|
|
|
|
2024-12-02 20:17:28 +07:00
|
|
|
pr = to_pr(env, prx)
|
2018-09-25 20:04:31 +07:00
|
|
|
assert pr.state == 'opened'
|
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
prx.post_comment('hansen r+', config['role_reviewer']['token'])
|
2018-09-25 20:04:31 +07:00
|
|
|
assert pr.state == 'approved'
|
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
prx.post_comment('hansen r-', config['role_user']['token'])
|
2018-09-25 20:04:31 +07:00
|
|
|
assert pr.state == 'opened'
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
prx.post_comment('hansen r+', config['role_reviewer']['token'])
|
2018-09-25 20:04:31 +07:00
|
|
|
assert pr.state == 'approved'
|
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
prx.post_comment('hansen r-', config['role_other']['token'])
|
2018-09-25 20:04:31 +07:00
|
|
|
assert pr.state == 'approved'
|
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
prx.post_comment('hansen r-', config['role_reviewer']['token'])
|
2018-09-25 20:04:31 +07:00
|
|
|
assert pr.state == 'opened'
|
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
def test_rminus_ready(self, repo, env, config):
|
2018-09-25 20:04:31 +07:00
|
|
|
""" ready -> r- -> validated
|
|
|
|
"""
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
m = repo.make_commit(None, 'initial', None, tree={'m': 'm'})
|
|
|
|
repo.make_ref('heads/master', m)
|
2018-09-25 20:04:31 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
c = repo.make_commit(m, 'first', None, tree={'m': 'c'})
|
|
|
|
prx = repo.make_pr(title='title', body=None, target='master', head=c)
|
|
|
|
repo.post_status(prx.head, 'success', 'ci/runbot')
|
|
|
|
repo.post_status(prx.head, 'success', 'legal/cla')
|
|
|
|
env.run_crons()
|
2018-09-25 20:04:31 +07:00
|
|
|
|
2024-12-02 20:17:28 +07:00
|
|
|
pr = to_pr(env, prx)
|
2018-09-25 20:04:31 +07:00
|
|
|
assert pr.state == 'validated'
|
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
prx.post_comment('hansen r+', config['role_reviewer']['token'])
|
2018-09-25 20:04:31 +07:00
|
|
|
assert pr.state == 'ready'
|
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
prx.post_comment('hansen r-', config['role_user']['token'])
|
2018-09-25 20:04:31 +07:00
|
|
|
assert pr.state == 'validated'
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
prx.post_comment('hansen r+', config['role_reviewer']['token'])
|
2018-09-25 20:04:31 +07:00
|
|
|
assert pr.state == 'ready'
|
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
prx.post_comment('hansen r-', config['role_other']['token'])
|
2018-09-25 20:04:31 +07:00
|
|
|
assert pr.state == 'ready'
|
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
prx.post_comment('hansen r-', config['role_reviewer']['token'])
|
2018-09-25 20:04:31 +07:00
|
|
|
assert pr.state == 'validated'
|
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
def test_rminus_staged(self, repo, env, config):
|
2018-09-25 20:04:31 +07:00
|
|
|
""" staged -> r- -> validated
|
|
|
|
"""
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
m = repo.make_commit(None, 'initial', None, tree={'m': 'm'})
|
|
|
|
repo.make_ref('heads/master', m)
|
2018-09-25 20:04:31 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
c = repo.make_commit(m, 'first', None, tree={'m': 'c'})
|
|
|
|
prx = repo.make_pr(title='title', body=None, target='master', head=c)
|
|
|
|
repo.post_status(prx.head, 'success', 'ci/runbot')
|
|
|
|
repo.post_status(prx.head, 'success', 'legal/cla')
|
|
|
|
env.run_crons()
|
2018-09-25 20:04:31 +07:00
|
|
|
|
2024-12-02 20:17:28 +07:00
|
|
|
pr = to_pr(env, prx)
|
2018-09-25 20:04:31 +07:00
|
|
|
|
|
|
|
# if reviewer unreviews, cancel staging & unreview
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
prx.post_comment('hansen r+', config['role_reviewer']['token'])
|
|
|
|
env.run_crons()
|
2018-09-25 20:04:31 +07:00
|
|
|
st = pr.staging_id
|
|
|
|
assert st
|
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
prx.post_comment('hansen r-', config['role_reviewer']['token'])
|
2018-09-25 20:04:31 +07:00
|
|
|
assert not st.active
|
|
|
|
assert not pr.staging_id
|
|
|
|
assert pr.state == 'validated'
|
|
|
|
|
|
|
|
# if author unreviews, cancel staging & unreview
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
prx.post_comment('hansen r+', config['role_reviewer']['token'])
|
|
|
|
env.run_crons()
|
2018-09-25 20:04:31 +07:00
|
|
|
st = pr.staging_id
|
|
|
|
assert st
|
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
prx.post_comment('hansen r-', config['role_user']['token'])
|
2018-09-25 20:04:31 +07:00
|
|
|
assert not st.active
|
|
|
|
assert not pr.staging_id
|
|
|
|
assert pr.state == 'validated'
|
|
|
|
|
|
|
|
# if rando unreviews, ignore
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
prx.post_comment('hansen r+', config['role_reviewer']['token'])
|
|
|
|
env.run_crons()
|
2018-09-25 20:04:31 +07:00
|
|
|
st = pr.staging_id
|
|
|
|
assert st
|
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
prx.post_comment('hansen r-', config['role_other']['token'])
|
2018-09-25 20:04:31 +07:00
|
|
|
assert pr.staging_id == st
|
|
|
|
assert pr.state == 'ready'
|
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
def test_split(self, env, repo, config):
|
2019-07-31 14:20:02 +07:00
|
|
|
""" Should remove the PR from its split, and possibly delete the split
|
|
|
|
entirely.
|
|
|
|
"""
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
m = repo.make_commit(None, 'initial', None, tree={'m': 'm'})
|
|
|
|
repo.make_ref('heads/master', m)
|
|
|
|
|
|
|
|
c = repo.make_commit(m, 'first', None, tree={'m': 'm', '1': '1'})
|
|
|
|
repo.make_ref('heads/p1', c)
|
|
|
|
prx1 = repo.make_pr(title='t1', body='b1', target='master', head='p1')
|
|
|
|
repo.post_status(prx1.head, 'success', 'legal/cla')
|
|
|
|
repo.post_status(prx1.head, 'success', 'ci/runbot')
|
|
|
|
prx1.post_comment('hansen r+', config['role_reviewer']['token'])
|
|
|
|
|
|
|
|
c = repo.make_commit(m, 'first', None, tree={'m': 'm', '2': '2'})
|
|
|
|
repo.make_ref('heads/p2', c)
|
|
|
|
prx2 = repo.make_pr(title='t2', body='b2', target='master', head='p2')
|
|
|
|
repo.post_status(prx2.head, 'success', 'legal/cla')
|
|
|
|
repo.post_status(prx2.head, 'success', 'ci/runbot')
|
|
|
|
prx2.post_comment('hansen r+', config['role_reviewer']['token'])
|
|
|
|
env.run_crons()
|
2019-07-31 14:20:02 +07:00
|
|
|
|
|
|
|
pr1, pr2 = env['runbot_merge.pull_requests'].search([], order='number')
|
|
|
|
assert pr1.number == prx1.number
|
|
|
|
assert pr2.number == prx2.number
|
|
|
|
assert pr1.staging_id == pr2.staging_id
|
|
|
|
s0 = pr1.staging_id
|
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
repo.post_status('heads/staging.master', 'failure', 'ci/runbot')
|
|
|
|
env.run_crons()
|
2019-07-31 14:20:02 +07:00
|
|
|
|
|
|
|
assert pr1.staging_id and pr1.staging_id != s0, "pr1 should have been re-staged"
|
|
|
|
assert not pr2.staging_id, "pr2 should not"
|
|
|
|
# TODO: remote doesn't currently handle env context so can't mess
|
|
|
|
# around using active_test=False
|
|
|
|
assert env['runbot_merge.split'].search([])
|
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
# prx2 was actually a terrible idea!
|
|
|
|
prx2.post_comment('hansen r-', config['role_reviewer']['token'])
|
2019-07-31 14:20:02 +07:00
|
|
|
# probably not necessary ATM but...
|
2019-10-10 14:22:12 +07:00
|
|
|
env.run_crons()
|
2019-07-31 14:20:02 +07:00
|
|
|
|
|
|
|
assert pr2.state == 'validated', "state should have been reset"
|
|
|
|
assert not env['runbot_merge.split'].search([]), "there should be no split left"
|
2018-09-25 20:04:31 +07:00
|
|
|
|
2018-09-21 15:27:07 +07:00
|
|
|
class TestComments:
|
2019-10-10 14:22:12 +07:00
|
|
|
def test_address_method(self, repo, env, config):
|
|
|
|
with repo:
|
|
|
|
m = repo.make_commit(None, 'initial', None, tree={'m': 'm'})
|
|
|
|
repo.make_ref('heads/master', m)
|
2018-09-21 15:27:07 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
c1 = repo.make_commit(m, 'first', None, tree={'m': 'c1'})
|
|
|
|
prx = repo.make_pr(title='title', body='body', target='master', head=c1)
|
2018-09-21 15:27:07 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
repo.post_status(prx.head, 'success', 'legal/cla')
|
|
|
|
repo.post_status(prx.head, 'success', 'ci/runbot')
|
|
|
|
prx.post_comment('hansen delegate=foo', config['role_reviewer']['token'])
|
|
|
|
prx.post_comment('@hansen delegate=bar', config['role_reviewer']['token'])
|
|
|
|
prx.post_comment('#hansen delegate=baz', config['role_reviewer']['token'])
|
2018-09-21 15:27:07 +07:00
|
|
|
|
2024-12-02 20:17:28 +07:00
|
|
|
pr = to_pr(env, prx)
|
2018-09-21 15:27:07 +07:00
|
|
|
|
|
|
|
assert {p.github_login for p in pr.delegates} \
|
|
|
|
== {'foo', 'bar', 'baz'}
|
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
def test_delete(self, repo, env, config):
|
2018-11-26 16:28:27 +07:00
|
|
|
""" Comments being deleted should be ignored
|
|
|
|
"""
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
m = repo.make_commit(None, 'initial', None, tree={'m': 'm'})
|
|
|
|
repo.make_ref('heads/master', m)
|
2018-11-26 16:28:27 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
c1 = repo.make_commit(m, 'first', None, tree={'m': 'c1'})
|
|
|
|
prx = repo.make_pr(title='title', body='body', target='master', head=c1)
|
2024-12-02 20:17:28 +07:00
|
|
|
pr = to_pr(env, prx)
|
2018-11-26 16:28:27 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
cid = prx.post_comment('hansen r+', config['role_reviewer']['token'])
|
|
|
|
# unreview by pushing a new commit
|
|
|
|
repo.update_ref(prx.ref, repo.make_commit(c1, 'second', None, tree={'m': 'c2'}), force=True)
|
2018-11-26 16:28:27 +07:00
|
|
|
assert pr.state == 'opened'
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
prx.delete_comment(cid, config['role_reviewer']['token'])
|
2018-11-26 16:28:27 +07:00
|
|
|
# check that PR is still unreviewed
|
|
|
|
assert pr.state == 'opened'
|
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
def test_edit(self, repo, env, config):
|
2018-11-26 16:28:27 +07:00
|
|
|
""" Comments being edited should be ignored
|
|
|
|
"""
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
m = repo.make_commit(None, 'initial', None, tree={'m': 'm'})
|
|
|
|
repo.make_ref('heads/master', m)
|
2018-11-26 16:28:27 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
c1 = repo.make_commit(m, 'first', None, tree={'m': 'c1'})
|
|
|
|
prx = repo.make_pr(title='title', body='body', target='master', head=c1)
|
2024-12-02 20:17:28 +07:00
|
|
|
pr = to_pr(env, prx)
|
2018-11-26 16:28:27 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
cid = prx.post_comment('hansen r+', config['role_reviewer']['token'])
|
|
|
|
# unreview by pushing a new commit
|
|
|
|
repo.update_ref(prx.ref, repo.make_commit(c1, 'second', None, tree={'m': 'c2'}), force=True)
|
2018-11-26 16:28:27 +07:00
|
|
|
assert pr.state == 'opened'
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
prx.edit_comment(cid, 'hansen r+ edited', config['role_reviewer']['token'])
|
2018-11-26 16:28:27 +07:00
|
|
|
# check that PR is still unreviewed
|
|
|
|
assert pr.state == 'opened'
|
|
|
|
|
2019-07-31 14:19:50 +07:00
|
|
|
class TestFeedback:
|
2019-10-10 14:22:12 +07:00
|
|
|
def test_ci_approved(self, repo, env, users, config):
|
2019-07-31 14:19:50 +07:00
|
|
|
"""CI failing on an r+'d PR sends feedback"""
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
[FIX] *: re-enable notification on status failure
If a PR gets approved *then* fails CI, there should be a notification
warning the author & reviewer since
48e08b657b23760e6d45f397cf59e7710bc7f89a, it even has a test, which
passes (in fact it has *two*, one of which is redundant, so merge
`test_ci_failure_after_review` into the later `test_ci_approved`).
*However* this is in runbot_merge, turns out in
fafa7ef437d75d72e34be91af72e16c52135b7fa some refactoring was done in
order to override the notification and customise it for *forward
ports* with a failed status... except that override never called its
`super()`, so as soon as forwardport is installed the base
notification stops working, and that's been that since October
2019 (had been added in March that year, ignoring deployment lag).
This can be revealed by adding the corresponding check in the
*forwardport* tests, revealing the failure.
This was a pain to track down, thankfully it reproduced relatively
easily locally.
While this could be resolved in the override, might as well fold it
into the base method in furtherance of #789: the mergebot is only
used by odoo, and only with both modules combined, so splitting them
is not useful. And furthermore it things should work fine with the
forwardport installed but unused.
Fixes #894
2024-06-21 15:27:01 +07:00
|
|
|
[m] = repo.make_commits(None, Commit('initial', tree={'m': 'm'}), ref="heads/master")
|
2019-07-31 14:19:50 +07:00
|
|
|
|
[FIX] *: re-enable notification on status failure
If a PR gets approved *then* fails CI, there should be a notification
warning the author & reviewer since
48e08b657b23760e6d45f397cf59e7710bc7f89a, it even has a test, which
passes (in fact it has *two*, one of which is redundant, so merge
`test_ci_failure_after_review` into the later `test_ci_approved`).
*However* this is in runbot_merge, turns out in
fafa7ef437d75d72e34be91af72e16c52135b7fa some refactoring was done in
order to override the notification and customise it for *forward
ports* with a failed status... except that override never called its
`super()`, so as soon as forwardport is installed the base
notification stops working, and that's been that since October
2019 (had been added in March that year, ignoring deployment lag).
This can be revealed by adding the corresponding check in the
*forwardport* tests, revealing the failure.
This was a pain to track down, thankfully it reproduced relatively
easily locally.
While this could be resolved in the override, might as well fold it
into the base method in furtherance of #789: the mergebot is only
used by odoo, and only with both modules combined, so splitting them
is not useful. And furthermore it things should work fine with the
forwardport installed but unused.
Fixes #894
2024-06-21 15:27:01 +07:00
|
|
|
[c1] = repo.make_commits(m, Commit('first', tree={'m': 'c1'}))
|
|
|
|
pr = repo.make_pr(title='title', body='body', target='master', head=c1)
|
|
|
|
pr.post_comment('hansen r+', config['role_reviewer']['token'])
|
2019-10-10 14:22:12 +07:00
|
|
|
env.run_crons()
|
2019-07-31 14:19:50 +07:00
|
|
|
|
[FIX] *: re-enable notification on status failure
If a PR gets approved *then* fails CI, there should be a notification
warning the author & reviewer since
48e08b657b23760e6d45f397cf59e7710bc7f89a, it even has a test, which
passes (in fact it has *two*, one of which is redundant, so merge
`test_ci_failure_after_review` into the later `test_ci_approved`).
*However* this is in runbot_merge, turns out in
fafa7ef437d75d72e34be91af72e16c52135b7fa some refactoring was done in
order to override the notification and customise it for *forward
ports* with a failed status... except that override never called its
`super()`, so as soon as forwardport is installed the base
notification stops working, and that's been that since October
2019 (had been added in March that year, ignoring deployment lag).
This can be revealed by adding the corresponding check in the
*forwardport* tests, revealing the failure.
This was a pain to track down, thankfully it reproduced relatively
easily locally.
While this could be resolved in the override, might as well fold it
into the base method in furtherance of #789: the mergebot is only
used by odoo, and only with both modules combined, so splitting them
is not useful. And furthermore it things should work fine with the
forwardport installed but unused.
Fixes #894
2024-06-21 15:27:01 +07:00
|
|
|
pr_id = to_pr(env, pr)
|
|
|
|
assert pr_id.state == 'approved'
|
|
|
|
|
|
|
|
for ctx, url in [
|
|
|
|
('ci/runbot', 'https://a'),
|
|
|
|
('ci/runbot', 'https://a'),
|
|
|
|
('legal/cla', 'https://b'),
|
|
|
|
('foo/bar', 'https://c'),
|
|
|
|
('ci/runbot', 'https://a'),
|
|
|
|
('legal/cla', 'https://d'), # url changes so different from the previous
|
|
|
|
]:
|
|
|
|
with repo:
|
|
|
|
repo.post_status(pr_id.head, 'failure', ctx, target_url=url)
|
|
|
|
env.run_crons()
|
|
|
|
|
|
|
|
assert pr.comments == [
|
2019-07-31 14:19:50 +07:00
|
|
|
(users['reviewer'], 'hansen r+'),
|
[FIX] *: re-enable notification on status failure
If a PR gets approved *then* fails CI, there should be a notification
warning the author & reviewer since
48e08b657b23760e6d45f397cf59e7710bc7f89a, it even has a test, which
passes (in fact it has *two*, one of which is redundant, so merge
`test_ci_failure_after_review` into the later `test_ci_approved`).
*However* this is in runbot_merge, turns out in
fafa7ef437d75d72e34be91af72e16c52135b7fa some refactoring was done in
order to override the notification and customise it for *forward
ports* with a failed status... except that override never called its
`super()`, so as soon as forwardport is installed the base
notification stops working, and that's been that since October
2019 (had been added in March that year, ignoring deployment lag).
This can be revealed by adding the corresponding check in the
*forwardport* tests, revealing the failure.
This was a pain to track down, thankfully it reproduced relatively
easily locally.
While this could be resolved in the override, might as well fold it
into the base method in furtherance of #789: the mergebot is only
used by odoo, and only with both modules combined, so splitting them
is not useful. And furthermore it things should work fine with the
forwardport installed but unused.
Fixes #894
2024-06-21 15:27:01 +07:00
|
|
|
seen(env, pr, users),
|
|
|
|
(users['user'], "@{user} @{reviewer} 'ci/runbot' failed on this reviewed PR.".format_map(users)),
|
|
|
|
(users['user'], "@{user} @{reviewer} 'legal/cla' failed on this reviewed PR.".format_map(users)),
|
|
|
|
(users['user'], "@{user} @{reviewer} 'legal/cla' failed on this reviewed PR.".format_map(users)),
|
2019-07-31 14:19:50 +07:00
|
|
|
]
|
|
|
|
|
2020-03-05 19:31:23 +07:00
|
|
|
def test_review_failed(self, repo, env, users, config):
|
2019-07-31 14:19:50 +07:00
|
|
|
"""r+-ing a PR with failed CI sends feedback"""
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
m = repo.make_commit(None, 'initial', None, tree={'m': 'm'})
|
|
|
|
repo.make_ref('heads/master', m)
|
2019-07-31 14:19:50 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
c1 = repo.make_commit(m, 'first', None, tree={'m': 'c1'})
|
|
|
|
prx = repo.make_pr(title='title', body='body', target='master', head=c1)
|
2024-12-02 20:17:28 +07:00
|
|
|
pr = to_pr(env, prx)
|
2019-07-31 14:19:50 +07:00
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
repo.post_status(prx.head, 'failure', 'ci/runbot')
|
|
|
|
env.run_crons()
|
2019-07-31 14:19:50 +07:00
|
|
|
assert pr.state == 'opened'
|
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
prx.post_comment('hansen r+', config['role_reviewer']['token'])
|
2019-07-31 14:19:50 +07:00
|
|
|
assert pr.state == 'approved'
|
|
|
|
|
2019-10-10 14:22:12 +07:00
|
|
|
env.run_crons()
|
2019-07-31 14:19:50 +07:00
|
|
|
|
|
|
|
assert prx.comments == [
|
2020-11-17 21:21:21 +07:00
|
|
|
seen(env, prx, users),
|
2019-07-31 14:19:50 +07:00
|
|
|
(users['reviewer'], 'hansen r+'),
|
2022-06-23 19:25:07 +07:00
|
|
|
(users['user'], "@%s you may want to rebuild or fix this PR as it has failed CI." % users['reviewer'])
|
2019-07-31 14:19:50 +07:00
|
|
|
]
|
2022-06-23 19:25:07 +07:00
|
|
|
|
2018-10-10 15:48:42 +07:00
|
|
|
class TestInfrastructure:
|
2022-07-11 13:17:04 +07:00
|
|
|
@pytest.mark.skip(reason="Don't want to implement")
|
2018-10-10 15:48:42 +07:00
|
|
|
def test_protection(self, repo):
|
|
|
|
""" force-pushing on a protected ref should fail
|
|
|
|
"""
|
2019-10-10 14:22:12 +07:00
|
|
|
with repo:
|
|
|
|
m0 = repo.make_commit(None, 'initial', None, tree={'m': 'm0'})
|
|
|
|
m1 = repo.make_commit(m0, 'first', None, tree={'m': 'm1'})
|
|
|
|
repo.make_ref('heads/master', m1)
|
|
|
|
repo.protect('master')
|
|
|
|
|
|
|
|
c1 = repo.make_commit(m0, 'other', None, tree={'m': 'c1'})
|
|
|
|
with pytest.raises(AssertionError):
|
|
|
|
repo.update_ref('heads/master', c1, force=True)
|
2018-10-10 15:48:42 +07:00
|
|
|
assert repo.get_ref('heads/master') == m1
|
|
|
|
|
2018-08-29 21:51:53 +07:00
|
|
|
def node(name, *children):
|
2024-06-04 17:15:29 +07:00
|
|
|
assert type(name) in (str, matches)
|
2018-08-29 21:51:53 +07:00
|
|
|
return name, frozenset(children)
|
2018-08-28 20:42:28 +07:00
|
|
|
def log_to_node(log):
|
|
|
|
log = list(log)
|
|
|
|
nodes = {}
|
2018-09-19 22:33:25 +07:00
|
|
|
# check that all parents are present
|
|
|
|
ids = {c['sha'] for c in log}
|
|
|
|
parents = {p['sha'] for c in log for p in c['parents']}
|
|
|
|
missing = parents - ids
|
|
|
|
assert parents, "Didn't find %s in log" % missing
|
|
|
|
|
|
|
|
# github doesn't necessarily log topologically maybe?
|
|
|
|
todo = list(reversed(log))
|
|
|
|
while todo:
|
|
|
|
c = todo.pop(0)
|
|
|
|
if all(p['sha'] in nodes for p in c['parents']):
|
|
|
|
nodes[c['sha']] = (c['commit']['message'], frozenset(
|
|
|
|
nodes[p['sha']]
|
|
|
|
for p in c['parents']
|
|
|
|
))
|
|
|
|
else:
|
|
|
|
todo.append(c)
|
|
|
|
|
2018-08-28 20:42:28 +07:00
|
|
|
return nodes[log[0]['sha']]
|
2018-11-22 00:43:05 +07:00
|
|
|
|
|
|
|
class TestEmailFormatting:
|
|
|
|
def test_simple(self, env):
|
|
|
|
p1 = env['res.partner'].create({
|
|
|
|
'name': 'Bob',
|
|
|
|
'email': 'bob@example.com',
|
|
|
|
})
|
|
|
|
assert p1.formatted_email == 'Bob <bob@example.com>'
|
|
|
|
|
|
|
|
def test_noemail(self, env):
|
|
|
|
p1 = env['res.partner'].create({
|
|
|
|
'name': 'Shultz',
|
|
|
|
'github_login': 'Osmose99',
|
|
|
|
})
|
|
|
|
assert p1.formatted_email == 'Shultz <Osmose99@users.noreply.github.com>'
|