mirror of
https://source.denx.de/u-boot/u-boot.git
synced 2025-08-10 17:26:59 +02:00
patman: add '--get-maintainer-script' argument
This makes it possible to configure a project to use some other location or script than the default scripts/get_maintainer.pl one used in the U-Boot and Linux projects. It can be configured via a .patman configuration file and accepts arguments, as documented. Reviewed-by: Simon Glass <sjg@chromium.org> Signed-off-by: Maxim Cournoyer <maxim.cournoyer@savoirfairelinux.com>
This commit is contained in:
parent
8f8d3f72f2
commit
8c042fb7f9
@ -21,6 +21,7 @@ if __name__ == "__main__":
|
|||||||
# Our modules
|
# Our modules
|
||||||
from patman import control
|
from patman import control
|
||||||
from patman import func_test
|
from patman import func_test
|
||||||
|
from patman import gitutil
|
||||||
from patman import project
|
from patman import project
|
||||||
from patman import settings
|
from patman import settings
|
||||||
from patman import terminal
|
from patman import terminal
|
||||||
@ -64,6 +65,12 @@ send.add_argument('-l', '--limit-cc', dest='limit', type=int, default=None,
|
|||||||
send.add_argument('-m', '--no-maintainers', action='store_false',
|
send.add_argument('-m', '--no-maintainers', action='store_false',
|
||||||
dest='add_maintainers', default=True,
|
dest='add_maintainers', default=True,
|
||||||
help="Don't cc the file maintainers automatically")
|
help="Don't cc the file maintainers automatically")
|
||||||
|
send.add_argument(
|
||||||
|
'--get-maintainer-script', dest='get_maintainer_script', type=str,
|
||||||
|
action='store',
|
||||||
|
default=os.path.join(gitutil.get_top_level(), 'scripts',
|
||||||
|
'get_maintainer.pl') + ' --norolestats',
|
||||||
|
help='File name of the get_maintainer.pl (or compatible) script.')
|
||||||
send.add_argument('-n', '--dry-run', action='store_true', dest='dry_run',
|
send.add_argument('-n', '--dry-run', action='store_true', dest='dry_run',
|
||||||
default=False, help="Do a dry run (create but don't email patches)")
|
default=False, help="Do a dry run (create but don't email patches)")
|
||||||
send.add_argument('-r', '--in-reply-to', type=str, action='store',
|
send.add_argument('-r', '--in-reply-to', type=str, action='store',
|
||||||
|
@ -94,8 +94,8 @@ def check_patches(series, patch_files, run_checkpatch, verbose, use_tree):
|
|||||||
|
|
||||||
|
|
||||||
def email_patches(col, series, cover_fname, patch_files, process_tags, its_a_go,
|
def email_patches(col, series, cover_fname, patch_files, process_tags, its_a_go,
|
||||||
ignore_bad_tags, add_maintainers, limit, dry_run, in_reply_to,
|
ignore_bad_tags, add_maintainers, get_maintainer_script, limit,
|
||||||
thread, smtp_server):
|
dry_run, in_reply_to, thread, smtp_server):
|
||||||
"""Email patches to the recipients
|
"""Email patches to the recipients
|
||||||
|
|
||||||
This emails out the patches and cover letter using 'git send-email'. Each
|
This emails out the patches and cover letter using 'git send-email'. Each
|
||||||
@ -123,6 +123,8 @@ def email_patches(col, series, cover_fname, patch_files, process_tags, its_a_go,
|
|||||||
ignore_bad_tags (bool): True to just print a warning for unknown tags,
|
ignore_bad_tags (bool): True to just print a warning for unknown tags,
|
||||||
False to halt with an error
|
False to halt with an error
|
||||||
add_maintainers (bool): Run the get_maintainer.pl script for each patch
|
add_maintainers (bool): Run the get_maintainer.pl script for each patch
|
||||||
|
get_maintainer_script (str): The script used to retrieve which
|
||||||
|
maintainers to cc
|
||||||
limit (int): Limit on the number of people that can be cc'd on a single
|
limit (int): Limit on the number of people that can be cc'd on a single
|
||||||
patch or the cover letter (None if no limit)
|
patch or the cover letter (None if no limit)
|
||||||
dry_run (bool): Don't actually email the patches, just print out what
|
dry_run (bool): Don't actually email the patches, just print out what
|
||||||
@ -134,7 +136,7 @@ def email_patches(col, series, cover_fname, patch_files, process_tags, its_a_go,
|
|||||||
smtp_server (str): SMTP server to use to send patches (None for default)
|
smtp_server (str): SMTP server to use to send patches (None for default)
|
||||||
"""
|
"""
|
||||||
cc_file = series.MakeCcFile(process_tags, cover_fname, not ignore_bad_tags,
|
cc_file = series.MakeCcFile(process_tags, cover_fname, not ignore_bad_tags,
|
||||||
add_maintainers, limit)
|
add_maintainers, limit, get_maintainer_script)
|
||||||
|
|
||||||
# Email the patches out (giving the user time to check / cancel)
|
# Email the patches out (giving the user time to check / cancel)
|
||||||
cmd = ''
|
cmd = ''
|
||||||
@ -174,8 +176,8 @@ def send(args):
|
|||||||
email_patches(
|
email_patches(
|
||||||
col, series, cover_fname, patch_files, args.process_tags,
|
col, series, cover_fname, patch_files, args.process_tags,
|
||||||
its_a_go, args.ignore_bad_tags, args.add_maintainers,
|
its_a_go, args.ignore_bad_tags, args.add_maintainers,
|
||||||
args.limit, args.dry_run, args.in_reply_to, args.thread,
|
args.get_maintainer_script, args.limit, args.dry_run,
|
||||||
args.smtp_server)
|
args.in_reply_to, args.thread, args.smtp_server)
|
||||||
|
|
||||||
def patchwork_status(branch, count, start, end, dest_branch, force,
|
def patchwork_status(branch, count, start, end, dest_branch, force,
|
||||||
show_comments, url):
|
show_comments, url):
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
"""Functional tests for checking that patman behaves correctly"""
|
"""Functional tests for checking that patman behaves correctly"""
|
||||||
|
|
||||||
|
import contextlib
|
||||||
import os
|
import os
|
||||||
import pathlib
|
import pathlib
|
||||||
import re
|
import re
|
||||||
@ -29,8 +30,19 @@ from patman.test_util import capture_sys_output
|
|||||||
import pygit2
|
import pygit2
|
||||||
from patman import status
|
from patman import status
|
||||||
|
|
||||||
|
PATMAN_DIR = pathlib.Path(__file__).parent
|
||||||
|
TEST_DATA_DIR = PATMAN_DIR / 'test/'
|
||||||
|
|
||||||
TEST_DATA_DIR = pathlib.Path(__file__).parent / 'test/'
|
|
||||||
|
@contextlib.contextmanager
|
||||||
|
def directory_excursion(directory):
|
||||||
|
"""Change directory to `directory` for a limited to the context block."""
|
||||||
|
current = os.getcwd()
|
||||||
|
try:
|
||||||
|
os.chdir(directory)
|
||||||
|
yield
|
||||||
|
finally:
|
||||||
|
os.chdir(current)
|
||||||
|
|
||||||
|
|
||||||
class TestFunctional(unittest.TestCase):
|
class TestFunctional(unittest.TestCase):
|
||||||
@ -204,6 +216,8 @@ class TestFunctional(unittest.TestCase):
|
|||||||
text = self._get_text('test01.txt')
|
text = self._get_text('test01.txt')
|
||||||
series = patchstream.get_metadata_for_test(text)
|
series = patchstream.get_metadata_for_test(text)
|
||||||
cover_fname, args = self._create_patches_for_test(series)
|
cover_fname, args = self._create_patches_for_test(series)
|
||||||
|
get_maintainer_script = str(pathlib.Path(__file__).parent.parent.parent
|
||||||
|
/ 'get_maintainer.pl') + ' --norolestats'
|
||||||
with capture_sys_output() as out:
|
with capture_sys_output() as out:
|
||||||
patchstream.fix_patches(series, args)
|
patchstream.fix_patches(series, args)
|
||||||
if cover_fname and series.get('cover'):
|
if cover_fname and series.get('cover'):
|
||||||
@ -211,7 +225,7 @@ class TestFunctional(unittest.TestCase):
|
|||||||
series.DoChecks()
|
series.DoChecks()
|
||||||
cc_file = series.MakeCcFile(process_tags, cover_fname,
|
cc_file = series.MakeCcFile(process_tags, cover_fname,
|
||||||
not ignore_bad_tags, add_maintainers,
|
not ignore_bad_tags, add_maintainers,
|
||||||
None)
|
None, get_maintainer_script)
|
||||||
cmd = gitutil.email_patches(
|
cmd = gitutil.email_patches(
|
||||||
series, cover_fname, args, dry_run, not ignore_bad_tags,
|
series, cover_fname, args, dry_run, not ignore_bad_tags,
|
||||||
cc_file, in_reply_to=in_reply_to, thread=None)
|
cc_file, in_reply_to=in_reply_to, thread=None)
|
||||||
@ -506,6 +520,37 @@ complicated as possible''')
|
|||||||
finally:
|
finally:
|
||||||
os.chdir(orig_dir)
|
os.chdir(orig_dir)
|
||||||
|
|
||||||
|
def test_custom_get_maintainer_script(self):
|
||||||
|
"""Validate that a custom get_maintainer script gets used."""
|
||||||
|
self.make_git_tree()
|
||||||
|
with directory_excursion(self.gitdir):
|
||||||
|
# Setup git.
|
||||||
|
os.environ['GIT_CONFIG_GLOBAL'] = '/dev/null'
|
||||||
|
os.environ['GIT_CONFIG_SYSTEM'] = '/dev/null'
|
||||||
|
tools.run('git', 'config', 'user.name', 'Dummy')
|
||||||
|
tools.run('git', 'config', 'user.email', 'dumdum@dummy.com')
|
||||||
|
tools.run('git', 'branch', 'upstream')
|
||||||
|
tools.run('git', 'branch', '--set-upstream-to=upstream')
|
||||||
|
tools.run('git', 'add', '.')
|
||||||
|
tools.run('git', 'commit', '-m', 'new commit')
|
||||||
|
|
||||||
|
# Setup patman configuration.
|
||||||
|
with open('.patman', 'w', buffering=1) as f:
|
||||||
|
f.write('[settings]\n'
|
||||||
|
'get_maintainer_script: dummy-script.sh\n'
|
||||||
|
'check_patch: False\n')
|
||||||
|
with open('dummy-script.sh', 'w', buffering=1) as f:
|
||||||
|
f.write('#!/usr/bin/env python\n'
|
||||||
|
'print("hello@there.com")\n')
|
||||||
|
os.chmod('dummy-script.sh', 0x555)
|
||||||
|
|
||||||
|
# Finally, do the test
|
||||||
|
with capture_sys_output():
|
||||||
|
output = tools.run(PATMAN_DIR / 'patman', '--dry-run')
|
||||||
|
# Assert the email address is part of the dry-run
|
||||||
|
# output.
|
||||||
|
self.assertIn('hello@there.com', output)
|
||||||
|
|
||||||
def test_tags(self):
|
def test_tags(self):
|
||||||
"""Test collection of tags in a patchstream"""
|
"""Test collection of tags in a patchstream"""
|
||||||
text = '''This is a patch
|
text = '''This is a patch
|
||||||
|
@ -1,48 +1,61 @@
|
|||||||
# SPDX-License-Identifier: GPL-2.0+
|
# SPDX-License-Identifier: GPL-2.0+
|
||||||
# Copyright (c) 2012 The Chromium OS Authors.
|
# Copyright (c) 2012 The Chromium OS Authors.
|
||||||
|
# Copyright (c) 2022 Maxim Cournoyer <maxim.cournoyer@savoirfairelinux.com>
|
||||||
#
|
#
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import shlex
|
||||||
|
import shutil
|
||||||
|
|
||||||
from patman import command
|
from patman import command
|
||||||
|
from patman import gitutil
|
||||||
|
|
||||||
def find_get_maintainer(try_list):
|
|
||||||
"""Look for the get_maintainer.pl script.
|
|
||||||
|
|
||||||
Args:
|
def find_get_maintainer(script_file_name):
|
||||||
try_list: List of directories to try for the get_maintainer.pl script
|
"""Try to find where `script_file_name` is.
|
||||||
|
|
||||||
Returns:
|
It searches in PATH and falls back to a path relative to the top
|
||||||
If the script is found we'll return a path to it; else None.
|
of the current git repository.
|
||||||
"""
|
"""
|
||||||
# Look in the list
|
get_maintainer = shutil.which(script_file_name)
|
||||||
for path in try_list:
|
if get_maintainer:
|
||||||
fname = os.path.join(path, 'get_maintainer.pl')
|
return get_maintainer
|
||||||
if os.path.isfile(fname):
|
|
||||||
return fname
|
|
||||||
|
|
||||||
return None
|
git_relative_script = os.path.join(gitutil.get_top_level(),
|
||||||
|
script_file_name)
|
||||||
|
if os.path.exists(git_relative_script):
|
||||||
|
return git_relative_script
|
||||||
|
|
||||||
def get_maintainer(dir_list, fname, verbose=False):
|
|
||||||
"""Run get_maintainer.pl on a file if we find it.
|
|
||||||
|
|
||||||
We look for get_maintainer.pl in the 'scripts' directory at the top of
|
def get_maintainer(script_file_name, fname, verbose=False):
|
||||||
git. If we find it we'll run it. If we don't find get_maintainer.pl
|
"""Run `script_file_name` on a file.
|
||||||
then we fail silently.
|
|
||||||
|
`script_file_name` should be a get_maintainer.pl-like script that
|
||||||
|
takes a patch file name as an input and return the email addresses
|
||||||
|
of the associated maintainers to standard output, one per line.
|
||||||
|
|
||||||
|
If `script_file_name` does not exist we fail silently.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
dir_list: List of directories to try for the get_maintainer.pl script
|
script_file_name: The file name of the get_maintainer.pl script
|
||||||
fname: Path to the patch file to run get_maintainer.pl on.
|
(or compatible).
|
||||||
|
fname: File name of the patch to process with get_maintainer.pl.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
A list of email addresses to CC to.
|
A list of email addresses to CC to.
|
||||||
"""
|
"""
|
||||||
get_maintainer = find_get_maintainer(dir_list)
|
# Expand `script_file_name` into a file name and its arguments, if
|
||||||
|
# any.
|
||||||
|
cmd_args = shlex.split(script_file_name)
|
||||||
|
file_name = cmd_args[0]
|
||||||
|
arguments = cmd_args[1:]
|
||||||
|
|
||||||
|
get_maintainer = find_get_maintainer(file_name)
|
||||||
if not get_maintainer:
|
if not get_maintainer:
|
||||||
if verbose:
|
if verbose:
|
||||||
print("WARNING: Couldn't find get_maintainer.pl")
|
print("WARNING: Couldn't find get_maintainer.pl")
|
||||||
return []
|
return []
|
||||||
|
|
||||||
stdout = command.output(get_maintainer, '--norolestats', fname)
|
stdout = command.output(get_maintainer, *arguments, fname)
|
||||||
lines = stdout.splitlines()
|
lines = stdout.splitlines()
|
||||||
return [ x.replace('"', '') for x in lines ]
|
return [x.replace('"', '') for x in lines]
|
||||||
|
@ -433,7 +433,7 @@ def check_suppress_cc_config():
|
|||||||
|
|
||||||
def email_patches(series, cover_fname, args, dry_run, warn_on_error, cc_fname,
|
def email_patches(series, cover_fname, args, dry_run, warn_on_error, cc_fname,
|
||||||
self_only=False, alias=None, in_reply_to=None, thread=False,
|
self_only=False, alias=None, in_reply_to=None, thread=False,
|
||||||
smtp_server=None):
|
smtp_server=None, get_maintainer_script=None):
|
||||||
"""Email a patch series.
|
"""Email a patch series.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@ -450,6 +450,7 @@ def email_patches(series, cover_fname, args, dry_run, warn_on_error, cc_fname,
|
|||||||
thread: True to add --thread to git send-email (make
|
thread: True to add --thread to git send-email (make
|
||||||
all patches reply to cover-letter or first patch in series)
|
all patches reply to cover-letter or first patch in series)
|
||||||
smtp_server: SMTP server to use to send patches
|
smtp_server: SMTP server to use to send patches
|
||||||
|
get_maintainer_script: File name of script to get maintainers emails
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Git command that was/would be run
|
Git command that was/would be run
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
.. SPDX-License-Identifier: GPL-2.0+
|
.. SPDX-License-Identifier: GPL-2.0+
|
||||||
.. Copyright (c) 2011 The Chromium OS Authors
|
.. Copyright (c) 2011 The Chromium OS Authors
|
||||||
.. Simon Glass <sjg@chromium.org>
|
.. Simon Glass <sjg@chromium.org>
|
||||||
|
.. Maxim Cournoyer <maxim.cournoyer@savoirfairelinux.com>
|
||||||
.. v1, v2, 19-Oct-11
|
.. v1, v2, 19-Oct-11
|
||||||
.. revised v3 24-Nov-11
|
.. revised v3 24-Nov-11
|
||||||
.. revised v4 Independence Day 2020, with Patchwork integration
|
.. revised v4 Independence Day 2020, with Patchwork integration
|
||||||
@ -68,8 +69,23 @@ this once::
|
|||||||
|
|
||||||
git config sendemail.aliasesfile doc/git-mailrc
|
git config sendemail.aliasesfile doc/git-mailrc
|
||||||
|
|
||||||
For both Linux and U-Boot the 'scripts/get_maintainer.pl' handles figuring
|
For both Linux and U-Boot the 'scripts/get_maintainer.pl' handles
|
||||||
out where to send patches pretty well.
|
figuring out where to send patches pretty well. For other projects,
|
||||||
|
you may want to specify a different script to be run, for example via
|
||||||
|
a project-specific `.patman` file::
|
||||||
|
|
||||||
|
# .patman configuration file at the root of some project
|
||||||
|
|
||||||
|
[settings]
|
||||||
|
get_maintainer_script: etc/teams.scm get-maintainer
|
||||||
|
|
||||||
|
The `get_maintainer_script` option corresponds to the
|
||||||
|
`--get-maintainer-script` argument of the `send` command. It is
|
||||||
|
looked relatively to the root of the current git repository, as well
|
||||||
|
as on PATH. It can also be provided arguments, as shown above. The
|
||||||
|
contract is that the script should accept a patch file name and return
|
||||||
|
a list of email addresses, one per line, like `get_maintainer.pl`
|
||||||
|
does.
|
||||||
|
|
||||||
During the first run patman creates a config file for you by taking the default
|
During the first run patman creates a config file for you by taking the default
|
||||||
user name and email address from the global .gitconfig file.
|
user name and email address from the global .gitconfig file.
|
||||||
@ -85,11 +101,11 @@ To add your own, create a file `~/.patman` like this::
|
|||||||
wolfgang: Wolfgang Denk <wd@denx.de>
|
wolfgang: Wolfgang Denk <wd@denx.de>
|
||||||
others: Mike Frysinger <vapier@gentoo.org>, Fred Bloggs <f.bloggs@napier.net>
|
others: Mike Frysinger <vapier@gentoo.org>, Fred Bloggs <f.bloggs@napier.net>
|
||||||
|
|
||||||
Patman will also look for a `.patman` configuration file at the root
|
As hinted above, Patman will also look for a `.patman` configuration
|
||||||
of the current project git repository, which makes it possible to
|
file at the root of the current project git repository, which makes it
|
||||||
override the `project` settings variable or anything else in a
|
possible to override the `project` settings variable or anything else
|
||||||
project-specific way. The values of this "local" configuration file
|
in a project-specific way. The values of this "local" configuration
|
||||||
take precedence over those of the "global" one.
|
file take precedence over those of the "global" one.
|
||||||
|
|
||||||
Aliases are recursive.
|
Aliases are recursive.
|
||||||
|
|
||||||
|
@ -235,7 +235,7 @@ class Series(dict):
|
|||||||
print(col.build(col.RED, str))
|
print(col.build(col.RED, str))
|
||||||
|
|
||||||
def MakeCcFile(self, process_tags, cover_fname, warn_on_error,
|
def MakeCcFile(self, process_tags, cover_fname, warn_on_error,
|
||||||
add_maintainers, limit):
|
add_maintainers, limit, get_maintainer_script):
|
||||||
"""Make a cc file for us to use for per-commit Cc automation
|
"""Make a cc file for us to use for per-commit Cc automation
|
||||||
|
|
||||||
Also stores in self._generated_cc to make ShowActions() faster.
|
Also stores in self._generated_cc to make ShowActions() faster.
|
||||||
@ -249,6 +249,8 @@ class Series(dict):
|
|||||||
True/False to call the get_maintainers to CC maintainers
|
True/False to call the get_maintainers to CC maintainers
|
||||||
List of maintainers to include (for testing)
|
List of maintainers to include (for testing)
|
||||||
limit: Limit the length of the Cc list (None if no limit)
|
limit: Limit the length of the Cc list (None if no limit)
|
||||||
|
get_maintainer_script: The file name of the get_maintainer.pl
|
||||||
|
script (or compatible).
|
||||||
Return:
|
Return:
|
||||||
Filename of temp file created
|
Filename of temp file created
|
||||||
"""
|
"""
|
||||||
@ -267,8 +269,9 @@ class Series(dict):
|
|||||||
if type(add_maintainers) == type(cc):
|
if type(add_maintainers) == type(cc):
|
||||||
cc += add_maintainers
|
cc += add_maintainers
|
||||||
elif add_maintainers:
|
elif add_maintainers:
|
||||||
dir_list = [os.path.join(gitutil.get_top_level(), 'scripts')]
|
|
||||||
cc += get_maintainer.get_maintainer(dir_list, commit.patch)
|
cc += get_maintainer.get_maintainer(get_maintainer_script,
|
||||||
|
commit.patch)
|
||||||
for x in set(cc) & set(settings.bounces):
|
for x in set(cc) & set(settings.bounces):
|
||||||
print(col.build(col.YELLOW, 'Skipping "%s"' % x))
|
print(col.build(col.YELLOW, 'Skipping "%s"' % x))
|
||||||
cc = list(set(cc) - set(settings.bounces))
|
cc = list(set(cc) - set(settings.bounces))
|
||||||
|
Loading…
Reference in New Issue
Block a user