mesa/bin/rb.py
Alyssa Rosenzweig f365c2d33b bin: add script for applying review trailers
..or "the one where Alyssa gets jealous by b4".

Those of us who have stuck around a while have a habit of just commenting "rb"
or "ab" on MRs. which raises the question for everyone else of what name/email
to use. I've personally built up a collection of 36 (!!) different
shell aliases to apply different people's trailers. I think other people do
similarly.

This calls for better tooling. This patch adds a little script for applying
review trailers given a fuzzy match on the reviewer's name. I recommend
contributors alias it to something like `mrb`, then you can do things like:

   mrb alyssa
   mrb -a faith

to add a review tag for me or an acked-by tag for Faith.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
Reviewed-by: Mel Henning <mhenning@darkrefraction.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/33896>
2025-03-10 20:09:40 +00:00

102 lines
3 KiB
Python
Executable file

#!/usr/bin/python3
#
# Copyright 2025 Valve Corporation
# SPDX-License-Identifier: MIT
import argparse
import csv
import unittest
import sys
import subprocess
import os
from unidecode import unidecode
def normalize(x):
return unidecode(x.lower())
def name(row):
return normalize(row[0])
def username(row):
return normalize(row[2])
def find_person(x):
x = normalize(x)
filename = 'people.csv'
path = os.path.join(os.path.dirname(os.path.realpath(__file__)), filename)
with open(path, 'r') as f:
people = list(csv.reader(f, skipinitialspace=True))
# First, try to exactly match username
for row in people:
if username(row) == x:
return row
# Next, try to exactly match fullname
for row in people:
if name(row) == x:
return row
# Now we get fuzzy. Try to match a first name.
candidates = [r for r in people if name(r).split(' ')[0] == x]
if len(candidates) == 1:
return candidates[0]
# Or a last name?
candidates = [r for r in people if x in name(r).split(' ')]
if len(candidates) == 1:
return candidates[0]
# Well, frick.
return None
# Self-test... is it even worth find a unit test framework for this?
TEST_CASES = {
'gfxstrand': 'faith.ekstrand@collabora.com',
'Faith': 'faith.ekstrand@collabora.com',
'faith': 'faith.ekstrand@collabora.com',
'alyssa': 'alyssa@rosenzweig.io',
'briano': 'ivan.briano@intel.com',
'schurmann': 'daniel@schuermann.dev',
'Schürmann': 'daniel@schuermann.dev',
}
for test in TEST_CASES:
a, b = find_person(test), TEST_CASES[test]
if a is None or a[1] != b:
print(test, a, b)
assert(a[1] == b)
# Now the tool itself
if __name__ == "__main__":
parser = argparse.ArgumentParser(
prog='rb',
description='Add review trailers')
parser.add_argument('person', nargs='+', help="Reviewer's username, first name, or full name")
parser.add_argument('-a', '--ack', action='store_true', help="Apply an acked-by tag")
parser.add_argument('-d', '--dry-run', action='store_true',
help="Print trailer without applying")
args = parser.parse_args()
for p in args.person:
person = find_person(p)
if person is None:
print(f'Could not uniquely identify {p}, skipping')
trailer = 'Acked-by' if args.ack else 'Reviewed-by'
trailer = f'{trailer}: {person[0]} <{person[1]}>'
if args.dry_run:
print(trailer)
continue
diff_index = subprocess.run("git diff-index --quiet --cached HEAD --".split(" "))
if diff_index.returncode != 0:
print("You have staged changes.")
print("Please commit before applying review tags.")
sys.exit(1)
env = os.environ.copy()
env['GIT_EDITOR'] = f'git interpret-trailers --trailer "{trailer}" --in-place'
subprocess.run(["git", "commit", "--amend"], env=env)