else: ok = True cc_file = series.MakeCcFile(options.process_tags, cover_fname, not options.ignore_bad_tags, options.add_maintainers, options.limit) # Email the patches out (giving the user time to check / cancel) cmd = '' its_a_go = ok or options.ignore_errors if its_a_go: cmd = gitutil.EmailPatches(series, cover_fname, args, options.dry_run, not options.ignore_bad_tags, cc_file, in_reply_to=options.in_reply_to, thread=options.thread, smtp_server=options.smtp_server) else: print(col.Color(col.RED, "Not sending emails due to errors/warnings")) # For a dry run, just show our actions as a sanity check if options.dry_run: series.ShowActions(args, cmd, options.process_tags) if not its_a_go: print(col.Color(col.RED, "Email would not be sent")) os.remove(cc_file)
if options.check_patch: ok = checkpatch.CheckPatches(options.verbose, args) else: ok = True cc_file = series.MakeCcFile(options.process_tags, cover_fname, not options.ignore_bad_tags, options.add_maintainers) # Email the patches out (giving the user time to check / cancel) cmd = '' its_a_go = ok or options.ignore_errors if its_a_go: cmd = gitutil.EmailPatches(series, cover_fname, args, options.dry_run, not options.ignore_bad_tags, cc_file, in_reply_to=options.in_reply_to) else: print col.Color(col.RED, "Not sending emails due to errors/warnings") # For a dry run, just show our actions as a sanity check if options.dry_run: series.ShowActions(args, cmd, options.process_tags) if not its_a_go: print col.Color(col.RED, "Email would not be sent") os.remove(cc_file)
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, thread, smtp_server): """Email patches to the recipients This emails out the patches and cover letter using 'git send-email'. Each patch is copied to recipients identified by the patch tag and output from the get_maintainer.pl script. The cover letter is copied to all recipients of any patch. To make this work a CC file is created holding the recipients for each patch and the cover letter. See the main program 'cc_cmd' for this logic. Args: col (terminal.Color): Colour output object series (Series): Series object for this series (set of patches) cover_fname (str): Filename of the cover letter as a string (None if none) patch_files (list): List of patch filenames, each a string, e.g. ['0001_xxx.patch', '0002_yyy.patch'] process_tags (bool): True to process subject tags in each patch, e.g. for 'dm: spi: Add SPI support' this would be 'dm' and 'spi'. The tags are looked up in the configured sendemail.aliasesfile and also in ~/.patman (see README) its_a_go (bool): True if we are going to actually send the patches, False if the patches have errors and will not be sent unless @ignore_errors ignore_bad_tags (bool): True to just print a warning for unknown tags, False to halt with an error add_maintainers (bool): Run the get_maintainer.pl script for each patch 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) dry_run (bool): Don't actually email the patches, just print out what would be sent in_reply_to (str): If not None we'll pass this to git as --in-reply-to. Should be a message ID that this is in reply to. thread (bool): True to add --thread to git send-email (make all patches reply to cover-letter or first patch in series) 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, add_maintainers, limit) # Email the patches out (giving the user time to check / cancel) cmd = '' if its_a_go: cmd = gitutil.EmailPatches(series, cover_fname, patch_files, dry_run, not ignore_bad_tags, cc_file, in_reply_to=in_reply_to, thread=thread, smtp_server=smtp_server) else: print(col.Color(col.RED, "Not sending emails due to errors/warnings")) # For a dry run, just show our actions as a sanity check if dry_run: series.ShowActions(patch_files, cmd, process_tags) if not its_a_go: print(col.Color(col.RED, "Email would not be sent")) os.remove(cc_file)
def testBasic(self): """Tests the basic flow of patman This creates a series from some hard-coded patches build from a simple tree with the following metadata in the top commit: Series-to: u-boot Series-prefix: RFC Series-cc: Stefan Brüns <*****@*****.**> Cover-letter-cc: Lord Mëlchett <*****@*****.**> Series-version: 3 Patch-cc: fred Series-process-log: sort, uniq Series-changes: 4 - Some changes - Multi line change Commit-changes: 2 - Changes only for this commit Cover-changes: 4 - Some notes for the cover letter Cover-letter: test: A test patch series This is a test of how the cover letter works END and this in the first commit: Commit-changes: 2 - second revision change Series-notes: some notes about some things from the first commit END Commit-notes: Some notes about the first commit END with the following commands: git log -n2 --reverse >/path/to/tools/patman/test/test01.txt git format-patch --subject-prefix RFC --cover-letter HEAD~2 mv 00* /path/to/tools/patman/test It checks these aspects: - git log can be processed by patchstream - emailing patches uses the correct command - CC file has information on each commit - cover letter has the expected text and subject - each patch has the correct subject - dry-run information prints out correctly - unicode is handled correctly - Series-to, Series-cc, Series-prefix, Cover-letter - Cover-letter-cc, Series-version, Series-changes, Series-notes - Commit-notes """ process_tags = True ignore_bad_tags = False stefan = b'Stefan Br\xc3\xbcns <*****@*****.**>'.decode('utf-8') rick = 'Richard III <*****@*****.**>' mel = b'Lord M\xc3\xablchett <*****@*****.**>'.decode('utf-8') add_maintainers = [stefan, rick] dry_run = True in_reply_to = mel count = 2 settings.alias = { 'fdt': ['simon'], 'u-boot': ['*****@*****.**'], 'simon': [self.leb], 'fred': [self.fred], } text = self._get_text('test01.txt') series = patchstream.get_metadata_for_test(text) cover_fname, args = self._create_patches_for_test(series) with capture_sys_output() as out: patchstream.fix_patches(series, args) if cover_fname and series.get('cover'): patchstream.insert_cover_letter(cover_fname, series, count) series.DoChecks() cc_file = series.MakeCcFile(process_tags, cover_fname, not ignore_bad_tags, add_maintainers, None) cmd = gitutil.EmailPatches( series, cover_fname, args, dry_run, not ignore_bad_tags, cc_file, in_reply_to=in_reply_to, thread=None) series.ShowActions(args, cmd, process_tags) cc_lines = open(cc_file, encoding='utf-8').read().splitlines() os.remove(cc_file) lines = iter(out[0].getvalue().splitlines()) self.assertEqual('Cleaned %s patches' % len(series.commits), next(lines)) self.assertEqual('Change log missing for v2', next(lines)) self.assertEqual('Change log missing for v3', next(lines)) self.assertEqual('Change log for unknown version v4', next(lines)) self.assertEqual("Alias 'pci' not found", next(lines)) self.assertIn('Dry run', next(lines)) self.assertEqual('', next(lines)) self.assertIn('Send a total of %d patches' % count, next(lines)) prev = next(lines) for i, commit in enumerate(series.commits): self.assertEqual(' %s' % args[i], prev) while True: prev = next(lines) if 'Cc:' not in prev: break self.assertEqual('To: [email protected]', prev) self.assertEqual('Cc: %s' % stefan, next(lines)) self.assertEqual('Version: 3', next(lines)) self.assertEqual('Prefix:\t RFC', next(lines)) self.assertEqual('Cover: 4 lines', next(lines)) self.assertEqual(' Cc: %s' % self.fred, next(lines)) self.assertEqual(' Cc: %s' % self.leb, next(lines)) self.assertEqual(' Cc: %s' % mel, next(lines)) self.assertEqual(' Cc: %s' % rick, next(lines)) expected = ('Git command: git send-email --annotate ' '--in-reply-to="%s" --to "*****@*****.**" ' '--cc "%s" --cc-cmd "%s send --cc-cmd %s" %s %s' % (in_reply_to, stefan, sys.argv[0], cc_file, cover_fname, ' '.join(args))) self.assertEqual(expected, next(lines)) self.assertEqual(('%s %s\0%s' % (args[0], rick, stefan)), cc_lines[0]) self.assertEqual( '%s %s\0%s\0%s\0%s' % (args[1], self.fred, self.leb, rick, stefan), cc_lines[1]) expected = ''' This is a test of how the cover letter works some notes about some things from the first commit Changes in v4: - Multi line change - Some changes - Some notes for the cover letter Simon Glass (2): pci: Correct cast for sandbox fdt: Correct cast for sandbox in fdtdec_setup_mem_size_base() cmd/pci.c | 3 ++- fs/fat/fat.c | 1 + lib/efi_loader/efi_memory.c | 1 + lib/fdtdec.c | 3 ++- 4 files changed, 6 insertions(+), 2 deletions(-) --\x20 2.7.4 ''' lines = open(cover_fname, encoding='utf-8').read().splitlines() self.assertEqual( 'Subject: [RFC PATCH v3 0/2] test: A test patch series', lines[3]) self.assertEqual(expected.splitlines(), lines[7:]) for i, fname in enumerate(args): lines = open(fname, encoding='utf-8').read().splitlines() subject = [line for line in lines if line.startswith('Subject')] self.assertEqual('Subject: [RFC %d/%d]' % (i + 1, count), subject[0][:18]) # Check that we got our commit notes start = 0 expected = '' if i == 0: start = 17 expected = '''--- Some notes about the first commit (no changes since v2) Changes in v2: - second revision change''' elif i == 1: start = 17 expected = '''--- Changes in v4: - Multi line change - Some changes Changes in v2: - Changes only for this commit''' if expected: expected = expected.splitlines() self.assertEqual(expected, lines[start:(start+len(expected))])
def testBasic(self): """Tests the basic flow of patman This creates a series from some hard-coded patches build from a simple tree with the following metadata in the top commit: Series-to: u-boot Series-prefix: RFC Series-cc: Stefan Brüns <*****@*****.**> Cover-letter-cc: Lord Mëlchett <*****@*****.**> Series-version: 2 Series-changes: 4 - Some changes Cover-letter: test: A test patch series This is a test of how the cover leter works END and this in the first commit: Series-notes: some notes about some things from the first commit END Commit-notes: Some notes about the first commit END with the following commands: git log -n2 --reverse >/path/to/tools/patman/test/test01.txt git format-patch --subject-prefix RFC --cover-letter HEAD~2 mv 00* /path/to/tools/patman/test It checks these aspects: - git log can be processed by patchstream - emailing patches uses the correct command - CC file has information on each commit - cover letter has the expected text and subject - each patch has the correct subject - dry-run information prints out correctly - unicode is handled correctly - Series-to, Series-cc, Series-prefix, Cover-letter - Cover-letter-cc, Series-version, Series-changes, Series-notes - Commit-notes """ process_tags = True ignore_bad_tags = True stefan = b'Stefan Br\xc3\xbcns <*****@*****.**>'.decode( 'utf-8') rick = 'Richard III <*****@*****.**>' mel = b'Lord M\xc3\xablchett <*****@*****.**>'.decode('utf-8') ed = b'Lond Edmund Blackadd\xc3\xabr <*****@*****.**'.decode( 'utf-8') fred = 'Fred Bloggs <*****@*****.**>' add_maintainers = [stefan, rick] dry_run = True in_reply_to = mel count = 2 settings.alias = { 'fdt': ['simon'], 'u-boot': ['*****@*****.**'], 'simon': [ed], 'fred': [fred], } text = self.GetText('test01.txt') series = patchstream.GetMetaDataForTest(text) cover_fname, args = self.CreatePatchesForTest(series) with capture() as out: patchstream.FixPatches(series, args) if cover_fname and series.get('cover'): patchstream.InsertCoverLetter(cover_fname, series, count) series.DoChecks() cc_file = series.MakeCcFile(process_tags, cover_fname, not ignore_bad_tags, add_maintainers, None) cmd = gitutil.EmailPatches(series, cover_fname, args, dry_run, not ignore_bad_tags, cc_file, in_reply_to=in_reply_to, thread=None) series.ShowActions(args, cmd, process_tags) cc_lines = open(cc_file, encoding='utf-8').read().splitlines() os.remove(cc_file) lines = out[0].splitlines() self.assertEqual('Cleaned %s patches' % len(series.commits), lines[0]) self.assertEqual('Change log missing for v2', lines[1]) self.assertEqual('Change log missing for v3', lines[2]) self.assertEqual('Change log for unknown version v4', lines[3]) self.assertEqual("Alias 'pci' not found", lines[4]) self.assertIn('Dry run', lines[5]) self.assertIn('Send a total of %d patches' % count, lines[7]) line = 8 for i, commit in enumerate(series.commits): self.assertEqual(' %s' % args[i], lines[line + 0]) line += 1 while 'Cc:' in lines[line]: line += 1 self.assertEqual('To: [email protected]', lines[line]) self.assertEqual('Cc: %s' % tools.FromUnicode(stefan), lines[line + 1]) self.assertEqual('Version: 3', lines[line + 2]) self.assertEqual('Prefix:\t RFC', lines[line + 3]) self.assertEqual('Cover: 4 lines', lines[line + 4]) line += 5 self.assertEqual(' Cc: %s' % fred, lines[line + 0]) self.assertEqual(' Cc: %s' % tools.FromUnicode(ed), lines[line + 1]) self.assertEqual(' Cc: %s' % tools.FromUnicode(mel), lines[line + 2]) self.assertEqual(' Cc: %s' % rick, lines[line + 3]) expected = ('Git command: git send-email --annotate ' '--in-reply-to="%s" --to "*****@*****.**" ' '--cc "%s" --cc-cmd "%s --cc-cmd %s" %s %s' % (in_reply_to, stefan, sys.argv[0], cc_file, cover_fname, ' '.join(args))) line += 4 self.assertEqual(expected, tools.ToUnicode(lines[line])) self.assertEqual(('%s %s\0%s' % (args[0], rick, stefan)), tools.ToUnicode(cc_lines[0])) self.assertEqual( ('%s %s\0%s\0%s\0%s' % (args[1], fred, ed, rick, stefan)), tools.ToUnicode(cc_lines[1])) expected = ''' This is a test of how the cover leter works some notes about some things from the first commit Changes in v4: - Some changes Simon Glass (2): pci: Correct cast for sandbox fdt: Correct cast for sandbox in fdtdec_setup_mem_size_base() cmd/pci.c | 3 ++- fs/fat/fat.c | 1 + lib/efi_loader/efi_memory.c | 1 + lib/fdtdec.c | 3 ++- 4 files changed, 6 insertions(+), 2 deletions(-) --\x20 2.7.4 ''' lines = open(cover_fname, encoding='utf-8').read().splitlines() self.assertEqual( 'Subject: [RFC PATCH v3 0/2] test: A test patch series', lines[3]) self.assertEqual(expected.splitlines(), lines[7:]) for i, fname in enumerate(args): lines = open(fname, encoding='utf-8').read().splitlines() subject = [line for line in lines if line.startswith('Subject')] self.assertEqual('Subject: [RFC %d/%d]' % (i + 1, count), subject[0][:18]) if i == 0: # Check that we got our commit notes self.assertEqual('---', lines[17]) self.assertEqual('Some notes about', lines[18]) self.assertEqual('the first commit', lines[19])