sync with OpenBSD -current
This commit is contained in:
parent
5d45cd7ee8
commit
155eb8555e
5506 changed files with 1786257 additions and 1416034 deletions
|
@ -40,16 +40,19 @@ if typing.TYPE_CHECKING:
|
|||
sha: str
|
||||
description: str
|
||||
nominated: bool
|
||||
nomination_type: typing.Optional[int]
|
||||
nomination_type: int
|
||||
resolution: typing.Optional[int]
|
||||
main_sha: typing.Optional[str]
|
||||
because_sha: typing.Optional[str]
|
||||
notes: typing.Optional[str] = attr.ib(None)
|
||||
|
||||
IS_FIX = re.compile(r'^\s*fixes:\s*([a-f0-9]{6,40})', flags=re.MULTILINE | re.IGNORECASE)
|
||||
# FIXME: I dislike the duplication in this regex, but I couldn't get it to work otherwise
|
||||
IS_CC = re.compile(r'^\s*cc:\s*["\']?([0-9]{2}\.[0-9])?["\']?\s*["\']?([0-9]{2}\.[0-9])?["\']?\s*\<?mesa-stable',
|
||||
flags=re.MULTILINE | re.IGNORECASE)
|
||||
IS_REVERT = re.compile(r'This reverts commit ([0-9a-f]{40})')
|
||||
IS_BACKPORT = re.compile(r'^\s*backport-to:\s*(\d{2}\.\d),?\s*(\d{2}\.\d)?',
|
||||
flags=re.MULTILINE | re.IGNORECASE)
|
||||
|
||||
# XXX: hack
|
||||
SEM = asyncio.Semaphore(50)
|
||||
|
@ -71,6 +74,8 @@ class NominationType(enum.Enum):
|
|||
CC = 0
|
||||
FIXES = 1
|
||||
REVERT = 2
|
||||
NONE = 3
|
||||
BACKPORT = 4
|
||||
|
||||
|
||||
@enum.unique
|
||||
|
@ -116,24 +121,24 @@ class Commit:
|
|||
sha: str = attr.ib()
|
||||
description: str = attr.ib()
|
||||
nominated: bool = attr.ib(False)
|
||||
nomination_type: typing.Optional[NominationType] = attr.ib(None)
|
||||
nomination_type: NominationType = attr.ib(NominationType.NONE)
|
||||
resolution: Resolution = attr.ib(Resolution.UNRESOLVED)
|
||||
main_sha: typing.Optional[str] = attr.ib(None)
|
||||
because_sha: typing.Optional[str] = attr.ib(None)
|
||||
notes: typing.Optional[str] = attr.ib(None)
|
||||
|
||||
def to_json(self) -> 'CommitDict':
|
||||
d: typing.Dict[str, typing.Any] = attr.asdict(self)
|
||||
if self.nomination_type is not None:
|
||||
d['nomination_type'] = self.nomination_type.value
|
||||
d['nomination_type'] = self.nomination_type.value
|
||||
if self.resolution is not None:
|
||||
d['resolution'] = self.resolution.value
|
||||
return typing.cast('CommitDict', d)
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, data: 'CommitDict') -> 'Commit':
|
||||
c = cls(data['sha'], data['description'], data['nominated'], main_sha=data['main_sha'], because_sha=data['because_sha'])
|
||||
if data['nomination_type'] is not None:
|
||||
c.nomination_type = NominationType(data['nomination_type'])
|
||||
c = cls(data['sha'], data['description'], data['nominated'], main_sha=data['main_sha'],
|
||||
because_sha=data['because_sha'], notes=data['notes'])
|
||||
c.nomination_type = NominationType(data['nomination_type'])
|
||||
if data['resolution'] is not None:
|
||||
c.resolution = Resolution(data['resolution'])
|
||||
return c
|
||||
|
@ -202,6 +207,14 @@ class Commit:
|
|||
assert v
|
||||
await ui.feedback(f'{self.sha} ({self.description}) committed successfully')
|
||||
|
||||
async def update_notes(self, ui: 'UI', notes: typing.Optional[str]) -> None:
|
||||
self.notes = notes
|
||||
async with ui.git_lock:
|
||||
ui.save()
|
||||
v = await commit_state(message=f'Updates notes for {self.sha}')
|
||||
assert v
|
||||
await ui.feedback(f'{self.sha} ({self.description}) notes updated successfully')
|
||||
|
||||
|
||||
async def get_new_commits(sha: str) -> typing.List[typing.Tuple[str, str]]:
|
||||
# Try to get the authoritative upstream main
|
||||
|
@ -266,13 +279,11 @@ async def resolve_nomination(commit: 'Commit', version: str) -> 'Commit':
|
|||
out = _out.decode()
|
||||
|
||||
# We give precedence to fixes and cc tags over revert tags.
|
||||
# XXX: not having the walrus operator available makes me sad :=
|
||||
m = IS_FIX.search(out)
|
||||
if m:
|
||||
if fix_for_commit := IS_FIX.search(out):
|
||||
# We set the nomination_type and because_sha here so that we can later
|
||||
# check to see if this fixes another staged commit.
|
||||
try:
|
||||
commit.because_sha = fixed = await full_sha(m.group(1))
|
||||
commit.because_sha = fixed = await full_sha(fix_for_commit.group(1))
|
||||
except PickUIException:
|
||||
pass
|
||||
else:
|
||||
|
@ -281,18 +292,22 @@ async def resolve_nomination(commit: 'Commit', version: str) -> 'Commit':
|
|||
commit.nominated = True
|
||||
return commit
|
||||
|
||||
m = IS_CC.search(out)
|
||||
if m:
|
||||
if m.groups() == (None, None) or version in m.groups():
|
||||
if backport_to := IS_BACKPORT.search(out):
|
||||
if version in backport_to.groups():
|
||||
commit.nominated = True
|
||||
commit.nomination_type = NominationType.BACKPORT
|
||||
return commit
|
||||
|
||||
if cc_to := IS_CC.search(out):
|
||||
if cc_to.groups() == (None, None) or version in cc_to.groups():
|
||||
commit.nominated = True
|
||||
commit.nomination_type = NominationType.CC
|
||||
return commit
|
||||
|
||||
m = IS_REVERT.search(out)
|
||||
if m:
|
||||
if revert_of := IS_REVERT.search(out):
|
||||
# See comment for IS_FIX path
|
||||
try:
|
||||
commit.because_sha = reverted = await full_sha(m.group(1))
|
||||
commit.because_sha = reverted = await full_sha(revert_of.group(1))
|
||||
except PickUIException:
|
||||
pass
|
||||
else:
|
||||
|
|
|
@ -94,9 +94,9 @@ class TestRE:
|
|||
Reviewed-by: Jonathan Marek <jonathan@marek.ca>
|
||||
""")
|
||||
|
||||
m = core.IS_FIX.search(message)
|
||||
assert m is not None
|
||||
assert m.group(1) == '3d09bb390a39'
|
||||
fix_for_commit = core.IS_FIX.search(message)
|
||||
assert fix_for_commit is not None
|
||||
assert fix_for_commit.group(1) == '3d09bb390a39'
|
||||
|
||||
class TestCC:
|
||||
|
||||
|
@ -114,9 +114,9 @@ class TestRE:
|
|||
Reviewed-by: Bas Nieuwenhuizen <bas@basnieuwenhuizen.nl>
|
||||
""")
|
||||
|
||||
m = core.IS_CC.search(message)
|
||||
assert m is not None
|
||||
assert m.group(1) == '19.2'
|
||||
cc_to = core.IS_CC.search(message)
|
||||
assert cc_to is not None
|
||||
assert cc_to.group(1) == '19.2'
|
||||
|
||||
def test_multiple_branches(self):
|
||||
"""Tests commit with more than one branch specified"""
|
||||
|
@ -130,10 +130,10 @@ class TestRE:
|
|||
Reviewed-by: Pierre-Eric Pelloux-Prayer <pierre-eric.pelloux-prayer@amd.com>
|
||||
""")
|
||||
|
||||
m = core.IS_CC.search(message)
|
||||
assert m is not None
|
||||
assert m.group(1) == '19.1'
|
||||
assert m.group(2) == '19.2'
|
||||
cc_to = core.IS_CC.search(message)
|
||||
assert cc_to is not None
|
||||
assert cc_to.group(1) == '19.1'
|
||||
assert cc_to.group(2) == '19.2'
|
||||
|
||||
def test_no_branch(self):
|
||||
"""Tests commit with no branch specification"""
|
||||
|
@ -148,8 +148,8 @@ class TestRE:
|
|||
Reviewed-by: Lionel Landwerlin <lionel.g.landwerlin@intel.com>
|
||||
""")
|
||||
|
||||
m = core.IS_CC.search(message)
|
||||
assert m is not None
|
||||
cc_to = core.IS_CC.search(message)
|
||||
assert cc_to is not None
|
||||
|
||||
def test_quotes(self):
|
||||
"""Tests commit with quotes around the versions"""
|
||||
|
@ -162,9 +162,9 @@ class TestRE:
|
|||
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/3454>
|
||||
""")
|
||||
|
||||
m = core.IS_CC.search(message)
|
||||
assert m is not None
|
||||
assert m.group(1) == '20.0'
|
||||
cc_to = core.IS_CC.search(message)
|
||||
assert cc_to is not None
|
||||
assert cc_to.group(1) == '20.0'
|
||||
|
||||
def test_multiple_quotes(self):
|
||||
"""Tests commit with quotes around the versions"""
|
||||
|
@ -177,10 +177,10 @@ class TestRE:
|
|||
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/3454>
|
||||
""")
|
||||
|
||||
m = core.IS_CC.search(message)
|
||||
assert m is not None
|
||||
assert m.group(1) == '20.0'
|
||||
assert m.group(2) == '20.1'
|
||||
cc_to = core.IS_CC.search(message)
|
||||
assert cc_to is not None
|
||||
assert cc_to.group(1) == '20.0'
|
||||
assert cc_to.group(2) == '20.1'
|
||||
|
||||
def test_single_quotes(self):
|
||||
"""Tests commit with quotes around the versions"""
|
||||
|
@ -193,9 +193,9 @@ class TestRE:
|
|||
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/3454>
|
||||
""")
|
||||
|
||||
m = core.IS_CC.search(message)
|
||||
assert m is not None
|
||||
assert m.group(1) == '20.0'
|
||||
cc_to = core.IS_CC.search(message)
|
||||
assert cc_to is not None
|
||||
assert cc_to.group(1) == '20.0'
|
||||
|
||||
def test_multiple_single_quotes(self):
|
||||
"""Tests commit with quotes around the versions"""
|
||||
|
@ -208,10 +208,10 @@ class TestRE:
|
|||
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/3454>
|
||||
""")
|
||||
|
||||
m = core.IS_CC.search(message)
|
||||
assert m is not None
|
||||
assert m.group(1) == '20.0'
|
||||
assert m.group(2) == '20.1'
|
||||
cc_to = core.IS_CC.search(message)
|
||||
assert cc_to is not None
|
||||
assert cc_to.group(1) == '20.0'
|
||||
assert cc_to.group(2) == '20.1'
|
||||
|
||||
class TestRevert:
|
||||
|
||||
|
@ -232,9 +232,61 @@ class TestRE:
|
|||
Reviewed-by: Bas Nieuwenhuizen <bas@basnieuwenhuizen.nl>
|
||||
""")
|
||||
|
||||
m = core.IS_REVERT.search(message)
|
||||
assert m is not None
|
||||
assert m.group(1) == '2ca8629fa9b303e24783b76a7b3b0c2513e32fbd'
|
||||
revert_of = core.IS_REVERT.search(message)
|
||||
assert revert_of is not None
|
||||
assert revert_of.group(1) == '2ca8629fa9b303e24783b76a7b3b0c2513e32fbd'
|
||||
|
||||
class TestBackportTo:
|
||||
|
||||
def test_single_release(self):
|
||||
"""Tests commit meant for a single branch, ie, 19.1"""
|
||||
message = textwrap.dedent("""\
|
||||
radv: fix DCC fast clear code for intensity formats
|
||||
|
||||
This fixes a rendering issue with DiRT 4 on GFX10. Only GFX10 was
|
||||
affected because intensity formats are different.
|
||||
|
||||
Backport-to: 19.2
|
||||
Closes: https://gitlab.freedesktop.org/mesa/mesa/-/issues/1923
|
||||
Signed-off-by: Samuel Pitoiset <samuel.pitoiset@gmail.com>
|
||||
Reviewed-by: Bas Nieuwenhuizen <bas@basnieuwenhuizen.nl>
|
||||
""")
|
||||
|
||||
backport_to = core.IS_BACKPORT.search(message)
|
||||
assert backport_to is not None
|
||||
assert backport_to.groups() == ('19.2', None)
|
||||
|
||||
def test_multiple_release_space(self):
|
||||
"""Tests commit with more than one branch specified"""
|
||||
message = textwrap.dedent("""\
|
||||
radeonsi: enable zerovram for Rocket League
|
||||
|
||||
Fixes corruption on game startup.
|
||||
Closes: https://gitlab.freedesktop.org/mesa/mesa/-/issues/1888
|
||||
|
||||
Backport-to: 19.1 19.2
|
||||
Reviewed-by: Pierre-Eric Pelloux-Prayer <pierre-eric.pelloux-prayer@amd.com>
|
||||
""")
|
||||
|
||||
backport_to = core.IS_BACKPORT.search(message)
|
||||
assert backport_to is not None
|
||||
assert backport_to.groups() == ('19.1', '19.2')
|
||||
|
||||
def test_multiple_release_comma(self):
|
||||
"""Tests commit with more than one branch specified"""
|
||||
message = textwrap.dedent("""\
|
||||
radeonsi: enable zerovram for Rocket League
|
||||
|
||||
Fixes corruption on game startup.
|
||||
Closes: https://gitlab.freedesktop.org/mesa/mesa/-/issues/1888
|
||||
|
||||
Backport-to: 19.1, 19.2
|
||||
Reviewed-by: Pierre-Eric Pelloux-Prayer <pierre-eric.pelloux-prayer@amd.com>
|
||||
""")
|
||||
|
||||
backport_to = core.IS_BACKPORT.search(message)
|
||||
assert backport_to is not None
|
||||
assert backport_to.groups() == ('19.1', '19.2')
|
||||
|
||||
|
||||
class TestResolveNomination:
|
||||
|
@ -323,6 +375,28 @@ class TestResolveNomination:
|
|||
assert not c.nominated
|
||||
assert c.nomination_type is None
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_backport_is_nominated(self):
|
||||
s = self.FakeSubprocess(b'Backport-to: 16.2')
|
||||
c = core.Commit('abcdef1234567890', 'a commit')
|
||||
|
||||
with mock.patch('bin.pick.core.asyncio.create_subprocess_exec', s.mock):
|
||||
await core.resolve_nomination(c, '16.2')
|
||||
|
||||
assert c.nominated
|
||||
assert c.nomination_type is core.NominationType.BACKPORT
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_backport_is_not_nominated(self):
|
||||
s = self.FakeSubprocess(b'Backport-to: 16.2')
|
||||
c = core.Commit('abcdef1234567890', 'a commit')
|
||||
|
||||
with mock.patch('bin.pick.core.asyncio.create_subprocess_exec', s.mock):
|
||||
await core.resolve_nomination(c, '16.1')
|
||||
|
||||
assert not c.nominated
|
||||
assert c.nomination_type is None
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_revert_is_nominated(self):
|
||||
s = self.FakeSubprocess(b'This reverts commit 1234567890123456789012345678901234567890.')
|
||||
|
@ -347,6 +421,21 @@ class TestResolveNomination:
|
|||
assert not c.nominated
|
||||
assert c.nomination_type is core.NominationType.REVERT
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_is_fix_and_backport(self):
|
||||
s = self.FakeSubprocess(
|
||||
b'Fixes: 3d09bb390a39 (etnaviv: GC7000: State changes for HALTI3..5)\n'
|
||||
b'Backport-to: 16.1'
|
||||
)
|
||||
c = core.Commit('abcdef1234567890', 'a commit')
|
||||
|
||||
with mock.patch('bin.pick.core.asyncio.create_subprocess_exec', s.mock):
|
||||
with mock.patch('bin.pick.core.is_commit_in_branch', self.return_true):
|
||||
await core.resolve_nomination(c, '16.1')
|
||||
|
||||
assert c.nominated
|
||||
assert c.nomination_type is core.NominationType.FIXES
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_is_fix_and_cc(self):
|
||||
s = self.FakeSubprocess(
|
||||
|
|
|
@ -47,6 +47,13 @@ class RootWidget(urwid.Frame):
|
|||
super().__init__(*args, **kwargs)
|
||||
self.ui = ui
|
||||
|
||||
|
||||
class CommitList(urwid.ListBox):
|
||||
|
||||
def __init__(self, *args, ui: 'UI', **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.ui = ui
|
||||
|
||||
def keypress(self, size: int, key: str) -> typing.Optional[str]:
|
||||
if key == 'q':
|
||||
raise urwid.ExitMainLoop()
|
||||
|
@ -101,6 +108,23 @@ class CommitWidget(urwid.Text):
|
|||
return None
|
||||
|
||||
|
||||
class FocusAwareEdit(urwid.Edit):
|
||||
|
||||
"""An Edit type that signals when it comes into and leaves focus."""
|
||||
|
||||
signals = urwid.Edit.signals + ['focus_changed']
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.__is_focus = False
|
||||
|
||||
def render(self, size: typing.Tuple[int], focus: bool = False) -> urwid.Canvas:
|
||||
if focus != self.__is_focus:
|
||||
self._emit("focus_changed", focus)
|
||||
self.__is_focus = focus
|
||||
return super().render(size, focus)
|
||||
|
||||
|
||||
@attr.s(slots=True)
|
||||
class UI:
|
||||
|
||||
|
@ -112,6 +136,7 @@ class UI:
|
|||
|
||||
commit_list: typing.List['urwid.Button'] = attr.ib(factory=lambda: urwid.SimpleFocusListWalker([]), init=False)
|
||||
feedback_box: typing.List['urwid.Text'] = attr.ib(factory=lambda: urwid.SimpleFocusListWalker([]), init=False)
|
||||
notes: 'FocusAwareEdit' = attr.ib(factory=lambda: FocusAwareEdit('', multiline=True), init=False)
|
||||
header: 'urwid.Text' = attr.ib(factory=lambda: urwid.Text('Mesa Stable Picker', align='center'), init=False)
|
||||
body: 'urwid.Columns' = attr.ib(attr.Factory(lambda s: s._make_body(), True), init=False)
|
||||
footer: 'urwid.Columns' = attr.ib(attr.Factory(lambda s: s._make_footer(), True), init=False)
|
||||
|
@ -122,10 +147,36 @@ class UI:
|
|||
new_commits: typing.List['core.Commit'] = attr.ib(factory=list, init=False)
|
||||
git_lock: asyncio.Lock = attr.ib(factory=asyncio.Lock, init=False)
|
||||
|
||||
def _get_current_commit(self) -> typing.Optional['core.Commit']:
|
||||
entry = self.commit_list.get_focus()[0]
|
||||
return entry.original_widget.commit if entry is not None else None
|
||||
|
||||
def _change_notes_cb(self) -> None:
|
||||
commit = self._get_current_commit()
|
||||
if commit and commit.notes:
|
||||
self.notes.set_edit_text(commit.notes)
|
||||
else:
|
||||
self.notes.set_edit_text('')
|
||||
|
||||
def _change_notes_focus_cb(self, notes: 'FocusAwareEdit', focus: 'bool') -> 'None':
|
||||
# in the case of coming into focus we don't want to do anything
|
||||
if focus:
|
||||
return
|
||||
commit = self._get_current_commit()
|
||||
if commit is None:
|
||||
return
|
||||
text: str = notes.get_edit_text()
|
||||
if text != commit.notes:
|
||||
asyncio.ensure_future(commit.update_notes(self, text))
|
||||
|
||||
def _make_body(self) -> 'urwid.Columns':
|
||||
commits = urwid.ListBox(self.commit_list)
|
||||
commits = CommitList(self.commit_list, ui=self)
|
||||
feedback = urwid.ListBox(self.feedback_box)
|
||||
return urwid.Columns([commits, feedback])
|
||||
urwid.connect_signal(self.commit_list, 'modified', self._change_notes_cb)
|
||||
notes = urwid.Filler(self.notes)
|
||||
urwid.connect_signal(self.notes, 'focus_changed', self._change_notes_focus_cb)
|
||||
|
||||
return urwid.Columns([urwid.LineBox(commits), urwid.Pile([urwid.LineBox(notes), urwid.LineBox(feedback)])])
|
||||
|
||||
def _make_footer(self) -> 'urwid.Columns':
|
||||
body = [
|
||||
|
@ -134,12 +185,12 @@ class UI:
|
|||
urwid.Text('[C]herry Pick'),
|
||||
urwid.Text('[D]enominate'),
|
||||
urwid.Text('[B]ackport'),
|
||||
urwid.Text('[A]pply additional patch')
|
||||
urwid.Text('[A]pply additional patch'),
|
||||
]
|
||||
return urwid.Columns(body)
|
||||
|
||||
def _make_root(self) -> 'RootWidget':
|
||||
return RootWidget(self.body, self.header, self.footer, 'body', ui=self)
|
||||
return RootWidget(self.body, urwid.LineBox(self.header), urwid.LineBox(self.footer), 'body', ui=self)
|
||||
|
||||
def render(self) -> 'WidgetType':
|
||||
asyncio.ensure_future(self.update())
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue