Commit a1318f7c authored by Simon Glass's avatar Simon Glass
Browse files

patman: Provide option to ignore bad aliases



Often it happens that patches include tags which don't have aliases. It
is annoying that patman fails in this case, and provides no option to
continue other than adding empty tags to the .patman file.

Correct this by adding a '-t' option to ignore tags that don't exist.
Print a warning instead.

Since running the tests is not a common operation, move this to --test
instead, to reserve -t for this new option.
Signed-off-by: default avatarSimon Glass <sjg@chromium.org>
Reviewed-by: default avatarDoug Anderson <dianders@chromium.org>
parent f140b586
...@@ -273,7 +273,7 @@ def ApplyPatches(verbose, args, start_point): ...@@ -273,7 +273,7 @@ def ApplyPatches(verbose, args, start_point):
print stdout, stderr print stdout, stderr
return error_count == 0 return error_count == 0
def BuildEmailList(in_list, tag=None, alias=None): def BuildEmailList(in_list, tag=None, alias=None, raise_on_error=True):
"""Build a list of email addresses based on an input list. """Build a list of email addresses based on an input list.
Takes a list of email addresses and aliases, and turns this into a list Takes a list of email addresses and aliases, and turns this into a list
...@@ -286,6 +286,9 @@ def BuildEmailList(in_list, tag=None, alias=None): ...@@ -286,6 +286,9 @@ def BuildEmailList(in_list, tag=None, alias=None):
Args: Args:
in_list: List of aliases/email addresses in_list: List of aliases/email addresses
tag: Text to put before each address tag: Text to put before each address
alias: Alias dictionary
raise_on_error: True to raise an error when an alias fails to match,
False to just print a message.
Returns: Returns:
List of email addresses List of email addresses
...@@ -307,7 +310,7 @@ def BuildEmailList(in_list, tag=None, alias=None): ...@@ -307,7 +310,7 @@ def BuildEmailList(in_list, tag=None, alias=None):
quote = '"' if tag and tag[0] == '-' else '' quote = '"' if tag and tag[0] == '-' else ''
raw = [] raw = []
for item in in_list: for item in in_list:
raw += LookupEmail(item, alias) raw += LookupEmail(item, alias, raise_on_error=raise_on_error)
result = [] result = []
for item in raw: for item in raw:
if not item in result: if not item in result:
...@@ -316,7 +319,7 @@ def BuildEmailList(in_list, tag=None, alias=None): ...@@ -316,7 +319,7 @@ def BuildEmailList(in_list, tag=None, alias=None):
return ['%s %s%s%s' % (tag, quote, email, quote) for email in result] return ['%s %s%s%s' % (tag, quote, email, quote) for email in result]
return result return result
def EmailPatches(series, cover_fname, args, dry_run, cc_fname, def EmailPatches(series, cover_fname, args, dry_run, raise_on_error, cc_fname,
self_only=False, alias=None, in_reply_to=None): self_only=False, alias=None, in_reply_to=None):
"""Email a patch series. """Email a patch series.
...@@ -325,6 +328,8 @@ def EmailPatches(series, cover_fname, args, dry_run, cc_fname, ...@@ -325,6 +328,8 @@ def EmailPatches(series, cover_fname, args, dry_run, cc_fname,
cover_fname: filename of cover letter cover_fname: filename of cover letter
args: list of filenames of patch files args: list of filenames of patch files
dry_run: Just return the command that would be run dry_run: Just return the command that would be run
raise_on_error: True to raise an error when an alias fails to match,
False to just print a message.
cc_fname: Filename of Cc file for per-commit Cc cc_fname: Filename of Cc file for per-commit Cc
self_only: True to just email to yourself as a test self_only: True to just email to yourself as a test
in_reply_to: If set we'll pass this to git as --in-reply-to. in_reply_to: If set we'll pass this to git as --in-reply-to.
...@@ -347,20 +352,21 @@ def EmailPatches(series, cover_fname, args, dry_run, cc_fname, ...@@ -347,20 +352,21 @@ def EmailPatches(series, cover_fname, args, dry_run, cc_fname,
>>> series = series.Series() >>> series = series.Series()
>>> series.to = ['fred'] >>> series.to = ['fred']
>>> series.cc = ['mary'] >>> series.cc = ['mary']
>>> EmailPatches(series, 'cover', ['p1', 'p2'], True, 'cc-fname', False, \ >>> EmailPatches(series, 'cover', ['p1', 'p2'], True, True, 'cc-fname', \
alias) False, alias)
'git send-email --annotate --to "f.bloggs@napier.co.nz" --cc \ 'git send-email --annotate --to "f.bloggs@napier.co.nz" --cc \
"m.poppins@cloud.net" --cc-cmd "./patman --cc-cmd cc-fname" cover p1 p2' "m.poppins@cloud.net" --cc-cmd "./patman --cc-cmd cc-fname" cover p1 p2'
>>> EmailPatches(series, None, ['p1'], True, 'cc-fname', False, alias) >>> EmailPatches(series, None, ['p1'], True, True, 'cc-fname', False, \
alias)
'git send-email --annotate --to "f.bloggs@napier.co.nz" --cc \ 'git send-email --annotate --to "f.bloggs@napier.co.nz" --cc \
"m.poppins@cloud.net" --cc-cmd "./patman --cc-cmd cc-fname" p1' "m.poppins@cloud.net" --cc-cmd "./patman --cc-cmd cc-fname" p1'
>>> series.cc = ['all'] >>> series.cc = ['all']
>>> EmailPatches(series, 'cover', ['p1', 'p2'], True, 'cc-fname', True, \ >>> EmailPatches(series, 'cover', ['p1', 'p2'], True, True, 'cc-fname', \
alias) True, alias)
'git send-email --annotate --to "this-is-me@me.com" --cc-cmd "./patman \ 'git send-email --annotate --to "this-is-me@me.com" --cc-cmd "./patman \
--cc-cmd cc-fname" cover p1 p2' --cc-cmd cc-fname" cover p1 p2'
>>> EmailPatches(series, 'cover', ['p1', 'p2'], True, 'cc-fname', False, \ >>> EmailPatches(series, 'cover', ['p1', 'p2'], True, True, 'cc-fname', \
alias) False, alias)
'git send-email --annotate --to "f.bloggs@napier.co.nz" --cc \ 'git send-email --annotate --to "f.bloggs@napier.co.nz" --cc \
"f.bloggs@napier.co.nz" --cc "j.bloggs@napier.co.nz" --cc \ "f.bloggs@napier.co.nz" --cc "j.bloggs@napier.co.nz" --cc \
"m.poppins@cloud.net" --cc-cmd "./patman --cc-cmd cc-fname" cover p1 p2' "m.poppins@cloud.net" --cc-cmd "./patman --cc-cmd cc-fname" cover p1 p2'
...@@ -368,14 +374,14 @@ def EmailPatches(series, cover_fname, args, dry_run, cc_fname, ...@@ -368,14 +374,14 @@ def EmailPatches(series, cover_fname, args, dry_run, cc_fname,
# Restore argv[0] since we clobbered it. # Restore argv[0] since we clobbered it.
>>> sys.argv[0] = _old_argv0 >>> sys.argv[0] = _old_argv0
""" """
to = BuildEmailList(series.get('to'), '--to', alias) to = BuildEmailList(series.get('to'), '--to', alias, raise_on_error)
if not to: if not to:
print ("No recipient, please add something like this to a commit\n" print ("No recipient, please add something like this to a commit\n"
"Series-to: Fred Bloggs <f.blogs@napier.co.nz>") "Series-to: Fred Bloggs <f.blogs@napier.co.nz>")
return return
cc = BuildEmailList(series.get('cc'), '--cc', alias) cc = BuildEmailList(series.get('cc'), '--cc', alias, raise_on_error)
if self_only: if self_only:
to = BuildEmailList([os.getenv('USER')], '--to', alias) to = BuildEmailList([os.getenv('USER')], '--to', alias, raise_on_error)
cc = [] cc = []
cmd = ['git', 'send-email', '--annotate'] cmd = ['git', 'send-email', '--annotate']
if in_reply_to: if in_reply_to:
...@@ -393,13 +399,16 @@ def EmailPatches(series, cover_fname, args, dry_run, cc_fname, ...@@ -393,13 +399,16 @@ def EmailPatches(series, cover_fname, args, dry_run, cc_fname,
return str return str
def LookupEmail(lookup_name, alias=None, level=0): def LookupEmail(lookup_name, alias=None, raise_on_error=True, level=0):
"""If an email address is an alias, look it up and return the full name """If an email address is an alias, look it up and return the full name
TODO: Why not just use git's own alias feature? TODO: Why not just use git's own alias feature?
Args: Args:
lookup_name: Alias or email address to look up lookup_name: Alias or email address to look up
alias: Dictionary containing aliases (None to use settings default)
raise_on_error: True to raise an error when an alias fails to match,
False to just print a message.
Returns: Returns:
tuple: tuple:
...@@ -433,6 +442,15 @@ def LookupEmail(lookup_name, alias=None, level=0): ...@@ -433,6 +442,15 @@ def LookupEmail(lookup_name, alias=None, level=0):
Traceback (most recent call last): Traceback (most recent call last):
... ...
OSError: Recursive email alias at 'other' OSError: Recursive email alias at 'other'
>>> LookupEmail('odd', alias, raise_on_error=False)
\033[1;31mAlias 'odd' not found\033[0m
[]
>>> # In this case the loop part will effectively be ignored.
>>> LookupEmail('loop', alias, raise_on_error=False)
\033[1;31mRecursive email alias at 'other'\033[0m
\033[1;31mRecursive email alias at 'john'\033[0m
\033[1;31mRecursive email alias at 'mary'\033[0m
['j.bloggs@napier.co.nz', 'm.poppins@cloud.net']
""" """
if not alias: if not alias:
alias = settings.alias alias = settings.alias
...@@ -441,16 +459,27 @@ def LookupEmail(lookup_name, alias=None, level=0): ...@@ -441,16 +459,27 @@ def LookupEmail(lookup_name, alias=None, level=0):
return [lookup_name] return [lookup_name]
lookup_name = lookup_name.lower() lookup_name = lookup_name.lower()
col = terminal.Color()
out_list = []
if level > 10: if level > 10:
raise OSError, "Recursive email alias at '%s'" % lookup_name msg = "Recursive email alias at '%s'" % lookup_name
if raise_on_error:
raise OSError, msg
else:
print col.Color(col.RED, msg)
return out_list
out_list = []
if lookup_name: if lookup_name:
if not lookup_name in alias: if not lookup_name in alias:
raise ValueError, "Alias '%s' not found" % lookup_name msg = "Alias '%s' not found" % lookup_name
if raise_on_error:
raise ValueError, msg
else:
print col.Color(col.RED, msg)
return out_list
for item in alias[lookup_name]: for item in alias[lookup_name]:
todo = LookupEmail(item, alias, level + 1) todo = LookupEmail(item, alias, raise_on_error, level + 1)
for new_item in todo: for new_item in todo:
if not new_item in out_list: if not new_item in out_list:
out_list.append(new_item) out_list.append(new_item)
......
...@@ -57,7 +57,9 @@ parser.add_option('-r', '--in-reply-to', type='string', action='store', ...@@ -57,7 +57,9 @@ parser.add_option('-r', '--in-reply-to', type='string', action='store',
help="Message ID that this series is in reply to") help="Message ID that this series is in reply to")
parser.add_option('-s', '--start', dest='start', type='int', parser.add_option('-s', '--start', dest='start', type='int',
default=0, help='Commit to start creating patches from (0 = HEAD)') default=0, help='Commit to start creating patches from (0 = HEAD)')
parser.add_option('-t', '--test', action='store_true', dest='test', parser.add_option('-t', '--ignore-bad-tags', action='store_true',
default=False, help='Ignore bad tags / aliases')
parser.add_option('--test', action='store_true', dest='test',
default=False, help='run tests') default=False, help='run tests')
parser.add_option('-v', '--verbose', action='store_true', dest='verbose', parser.add_option('-v', '--verbose', action='store_true', dest='verbose',
default=False, help='Verbose output of errors and warnings') default=False, help='Verbose output of errors and warnings')
...@@ -159,13 +161,15 @@ else: ...@@ -159,13 +161,15 @@ else:
options.count + options.start): options.count + options.start):
ok = False ok = False
cc_file = series.MakeCcFile(options.process_tags, cover_fname) cc_file = series.MakeCcFile(options.process_tags, cover_fname,
not options.ignore_bad_tags)
# Email the patches out (giving the user time to check / cancel) # Email the patches out (giving the user time to check / cancel)
cmd = '' cmd = ''
if ok or options.ignore_errors: if ok or options.ignore_errors:
cmd = gitutil.EmailPatches(series, cover_fname, args, cmd = gitutil.EmailPatches(series, cover_fname, args,
options.dry_run, cc_file, in_reply_to=options.in_reply_to) options.dry_run, not options.ignore_bad_tags, cc_file,
in_reply_to=options.in_reply_to)
# For a dry run, just show our actions as a sanity check # For a dry run, just show our actions as a sanity check
if options.dry_run: if options.dry_run:
......
...@@ -210,7 +210,7 @@ class Series(dict): ...@@ -210,7 +210,7 @@ class Series(dict):
str = 'Change log exists, but no version is set' str = 'Change log exists, but no version is set'
print col.Color(col.RED, str) print col.Color(col.RED, str)
def MakeCcFile(self, process_tags, cover_fname): def MakeCcFile(self, process_tags, cover_fname, raise_on_error):
"""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.
...@@ -218,6 +218,8 @@ class Series(dict): ...@@ -218,6 +218,8 @@ class Series(dict):
Args: Args:
process_tags: Process tags as if they were aliases process_tags: Process tags as if they were aliases
cover_fname: If non-None the name of the cover letter. cover_fname: If non-None the name of the cover letter.
raise_on_error: True to raise an error when an alias fails to match,
False to just print a message.
Return: Return:
Filename of temp file created Filename of temp file created
""" """
...@@ -228,8 +230,10 @@ class Series(dict): ...@@ -228,8 +230,10 @@ class Series(dict):
for commit in self.commits: for commit in self.commits:
list = [] list = []
if process_tags: if process_tags:
list += gitutil.BuildEmailList(commit.tags) list += gitutil.BuildEmailList(commit.tags,
list += gitutil.BuildEmailList(commit.cc_list) raise_on_error=raise_on_error)
list += gitutil.BuildEmailList(commit.cc_list,
raise_on_error=raise_on_error)
list += get_maintainer.GetMaintainer(commit.patch) list += get_maintainer.GetMaintainer(commit.patch)
all_ccs += list all_ccs += list
print >>fd, commit.patch, ', '.join(list) print >>fd, commit.patch, ', '.join(list)
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment